Skip to content

Reference for FieldDiscretizer

This utility is used to transform the continuous 2d spaces into a 1d discretized space with variable resolution. The method of doing so was adapted from this source. By default it used the MIT aquaticus field lines to define the 2d space.

The discrete space is a 0...n space where 0 represents an input that is outside the continuous space.

See to_discrete_idx() for the primary means of translation.

Source code in mivp_agent/aquaticus/field.py
class FieldDiscretizer:
  '''
  This utility is used to transform the continuous 2d spaces into a 1d discretized space with variable resolution. The method of doing so was adapted from [this source](https://stackoverflow.com/questions/62778939/python-fastest-way-to-map-continuous-coordinates-to-discrete-grid). By default it used the MIT aquaticus field lines to define the 2d space.

  The discrete space is a 0...n space where 0 represents an input that is **outside** the continuous space.

  See 
  [`to_discrete_idx()`][mivp_agent.aquaticus.field.FieldDiscretizer.to_discrete_idx] for the primary means of translation.
  '''
  def __init__(self, resolution=6):
    '''
    Args:
      resolution (int): This was named **poorly**. It is not the resolution but the opposite. This variable represents the step size used between discrete points. Incresing this parameter will result in a discrete space of smaller size.
    '''
    # Find the min/max x and y in the field
    min = None
    max = None
    for c in FIELD_CORNERS:
      if min is None:
        min = list(c) # Change to list for mutability
      else:
        if min[0] > c[0]:
          min[0] = c[0]
        elif min[1] > c[1]:
          min[1] = c[1]

      if max is None:
        max = list(c)
      else:
        if max[0] < c[0]:
          max[0] = c[0]
        if max[1] < c[1]:
          max[1] = c[1]

    # Define the offset (starting point) for our grid
    self._offset = np.array([min[0], min[1]])
    # Define the spacing (resolution) for the grid
    self._spacing = np.array([resolution, resolution])

    # Create map between grid points and an index (different than the reference's use of index)
    index = 1
    self._point_idx_map = {
      None: 0 # Used for points off the field
    }
    self._idx_point_map = [None]

    # The below maps the space we are creating to 0...n values for both x and y. This is useful when later binning / counting the occurrences of data in after collected. For example if we have a grid x,y we can create a counter where `count[0][0]` is the count of the first discrete point on our grid. But our discrete grid starts at whatever `min` is no zero. So we must map from the discrete x,y to these indexes in the counter. We do this through `enumerate(range(....))` to map from some x to 0...n.
    # Note: Most times, len(self._xs) and len(self._ys) will not be the same size, and contain values for x & y values which will never be mapped to. This is to make storage in arrays easy too (easy to define a count[n][m]).
    self._xs = {x:i for i, x in enumerate(range(min[0], max[0]+1, resolution))}
    self._ys = {y:i for i, y in enumerate(range(min[1], max[1]+1, resolution))}

    # Interate through the x, y values to find the ones in range
    for x in self._xs:
      for y in self._ys:
        # Add to map if in bounds 
        if in_bounds(np.array([x,y])):
          self._point_idx_map[(x,y)] = index
          index += 1
          self._idx_point_map.append((x,y))

    self.space_size = len(self._point_idx_map)
    # Sanity check
    assert self.space_size == len(self._idx_point_map)

  def to_discrete_point(self, nav_x, nav_y):
    '''
    This method translates continuous x / y coordinates into discrete x / y coordinates.

    Args:
      nav_x (float): A continuous x coordinate
      nav_y (float): A continuous y coordinate

    Returns:
      tuple/None: Will return a `tuple` with (x, y) of type `int` or `None` if the input is outside of the defined space.
    '''
    d = self._offset + np.round((np.array([nav_x, nav_y]) - self._offset) / self._spacing) * self._spacing
    d = tuple(d.astype(int))

    # Handle out of feild positions
    if d not in self._point_idx_map:
      return None

    return d

  def to_discrete_idx(self, nav_x, nav_y):
    '''
    This method translates continuous x / y coordinates into discrete 0...n index. 0 indicates that the input was outside of the defined 2d space.

    Args:
      nav_x (float): A continuous x coordinate
      nav_y (float): A continuous y coordinate

    Returns:
      int: A discrete index corresponding to a unique point on the 2d discrete field.
    '''
    d = self.to_discrete_point(nav_x, nav_y)
    return self._point_idx_map[d]

  def idx_to_discrete_point(self, idx):
    if idx >= self.space_size:
      raise ValueError('Index is outside of discrete space')

    return self._idx_point_map[idx]

__init__(self, resolution=6) special

Parameters:

Name Type Description Default
resolution int

This was named poorly. It is not the resolution but the opposite. This variable represents the step size used between discrete points. Incresing this parameter will result in a discrete space of smaller size.

6
Source code in mivp_agent/aquaticus/field.py
def __init__(self, resolution=6):
  '''
  Args:
    resolution (int): This was named **poorly**. It is not the resolution but the opposite. This variable represents the step size used between discrete points. Incresing this parameter will result in a discrete space of smaller size.
  '''
  # Find the min/max x and y in the field
  min = None
  max = None
  for c in FIELD_CORNERS:
    if min is None:
      min = list(c) # Change to list for mutability
    else:
      if min[0] > c[0]:
        min[0] = c[0]
      elif min[1] > c[1]:
        min[1] = c[1]

    if max is None:
      max = list(c)
    else:
      if max[0] < c[0]:
        max[0] = c[0]
      if max[1] < c[1]:
        max[1] = c[1]

  # Define the offset (starting point) for our grid
  self._offset = np.array([min[0], min[1]])
  # Define the spacing (resolution) for the grid
  self._spacing = np.array([resolution, resolution])

  # Create map between grid points and an index (different than the reference's use of index)
  index = 1
  self._point_idx_map = {
    None: 0 # Used for points off the field
  }
  self._idx_point_map = [None]

  # The below maps the space we are creating to 0...n values for both x and y. This is useful when later binning / counting the occurrences of data in after collected. For example if we have a grid x,y we can create a counter where `count[0][0]` is the count of the first discrete point on our grid. But our discrete grid starts at whatever `min` is no zero. So we must map from the discrete x,y to these indexes in the counter. We do this through `enumerate(range(....))` to map from some x to 0...n.
  # Note: Most times, len(self._xs) and len(self._ys) will not be the same size, and contain values for x & y values which will never be mapped to. This is to make storage in arrays easy too (easy to define a count[n][m]).
  self._xs = {x:i for i, x in enumerate(range(min[0], max[0]+1, resolution))}
  self._ys = {y:i for i, y in enumerate(range(min[1], max[1]+1, resolution))}

  # Interate through the x, y values to find the ones in range
  for x in self._xs:
    for y in self._ys:
      # Add to map if in bounds 
      if in_bounds(np.array([x,y])):
        self._point_idx_map[(x,y)] = index
        index += 1
        self._idx_point_map.append((x,y))

  self.space_size = len(self._point_idx_map)
  # Sanity check
  assert self.space_size == len(self._idx_point_map)

to_discrete_idx(self, nav_x, nav_y)

This method translates continuous x / y coordinates into discrete 0...n index. 0 indicates that the input was outside of the defined 2d space.

Parameters:

Name Type Description Default
nav_x float

A continuous x coordinate

required
nav_y float

A continuous y coordinate

required

Returns:

Type Description
int

A discrete index corresponding to a unique point on the 2d discrete field.

Source code in mivp_agent/aquaticus/field.py
def to_discrete_idx(self, nav_x, nav_y):
  '''
  This method translates continuous x / y coordinates into discrete 0...n index. 0 indicates that the input was outside of the defined 2d space.

  Args:
    nav_x (float): A continuous x coordinate
    nav_y (float): A continuous y coordinate

  Returns:
    int: A discrete index corresponding to a unique point on the 2d discrete field.
  '''
  d = self.to_discrete_point(nav_x, nav_y)
  return self._point_idx_map[d]

to_discrete_point(self, nav_x, nav_y)

This method translates continuous x / y coordinates into discrete x / y coordinates.

Parameters:

Name Type Description Default
nav_x float

A continuous x coordinate

required
nav_y float

A continuous y coordinate

required

Returns:

Type Description
tuple/None

Will return a tuple with (x, y) of type int or None if the input is outside of the defined space.

Source code in mivp_agent/aquaticus/field.py
def to_discrete_point(self, nav_x, nav_y):
  '''
  This method translates continuous x / y coordinates into discrete x / y coordinates.

  Args:
    nav_x (float): A continuous x coordinate
    nav_y (float): A continuous y coordinate

  Returns:
    tuple/None: Will return a `tuple` with (x, y) of type `int` or `None` if the input is outside of the defined space.
  '''
  d = self._offset + np.round((np.array([nav_x, nav_y]) - self._offset) / self._spacing) * self._spacing
  d = tuple(d.astype(int))

  # Handle out of feild positions
  if d not in self._point_idx_map:
    return None

  return d