Esempio n. 1
0
def doPointsExperiment(cellDimensions, cellCoordinateOffsets):
  """
  Learn a set of objects. Then try to recognize each object. Output an
  interactive visualization.

  @param cellDimensions (pair)
  The cell dimensions of each module

  @param cellCoordinateOffsets (sequence)
  The "cellCoordinateOffsets" parameter for each module
  """
  if not os.path.exists("traces"):
    os.makedirs("traces")

  locationConfigs = []
  for i in xrange(5):
    scale = 10.0 * (math.sqrt(2) ** i)

    for _ in xrange(4):
      orientation = np.radians(random.gauss(7.5, 7.5))
      orientation = random.choice([orientation, -orientation])

      locationConfigs.append({
        "cellsPerAxis": cellDimensions[0],
        "scale": scale,
        "orientation": orientation,
        "activationThreshold": 8,
        "initialPermanence": 1.0,
        "connectedPermanence": 0.5,
        "learningThreshold": 8,
        "sampleSize": 10,
        "permanenceIncrement": 0.1,
        "permanenceDecrement": 0.0,
        "cellCoordinateOffsets": cellCoordinateOffsets,
      })

  L4Overrides = {
    "activationThreshold": 15,
    "minThreshold": 15,
    "initialPermanence": 1.0,
  }

  column = PIUNCorticalColumn(locationConfigs, L4Overrides, bumpType="square")
  exp = PIUNExperiment(column, featureNames=("A", "B"))

  for objectDescription in OBJECTS:
    exp.learnObject(objectDescription)

  filename = "traces/{}-points-{}-cells.html".format(
    len(cellCoordinateOffsets)**2, np.prod(cellDimensions))

  with io.open(filename, "w", encoding="utf8") as fileOut:
    with trace(fileOut, exp, includeSynapses=True):
      print "Logging to", filename
      for objectDescription in OBJECTS:
        succeeded = exp.inferObjectWithRandomMovements(objectDescription)
        if not succeeded:
          print 'Failed to infer object "{}"'.format(objectDescription["name"])
def doExperiment(numObjects,
                 featuresPerObject,
                 objectWidth,
                 numFeatures,
                 featureDistribution,
                 useTrace,
                 noiseFactor,
                 moduleNoiseFactor,
                 numModules,
                 thresholds,
                 inverseReadoutResolution,
                 enlargeModuleFactor,
                 bumpOverlapMethod,
                 seed1,
                 seed2):
  """
  Learn a set of objects. Then try to recognize each object. Output an
  interactive visualization.
  """
  if not os.path.exists("traces"):
    os.makedirs("traces")

  if seed1 != -1:
    np.random.seed(seed1)

  if seed2 != -1:
    random.seed(seed2)

  features = [str(i) for i in xrange(numFeatures)]
  objects = generateObjects(numObjects, featuresPerObject, objectWidth,
                            numFeatures, featureDistribution)

  locationConfigs = []
  scale = 40.0

  if thresholds == -1:
    thresholds = int(math.ceil(numModules*0.8))
  elif thresholds == 0:
    thresholds = numModules
  perModRange = float(60.0 / float(numModules))
  for i in xrange(numModules):
    orientation = (float(i) * perModRange) + (perModRange / 2.0)

    locationConfigs.append({
        "scale": scale,
        "orientation": np.radians(orientation),
        "activationThreshold": 8,
        "initialPermanence": 1.0,
        "connectedPermanence": 0.5,
        "learningThreshold": 8,
        "sampleSize": 10,
        "permanenceIncrement": 0.1,
        "permanenceDecrement": 0.0,
        "inverseReadoutResolution": inverseReadoutResolution,
        "enlargeModuleFactor": enlargeModuleFactor,
        "bumpOverlapMethod": bumpOverlapMethod,
    })

  l4Overrides = {
    "initialPermanence": 1.0,
    "activationThreshold": thresholds,
    "reducedBasalThreshold": thresholds,
    "minThreshold": numModules,
    "sampleSize": numModules,
    "cellsPerColumn": 16,
  }

  column = PIUNCorticalColumn(locationConfigs, L4Overrides=l4Overrides,
                              bumpType="gaussian2")
  exp = PIUNExperiment(column, featureNames=features,
                       numActiveMinicolumns=10,
                       noiseFactor=noiseFactor,
                       moduleNoiseFactor=moduleNoiseFactor)

  for objectDescription in objects:
    exp.learnObject(objectDescription)

  convergence = collections.defaultdict(int)
  try:
    if useTrace:
      filename = os.path.join(
        SCRIPT_DIR,
        "traces/{}-resolution-{}-modules-{}-objects-{}-feats.html".format(
          inverseReadoutResolution, numModules, numObjects, numFeatures)
      )
      traceFileOut = io.open(filename, "w", encoding="utf8")
      traceHandle = trace(traceFileOut, exp, includeSynapses=True)
      print "Logging to", filename

    for objectDescription in objects:
      steps = exp.inferObjectWithRandomMovements(objectDescription)
      convergence[steps] += 1
      if steps is None:
        print 'Failed to infer object "{}"'.format(objectDescription["name"])
  finally:
    if useTrace:
      traceHandle.__exit__()
      traceFileOut.close()

  for step, num in sorted(convergence.iteritems()):
    print "{}: {}".format(step, num)

  result = {
    "convergence": convergence,
  }

  return result
Esempio n. 3
0
def doExperiment(locationModuleWidth,
                 bumpType,
                 cellCoordinateOffsets,
                 numObjects,
                 featuresPerObject,
                 objectWidth,
                 numFeatures,
                 featureDistribution,
                 useTrace,
                 useRawTrace,
                 logCellActivity,
                 logNumFeatureOccurrences,
                 noiseFactor,
                 moduleNoiseFactor,
                 numModules,
                 numSensations,
                 thresholds,
                 seed1,
                 seed2,
                 anchoringMethod):
  """
  Learn a set of objects. Then try to recognize each object. Output an
  interactive visualization.

  @param locationModuleWidth (int)
  The cell dimensions of each module

  @param cellCoordinateOffsets (sequence)
  The "cellCoordinateOffsets" parameter for each module
  """
  if not os.path.exists("traces"):
    os.makedirs("traces")

  if seed1 != -1:
    np.random.seed(seed1)

  if seed2 != -1:
    random.seed(seed2)

  features = [str(i) for i in xrange(numFeatures)]
  objects = generateObjects(numObjects, featuresPerObject, objectWidth,
                            numFeatures, featureDistribution)

  if logNumFeatureOccurrences:
    featureOccurrences = collections.Counter(feat["name"]
                                             for obj in objects
                                             for feat in obj["features"])
    occurrencesConvergenceLog = []

  locationConfigs = []
  scale = 40.0

  if thresholds == -1:
    thresholds = int(math.ceil(numModules*0.8))
  elif thresholds == 0:
    thresholds = numModules
  perModRange = float((90.0 if bumpType == "square" else 60.0) /
                      float(numModules))
  for i in xrange(numModules):
    orientation = (float(i) * perModRange) + (perModRange / 2.0)

    config = {
      "cellsPerAxis": locationModuleWidth,
      "scale": scale,
      "orientation": np.radians(orientation),
      "activationThreshold": 8,
      "initialPermanence": 1.0,
      "connectedPermanence": 0.5,
      "learningThreshold": 8,
      "sampleSize": 10,
      "permanenceIncrement": 0.1,
      "permanenceDecrement": 0.0,
    }

    if bumpType == "square":
      config["cellCoordinateOffsets"] = cellCoordinateOffsets
      config["anchoringMethod"] = anchoringMethod
    elif bumpType == "gaussian":
      config["bumpOverlapMethod"] = "probabilistic"
      config["baselineCellsPerAxis"] = 6
    else:
      raise ValueError("Invalid bumpType", bumpType)

    locationConfigs.append(config)

  l4Overrides = {
    "initialPermanence": 1.0,
    "activationThreshold": thresholds,
    "reducedBasalThreshold": thresholds,
    "minThreshold": numModules,
    "sampleSize": numModules,
    "cellsPerColumn": 16,
  }

  column = PIUNCorticalColumn(locationConfigs, L4Overrides=l4Overrides,
                              bumpType=bumpType)
  exp = PIUNExperiment(column, featureNames=features,
                       numActiveMinicolumns=10,
                       noiseFactor=noiseFactor,
                       moduleNoiseFactor=moduleNoiseFactor)

  for objectDescription in objects:
    exp.learnObject(objectDescription)

  convergence = collections.defaultdict(int)

  try:
    if useTrace:
      filename = os.path.join(
        SCRIPT_DIR,
        "traces/{}-points-{}-cells-{}-objects-{}-feats.html".format(
          len(cellCoordinateOffsets)**2, exp.column.L6aModules[0].numberOfCells(),
          numObjects, numFeatures)
      )
      traceFileOut = io.open(filename, "w", encoding="utf8")
      traceHandle = trace(traceFileOut, exp, includeSynapses=True)
      print "Logging to", filename

    if useRawTrace:
      rawFilename = os.path.join(
        SCRIPT_DIR,
        "traces/{}-points-{}-cells-{}-objects-{}-feats.trace".format(
          len(cellCoordinateOffsets)**2, exp.column.L6aModules[0].numberOfCells(),
          numObjects, numFeatures)
      )
      rawTraceFileOut = open(rawFilename, "w")
      rawTraceHandle = rawTrace(rawTraceFileOut, exp, includeSynapses=False)
      print "Logging to", rawFilename

    if logCellActivity:
      cellActivityTracer = PIUNCellActivityTracer(exp)
      exp.addMonitor(cellActivityTracer)

    for objectDescription in objects:

      numSensationsToInference = exp.inferObjectWithRandomMovements(
        objectDescription, numSensations)

      if logNumFeatureOccurrences:
        objectFeatureOccurrences = sorted(featureOccurrences[feat["name"]]
                                          for feat in objectDescription["features"])
        occurrencesConvergenceLog.append(
          (objectFeatureOccurrences, numSensationsToInference))
      convergence[numSensationsToInference] += 1
      if numSensationsToInference is None:
        print 'Failed to infer object "{}"'.format(objectDescription["name"])
  finally:
    if useTrace:
      traceHandle.__exit__()
      traceFileOut.close()

    if useRawTrace:
      rawTraceHandle.__exit__()
      rawTraceFileOut.close()

  for step, num in sorted(convergence.iteritems()):
    print "{}: {}".format(step, num)

  result = {
    "convergence": convergence,
  }

  if bumpType == "gaussian":
    result["bumpSigma"] = column.L6aModules[0].bumpSigma

  if logCellActivity:
    result["locationLayerTimelineByObject"] = (
      cellActivityTracer.locationLayerTimelineByObject)
    result["inferredStepByObject"] = cellActivityTracer.inferredStepByObject

  if logNumFeatureOccurrences:
    result["occurrencesConvergenceLog"] = occurrencesConvergenceLog

  return result
def doExperiment(locationModuleWidth,
                 bumpType,
                 cellCoordinateOffsets,
                 initialIncrement,
                 minAccuracy,
                 capacityResolution,
                 capacityPercentageResolution,
                 featuresPerObject,
                 objectWidth,
                 numFeatures,
                 featureDistribution,
                 useTrace,
                 noiseFactor,
                 moduleNoiseFactor,
                 numModules,
                 thresholds,
                 seed1,
                 seed2,
                 anchoringMethod):
  """
  Finds the capacity of the specified model and object configuration. The
  algorithm has two stages. First it finds an upper bound for the capacity by
  repeatedly incrementing the number of objects by initialIncrement. After it
  finds a number of objects that is above capacity, it begins the second stage:
  performing a binary search over the number of objects to find an exact
  capacity.

  @param initialIncrement (int)
  For example, if this number is 128, this method will test 128 objects, then
  256, and so on, until it finds an upper bound, then it will narrow in on the
  breaking point. This number can't be incorrect, but the simulation will be
  inefficient if it's too low or too high.

  @param capacityResolution (int)
  The resolution of the capacity. If capacityResolution=1, this method will find
  the exact capacity. If the capacityResolution is higher, the method will
  return a capacity that is potentially less than the actual capacity.

  @param capacityPercentageResolution (float)
  An alternate way of specifying the resolution. For example, if
  capacityPercentageResolution=0.01, if the actual capacity is 3020 and this
  method has reached 3000, it will stop searching, because the next increment
  will be less than 1% of 3000.

  @param minAccuracy (float)
  The recognition success rate that the model must achieve.
  """
  if not os.path.exists("traces"):
    os.makedirs("traces")

  if seed1 != -1:
    np.random.seed(seed1)

  if seed2 != -1:
    random.seed(seed2)

  features = [str(i) for i in xrange(numFeatures)]

  locationConfigs = []
  scale = 40.0

  if thresholds == -1:
    thresholds = int(math.ceil(numModules * 0.8))
  elif thresholds == 0:
    thresholds = numModules
  perModRange = float((90.0 if bumpType == "square" else 60.0) /
                      float(numModules))
  for i in xrange(numModules):
    orientation = (float(i) * perModRange) + (perModRange / 2.0)

    config = {
      "cellsPerAxis": locationModuleWidth,
      "scale": scale,
      "orientation": np.radians(orientation),
      "activationThreshold": 8,
      "initialPermanence": 1.0,
      "connectedPermanence": 0.5,
      "learningThreshold": 8,
      "sampleSize": 10,
      "permanenceIncrement": 0.1,
      "permanenceDecrement": 0.0,
    }

    if bumpType == "square":
      config["cellCoordinateOffsets"] = cellCoordinateOffsets
      config["anchoringMethod"] = anchoringMethod
    elif bumpType == "gaussian":
      config["bumpOverlapMethod"] = "probabilistic"
      config["baselineCellsPerAxis"] = 6
    else:
      raise ValueError("Invalid bumpType", bumpType)

    locationConfigs.append(config)

  l4Overrides = {
    "initialPermanence": 1.0,
    "activationThreshold": thresholds,
    "reducedBasalThreshold": thresholds,
    "minThreshold": numModules,
    "sampleSize": numModules,
    "cellsPerColumn": 16,
  }

  numObjects = 0
  accuracy = None
  allLocationsAreUnique = None
  occurrencesConvergenceLog = []

  increment = initialIncrement
  foundUpperBound = False

  while True:
    currentNumObjects = numObjects + increment
    numFailuresAllowed = currentNumObjects * (1 - minAccuracy)
    print "Testing", currentNumObjects

    objects = generateObjects(currentNumObjects, featuresPerObject, objectWidth,
                              numFeatures, featureDistribution)

    column = PIUNCorticalColumn(locationConfigs, L4Overrides=l4Overrides,
                                bumpType=bumpType)
    exp = PIUNExperiment(column, featureNames=features,
                         numActiveMinicolumns=10,
                         noiseFactor=noiseFactor,
                         moduleNoiseFactor=moduleNoiseFactor)

    currentLocsUnique = True

    for objectDescription in objects:
      objLocsUnique = exp.learnObject(objectDescription)
      currentLocsUnique = currentLocsUnique and objLocsUnique

    numFailures = 0

    try:
      if useTrace:
        filename = os.path.join(
          SCRIPT_DIR,
          "traces/capacity-{}-points-{}-cells-{}-objects-{}-feats.html".format(
            len(cellCoordinateOffsets)**2, exp.column.L6aModules[0].numberOfCells(),
            currentNumObjects, numFeatures)
        )
        traceFileOut = io.open(filename, "w", encoding="utf8")
        traceHandle = trace(traceFileOut, exp, includeSynapses=False)
        print "Logging to", filename

      for objectDescription in objects:
        numSensationsToInference = exp.inferObjectWithRandomMovements(
            objectDescription)
        if numSensationsToInference is None:
          numFailures += 1

          if numFailures > numFailuresAllowed:
            break
    finally:
      if useTrace:
        traceHandle.__exit__()
        traceFileOut.close()

    if numFailures < numFailuresAllowed:
      numObjects = currentNumObjects
      accuracy = float(currentNumObjects - numFailures) / currentNumObjects
      allLocationsAreUnique = currentLocsUnique
    else:
      foundUpperBound = True

    if foundUpperBound:
      increment /= 2

      goalResolution = capacityResolution

      if capacityPercentageResolution > 0:
        goalResolution = min(goalResolution,
                             capacityPercentageResolution*numObjects)

      if increment < goalResolution:
        break

  result = {
    "numObjects": numObjects,
    "accuracy": accuracy,
    "allLocationsAreUnique": allLocationsAreUnique,
  }

  print result
  return result
def doExperiment(numObjects, featuresPerObject, objectWidth, numFeatures,
                 featureDistribution, useTrace, noiseFactor, moduleNoiseFactor,
                 numModules, thresholds, inverseReadoutResolution,
                 enlargeModuleFactor, bumpOverlapMethod, seed1, seed2):
    """
  Learn a set of objects. Then try to recognize each object. Output an
  interactive visualization.
  """
    if not os.path.exists("traces"):
        os.makedirs("traces")

    if seed1 != -1:
        np.random.seed(seed1)

    if seed2 != -1:
        random.seed(seed2)

    features = [str(i) for i in xrange(numFeatures)]
    objects = generateObjects(numObjects, featuresPerObject, objectWidth,
                              numFeatures, featureDistribution)

    locationConfigs = []
    scale = 40.0

    if thresholds == -1:
        thresholds = int(math.ceil(numModules * 0.8))
    elif thresholds == 0:
        thresholds = numModules
    perModRange = float(60.0 / float(numModules))
    for i in xrange(numModules):
        orientation = (float(i) * perModRange) + (perModRange / 2.0)

        locationConfigs.append({
            "scale": scale,
            "orientation": np.radians(orientation),
            "activationThreshold": 8,
            "initialPermanence": 1.0,
            "connectedPermanence": 0.5,
            "learningThreshold": 8,
            "sampleSize": 10,
            "permanenceIncrement": 0.1,
            "permanenceDecrement": 0.0,
            "inverseReadoutResolution": inverseReadoutResolution,
            "enlargeModuleFactor": enlargeModuleFactor,
            "bumpOverlapMethod": bumpOverlapMethod,
        })

    l4Overrides = {
        "initialPermanence": 1.0,
        "activationThreshold": thresholds,
        "reducedBasalThreshold": thresholds,
        "minThreshold": numModules,
        "sampleSize": numModules,
        "cellsPerColumn": 16,
    }

    column = PIUNCorticalColumn(locationConfigs,
                                L4Overrides=l4Overrides,
                                bumpType="gaussian2")
    exp = PIUNExperiment(column,
                         featureNames=features,
                         numActiveMinicolumns=10,
                         noiseFactor=noiseFactor,
                         moduleNoiseFactor=moduleNoiseFactor)

    for objectDescription in objects:
        exp.learnObject(objectDescription)

    convergence = collections.defaultdict(int)
    try:
        if useTrace:
            filename = os.path.join(
                SCRIPT_DIR,
                "traces/{}-resolution-{}-modules-{}-objects-{}-feats.html".
                format(inverseReadoutResolution, numModules, numObjects,
                       numFeatures))
            traceFileOut = io.open(filename, "w", encoding="utf8")
            traceHandle = trace(traceFileOut, exp, includeSynapses=True)
            print "Logging to", filename

        for objectDescription in objects:
            steps = exp.inferObjectWithRandomMovements(objectDescription)
            convergence[steps] += 1
            if steps is None:
                print 'Failed to infer object "{}"'.format(
                    objectDescription["name"])
    finally:
        if useTrace:
            traceHandle.__exit__()
            traceFileOut.close()

    for step, num in sorted(convergence.iteritems()):
        print "{}: {}".format(step, num)

    result = {
        "convergence": convergence,
    }

    return result
Esempio n. 6
0
def doExperiment(locationModuleWidth, bumpType, cellCoordinateOffsets,
                 initialIncrement, minAccuracy, capacityResolution,
                 capacityPercentageResolution, featuresPerObject, objectWidth,
                 numFeatures, featureDistribution, useTrace, noiseFactor,
                 moduleNoiseFactor, numModules, thresholds, seed1, seed2,
                 anchoringMethod):
    """
  Finds the capacity of the specified model and object configuration. The
  algorithm has two stages. First it finds an upper bound for the capacity by
  repeatedly incrementing the number of objects by initialIncrement. After it
  finds a number of objects that is above capacity, it begins the second stage:
  performing a binary search over the number of objects to find an exact
  capacity.

  @param initialIncrement (int)
  For example, if this number is 128, this method will test 128 objects, then
  256, and so on, until it finds an upper bound, then it will narrow in on the
  breaking point. This number can't be incorrect, but the simulation will be
  inefficient if it's too low or too high.

  @param capacityResolution (int)
  The resolution of the capacity. If capacityResolution=1, this method will find
  the exact capacity. If the capacityResolution is higher, the method will
  return a capacity that is potentially less than the actual capacity.

  @param capacityPercentageResolution (float)
  An alternate way of specifying the resolution. For example, if
  capacityPercentageResolution=0.01, if the actual capacity is 3020 and this
  method has reached 3000, it will stop searching, because the next increment
  will be less than 1% of 3000.

  @param minAccuracy (float)
  The recognition success rate that the model must achieve.
  """
    if not os.path.exists("traces"):
        os.makedirs("traces")

    if seed1 != -1:
        np.random.seed(seed1)

    if seed2 != -1:
        random.seed(seed2)

    features = [str(i) for i in xrange(numFeatures)]

    locationConfigs = []
    scale = 40.0

    if thresholds == -1:
        thresholds = int(math.ceil(numModules * 0.8))
    elif thresholds == 0:
        thresholds = numModules
    perModRange = float(
        (90.0 if bumpType == "square" else 60.0) / float(numModules))
    for i in xrange(numModules):
        orientation = (float(i) * perModRange) + (perModRange / 2.0)

        config = {
            "cellsPerAxis": locationModuleWidth,
            "scale": scale,
            "orientation": np.radians(orientation),
            "activationThreshold": 8,
            "initialPermanence": 1.0,
            "connectedPermanence": 0.5,
            "learningThreshold": 8,
            "sampleSize": 10,
            "permanenceIncrement": 0.1,
            "permanenceDecrement": 0.0,
        }

        if bumpType == "square":
            config["cellCoordinateOffsets"] = cellCoordinateOffsets
            config["anchoringMethod"] = anchoringMethod
        elif bumpType == "gaussian":
            config["bumpOverlapMethod"] = "probabilistic"
            config["baselineCellsPerAxis"] = 6
        else:
            raise ValueError("Invalid bumpType", bumpType)

        locationConfigs.append(config)

    l4Overrides = {
        "initialPermanence": 1.0,
        "activationThreshold": thresholds,
        "reducedBasalThreshold": thresholds,
        "minThreshold": numModules,
        "sampleSize": numModules,
        "cellsPerColumn": 16,
    }

    numObjects = 0
    accuracy = None
    allLocationsAreUnique = None
    occurrencesConvergenceLog = []

    increment = initialIncrement
    foundUpperBound = False

    while True:
        currentNumObjects = numObjects + increment
        numFailuresAllowed = currentNumObjects * (1 - minAccuracy)
        print "Testing", currentNumObjects

        objects = generateObjects(currentNumObjects, featuresPerObject,
                                  objectWidth, numFeatures,
                                  featureDistribution)

        column = PIUNCorticalColumn(locationConfigs,
                                    L4Overrides=l4Overrides,
                                    bumpType=bumpType)
        exp = PIUNExperiment(column,
                             featureNames=features,
                             numActiveMinicolumns=10,
                             noiseFactor=noiseFactor,
                             moduleNoiseFactor=moduleNoiseFactor)

        currentLocsUnique = True

        for objectDescription in objects:
            objLocsUnique = exp.learnObject(objectDescription)
            currentLocsUnique = currentLocsUnique and objLocsUnique

        numFailures = 0

        try:
            if useTrace:
                filename = os.path.join(
                    SCRIPT_DIR,
                    "traces/capacity-{}-points-{}-cells-{}-objects-{}-feats.html"
                    .format(
                        len(cellCoordinateOffsets)**2,
                        exp.column.L6aModules[0].numberOfCells(),
                        currentNumObjects, numFeatures))
                traceFileOut = io.open(filename, "w", encoding="utf8")
                traceHandle = trace(traceFileOut, exp, includeSynapses=False)
                print "Logging to", filename

            for objectDescription in objects:
                numSensationsToInference = exp.inferObjectWithRandomMovements(
                    objectDescription)
                if numSensationsToInference is None:
                    numFailures += 1

                    if numFailures > numFailuresAllowed:
                        break
        finally:
            if useTrace:
                traceHandle.__exit__()
                traceFileOut.close()

        if numFailures < numFailuresAllowed:
            numObjects = currentNumObjects
            accuracy = float(currentNumObjects -
                             numFailures) / currentNumObjects
            allLocationsAreUnique = currentLocsUnique
        else:
            foundUpperBound = True

        if foundUpperBound:
            increment /= 2

            goalResolution = capacityResolution

            if capacityPercentageResolution > 0:
                goalResolution = min(goalResolution,
                                     capacityPercentageResolution * numObjects)

            if increment < goalResolution:
                break

    result = {
        "numObjects": numObjects,
        "accuracy": accuracy,
        "allLocationsAreUnique": allLocationsAreUnique,
    }

    print result
    return result