Monty.Attention.VoxelGrid (ExTbpMonty.Core v0.1.0)

Copy Markdown View Source

A sparse grid of per-voxel weights over 3-D space: Monty's attention/voxel_grid.py::VoxelGrid.

An attention system remembers where it has looked, and it remembers it in voxels: space is cut into cubes voxel_size on a side, and each cube that has been looked at holds one weight. Space is mostly empty, so the grid is sparse — Monty backs it with a pandas DataFrame whose index is the occupied voxels' coordinates; here it is a map from a voxel to its weight, which is the same thing with less machinery.

Which voxel a point is in

Voxels are half-open: a point on the face shared by two voxels belongs to the one with the larger coordinate, so voxel v spans [v, v + 1) in units of voxel_size. That is floor(point / voxel_size) and it is why a point at exactly 0.0 is in voxel 0 while one at -0.0001 is in voxel -1.

What is not here

Monty's representation is public, so its tests can build a malformed DataFrame and check that validate_dataframe refuses it: no (x, y, z) index, a duplicated index, no weight column, a weight column that is not one dimension. A map cannot be malformed in those ways — a duplicate voxel is simply one entry — so what survives of those four cases here is the one rule a map can break: new/3 refuses two weights for one voxel rather than letting the last one win. from_pandas/2 and to_pandas/1 have no counterpart.

Summary

Types

t()

A voxel's lower corner, in voxels.

Functions

A grid with no occupied voxels.

The grid as JSON-encodable data — Monty's encode_voxel_grid.

A grid over the given voxels.

The number of occupied voxels.

Edge length of a voxel.

Each point's voxel alongside its weight — Monty's voxelize_and_bin_points.

Which voxel each point is in — Monty's voxelize_points.

The occupied voxels, in a stable order (the tuples sort).

The weights of the occupied voxels, in the same order as voxels/1.

The grid's weight at each point.

Types

t()

@type t() :: %Monty.Attention.VoxelGrid{
  voxel_size: float(),
  weights: %{optional(voxel()) => number()}
}

voxel()

@type voxel() :: {integer(), integer(), integer()}

A voxel's lower corner, in voxels.

Functions

empty(voxel_size)

@spec empty(float()) :: t()

A grid with no occupied voxels.

encode(grid)

@spec encode(t()) :: %{optional(String.t()) => term()}

The grid as JSON-encodable data — Monty's encode_voxel_grid.

voxels and weight are parallel lists, which is how Monty writes the MultiIndex it cannot serialise.

new(voxel_size, voxels, weights)

@spec new(float(), [voxel()], [number()]) :: t()

A grid over the given voxels.

Refuses two weights for one voxel: a voxel holds one weight, and silently keeping the last would turn a caller's bookkeeping bug into a grid that looks fine.

size(voxel_grid)

@spec size(t()) :: non_neg_integer()

The number of occupied voxels.

voxel_size(voxel_grid)

@spec voxel_size(t()) :: float()

Edge length of a voxel.

voxelize_and_bin_points(voxel_size, points, weights)

@spec voxelize_and_bin_points(float(), [[number()]], [number()]) :: [
  {voxel(), number()}
]

Each point's voxel alongside its weight — Monty's voxelize_and_bin_points.

The rows come back in point order, which is what a caller that wants to group them by voxel needs to keep the point indices.

voxelize_points(voxel_size, points)

@spec voxelize_points(float(), [[number()]]) :: [voxel()]

Which voxel each point is in — Monty's voxelize_points.

floor rather than trunc: the voxels are half-open, so the point at -0.5 is in voxel -1, not in voxel 0.

voxels(voxel_grid)

@spec voxels(t()) :: [voxel()]

The occupied voxels, in a stable order (the tuples sort).

weights(grid)

@spec weights(t()) :: [number()]

The weights of the occupied voxels, in the same order as voxels/1.

weights_at_points(grid, points, options \\ [])

@spec weights_at_points(t(), [[number()]], keyword()) :: [number() | nil]

The grid's weight at each point.

:fill is what an unoccupied voxel answers — nil by default, the NaN of this port, which says "no weight here" rather than "a weight of zero".