Skip to content

Commit

Permalink
Add hilbert matrix as simple example
Browse files Browse the repository at this point in the history
  • Loading branch information
griswaldbrooks committed Sep 15, 2024
1 parent cb1e8ab commit df13ebf
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 25 deletions.
50 changes: 39 additions & 11 deletions OUTLINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,35 @@ that they are not limited and can leverage the full capabilities of C++
## Features of std::mdspan
- Multi-dimensional view type without memory layout restrictions
- Customization points: layout and accessor policies
- strategy pattern
- make this really short as we will go more in depth later but in short layout describe how to translate a multidimensional index to a storage location, and accessors describe how to retrive data from the storage location
- we will learn how layout and accessor policies work, what the type requirements of them are, how to implement your own custom policies, and how they leverage the full flexibility of C++

## Layout policies and how to use them
- Explain what a layout is
- index to memory location mapping
- explain with ascii art
- briefly explain out of the box layouts, right, left, stride
- i really like the downsampling example for a 2d stride
- no requirement on default constructability
- explain layout requirements being a templated mapping type with template Extents parameter
extents_type const& extents() const
- extents represent the dimensionality of the object ie number of dimensions (rank) and how long
they are
size_type operator()(auto indices...) // you choose how many!
size_type required_span_size() const
- the number of contiguous elements in the referred data structure needed to contain the spanned elements
- static constexpr bool is_always_unique()
- bool is_unique()
- use timur's example of when this is false
- show how a symmetric matrix index mapping and diagram as the code for a layout this early would be premature
static constexpr bool is_always_exhaustive()
bool is_exhaustive()
- compare layout
- show how layout_strided is not exhaustive
static constexpr bool is_always_strided()
bool is_strided()
- has consistent constant offsets, all strided layouts are non-exhaustive but we will show
examples of layouts that are not exhaustive and have no consistent stride
https://eel.is/c++draft/mdspan.layout#reqmts
- no requirement on default constructability
- briefly explain out of the box layouts, right, left, stride
- i really like the downsampling example for a 2d stride
- Occupancy grids
- briefly explain occupancy grid and the trinary scheme
- show how to implement "global" occupancy grid with std::vector
Expand All @@ -63,8 +71,12 @@ that they are not limited and can leverage the full capabilities of C++
- if the submap could follow the orientation of the robot this wouldn't be a problem
- this means we get to define our own layout!
- which ends up being an exercise in indice mapping
- non-defaul ctor
- skew rotation algorithm mapping
- show layout_rotated from it's point of view
- show layout_rotatable and all of the indices which won't map in the square to the range
and therefore is not exhaustive nor strided
- make sure the example here has a nice sharp corner near the top
- Polygonal views and arbitrary layouts
- The above example shows the power of writing functional non-square layout, we can go further
- We can generate a set of indices, much like you might do with the std::cartesian example
Expand All @@ -77,20 +89,29 @@ that they are not limited and can leverage the full capabilities of C++
- this allows me to add obstacles to my map
- collision check my robot footprint
- clear and mark lidar points
- conclusion to this section?
- lead in to the next section?
- conclusion to this section
- mention that we learned what a layout is and it's requirements
- what the out of the box layouts do
- how we can make our own layouts with non-default constructable
- have state
- dimensionality reduction
- lead in to the next section
- these examples still referred to elements on the stack/heap
- but what if our data lives somewhere else?

## Accessor policies and how to use them
- Explain what accessor policies are (reprise intro on customization points briefly)
- they take the offset from the layout and return data from a storage location
- Explain how the default accessor works ie return p[offset]
- Talk about the type requirements of an accessor, ie using element_type, reference, data_handle_type
- accessor has no requirements on default constructability
- Note there are no template requirements like the layout policy has
- Note that they are not limited to this pattern
- In fact... you can call arbitrary functions in the accessor
- Show the simple std function accessor example and how the mdspan::T must match accessor::element_type
but the first ctor element must match accessor::data_handle_type
- show a hilbert matrix
- note that accessor::reference does not need to be a & or * or even related to accessor::element_type
- accessor has no requirements on default constructability
- Daisy Hollman said... Accessor policies interfacing with GPUs (e.g., CUDA calls)
- But let's see what we can do
- I don't work on GPUs, but I do work on robots
Expand All @@ -107,13 +128,15 @@ that they are not limited and can leverage the full capabilities of C++
- also here's the github
- what's different from the default accessor policy
- first of all, calling a blocking function in an accessor
- i mean the default one is blocking but this one takes seconds to access an element
- show the initial version of this did not require a reference to stack/heap memory and
was purely functional
- this version only uses the pointed to array from the json to give the metric locations
- but let's not the anatomy of the accessor type declaration and ctor
- but let's now talk about the anatomy of the accessor type declaration and ctor
- note how those two parameters, that normally would be realted, need not be
and are actually unrelated and depend on the accessor types
and are actually unrelated and depend on the accessor types and mentioned previously
- this allows the element type to be a bin_state, but the data_handle_type to be the robot arbiter
as with our lambda example from earlier
- so now we can access the elements, which causes us to command the robot arm to check the appropriate bin
- Synchronous command of dual robot arms
- problem: this seems slow and inefficient... but what if we added another robot arm
Expand All @@ -126,7 +149,12 @@ that they are not limited and can leverage the full capabilities of C++
- show the arbiter using std::jthread and std::packaged_task with a thread_safe_queue
- now, the accessor is calling functions that create std::futures!
- this isn't really anything special about accessors as they are just types with a few requirements

- to summarize
- we learned what accessor policies do and how the default one works
- we learned the requirements of the accessor, and what aren't requirements
- we learned how to implement our own accessor from a very simple lambda calculation
to something very complicated
- we learned that accessors can return async resources because they can use the full flexibility of C++
## Conclusion
- Summary of mdspan features and benefits
- Discussion of use cases in fleet management and warehouse inventory control
Expand Down
31 changes: 17 additions & 14 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,15 @@ bool is_ready(std::future<R> const& f) {
return f.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready;
}

template<std::size_t Width>
struct hilbert_accessor_t {
using element_type = double;
using reference = element_type;
using size_type = std::size_t;
using data_handle_type = std::function<double(size_type, size_type)>;

reference access(data_handle_type p, size_type offset) const { return p(offset, Width); }
};
int main() {
std::ifstream bin_file{"src/spanny2/config/bin_config.json"};
json bin_config = json::parse(bin_file);
Expand Down Expand Up @@ -932,22 +941,16 @@ int main() {
// }
}

struct remote_accessor_t {
using element_type = int;
using reference = element_type;
using size_type = std::size_t;
using data_handle_type = std::function<int(size_type)>;

reference access(data_handle_type p, size_type offset) const { return p(offset); }
using hilbert_view_t = std::mdspan<double, std::extents<std::size_t, 3, 3>, std::layout_right, hilbert_accessor_t<3>>;
auto hilbert_function = [](std::size_t ndx, std::size_t width) -> double {
auto const n = static_cast<double>(ndx);
auto const M = static_cast<double>(width);
return 1. / (std::floor(n / M) + std::fmod(n, M) + 1);
};

using remote_view_t =
std::mdspan<int, std::extents<std::size_t, 3, 3>, std::layout_right, remote_accessor_t>;

auto nl_function = [](std::size_t ndx) -> int { return static_cast<int>(ndx); };

auto remote_view = remote_view_t{nl_function};
assert(4 == remote_view(1, 1));

auto hilbert_view = hilbert_view_t{hilbert_function};
std::cout << hilbert_view(2, 2) << std::endl;
return 0;
/* TEST DUAL ARM ASYNCHRONOUS */
{
Expand Down
2 changes: 2 additions & 0 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,8 @@ int main(int /* argc */, char** /* argv[] */) {
double const orientation = 2.;
set_scan(global_map, laser_scan, center, orientation);
std::cout << global_map << std::endl;
std::cout << local_map.is_exhaustive() << std::endl;
std::cout << local_map.is_strided() << std::endl;
// save_occupancy_grid("occupancy_grid.png", global_map);
return 0;
}

0 comments on commit df13ebf

Please sign in to comment.