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 |
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