-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ball/Metric Tree using NearestNeighbors.jl / Manifolds.jl / Distances.jl / CoordinateTransformations.jl #41
Comments
We've integrated Manifolds.jl and Distances.jl here: https://github.com/JuliaManifolds/ManifoldML.jl/blob/master/src/distances.jl . |
Hi @mateuszbaran , oh great thanks -- would you recommend i go down this path (JuliaManifolds/Manifolds.jl#268 (comment))? Basically I'm trying to plan how to do a separate and new rewrite of the KernelDensityEstimate.jl package by first building/exporting a new EDIT, see more comments at Manifolds.jl 268 |
Hi! I see there is quite a lot you're trying to do. Here you can see how to do nearest neighbor search using distances on manifolds: https://juliamanifolds.github.io/ManifoldML.jl/dev/knn.html . The example is for kNN classifier but it can be adapted to NN descent for any purpose.
Special Euclidean group is one of the trickier manifolds to work with. First of all it's a product manifold, and we have two representations for points and tangent vectors on product manifolds, and then Seth wrote yet another representation. As he responded in that issue, SE doesn't currently support manifold operations on the matrix representation but it could be added. I quite like his idea to make an
By the way, what representation of points on SE(2) are you currently using?
What do you mean by |
Thanks, I also worked through your other reply (JuliaManifolds/Manifolds.jl#268 (comment))
Yes there are a lot of things but it has been coming for a long time. I have been collecting various aspects within the Caesar.jl project and probably time to address the consolidation/standardization with a common JuliaLang definition of Manifolds.
Oh great thanks, I missed that.
This sounds like the way to go then. I'm probably a little behind on understanding precisely how you intend to expand
Okay, will keep this in mind thanks.
Currently my own hacky bare-minimum definitions as part of a wait-and-see on how to best consolidate Manifolds for Julia in general. Thanks to work from @Affie we at least got our definitions down to a single clean Way back in the beginning (like in Julia v0.1 days) I wrote a quick and hacky TransformUtils.jl and have been trying to get away from that ever since (but has been good enough until now). This was long before StaticArrays.jl even existed. CoordinateTransforms.jl (CT.jl) came online a little later and the plan became to rebrand and gut TransformUtils.jl to better use CT.jl, and then reintroduce some of the user friendly "one button" features to automatically switch between manifolds as necessary -- e.g. AngleAxis/SO3/Quaternion/Euler should not require the user to forcefully convert up and down, but instead use multiple dispatch and promotion with common sense rules like SO3 * Quat ==> SO3 (assuming convention aV = aRb*bV). That was all fine until the plotting libraries also started making
Building kNN if a metric exists, to generalize "ball tree" beyond Euclidean distance:
This is exactly what im trying to do as proof of concept -- i.e. trying to find a roadmap of how to do all this consolidation work. If you could help point in the right direction for how to build a tree over SE2 that would be great. Please note that I already did that once, but this was using the hacky manifold definitions (and also I want a fresh implementation under permissive open source license, not something restricted to LGPL as the prior implementation). Won't link it here so that we can keep the copyright as separate as possible :-P. Perhaps I can ask again from JuliaManifolds/Manifolds.jl#268 (comment), this is what I was planning on doing for a "distance" (either logmap or Frobenius norm), but logmap seems more general for Riemannian: distance(::SE2, T2, T1) = norm(vee(logmap(inverse(T2) * T1)))
# or whatever is most efficient PS, once there is a route to building kNN for SE2 or SE3 using multiple dispatch around some sort of "metric" or "distance", ("MetricTree" as a grabbag term), I'd like to make my own manifold that is much much more intricate (integrated inertial navigation solution), you might recall an earlier comment
EDIT: Actually, let me add a shameless plug too: https://marinerobotics.mit.edu/sites/default/files/fourie_iros19_manifolds.pdf Here is the sandbox where I'm trying to find the way forward: |
I've made a small example how you can make a NN descent using Manifolds: using StaticArrays, Manifolds, NearestNeighbors, Distances
M = SpecialEuclidean(2)
N = 100
# convert point to coordinates
function coords(p)
return SA[p.parts[1][1], p.parts[1][2], atan(p.parts[2][2,1],p.parts[2][1,1])] # fixed from acos(p.parts[2][1,1]
end
# reverse of `coords`
function uncoords(p)
α = p[3]
return ProductRepr((SA[p[1], p[2]]), SA[cos(α) -sin(α); sin(α) cos(α)])
end
# some random points to make a tree from
pts = [uncoords(@SVector randn(3)) for _ in 1:N]
# The variant in ManifoldML doesn't support `ProductRepr` currently.
struct SE2Distance{TM<:Manifold} <: Distances.Metric
manifold::TM
end
function (dist::SE2Distance)(a, b)
return distance(dist.manifold, uncoords(a), uncoords(b))
end
dist = SE2Distance(M)
# making a tree
point_matrix = reduce(hcat, map(a -> coords(a), pts))
balltree = BallTree(point_matrix, dist)
# finding nearest neighbors
k = 3
idxs, dists = knn(balltree, coords(pts[2]), k) It could definitely be made a bit nicer, for example Currently we have a nice way of representing coordinates of tangent vectors (
That's good to know, I'll have to take a closer look at CT.jl. I think we just need to add dispatches to
We have a slightly different default definition of distance: https://github.com/JuliaManifolds/ManifoldsBase.jl/blob/aeded401bdd7b00029051a92e7fb5c4771fb998a/src/ManifoldsBase.jl#L292 . Anyway, most manifolds have better implementations because
Nice, that would be really cool 🙂 . |
I've taken a look there. @sethaxen, what do you think about integrating CoordinateTransformations.jl into Manifolds.jl for things like SE(N) or SO(N) or groups of translations? What we offer now is definitely not great. |
@mateuszbaran , thank you very much! I'm going to work through this now, this is I was missing before. Will look to build more in-place etc. and will call again when I have something to show.
Good to see the
Its looking like we are now going to do a major re-write of some core features between ApproxManifoldProducts.jl and IncrementalInference.jl to fix a whole slew of stuff identified in the last months including #42, JuliaRobotics/IncrementalInference.jl#1010, JuliaRobotics/IncrementalInference.jl#1025, JuliaRobotics/RoME.jl#244, and a few others. Thanks for the pointer on distances, it has an immediate impact for us here!
That's great! Will look at that more. Here on the Caesar.jl/RoME.jl side we are working to finish the 2020 abstraction / numerical quality round probably by early 2021, after which we plan to switch into a short performance round through late Spring '21. Just in case you hadn't seen yet, SLEEFPirates.jl do high speed hardware tricks with AVX. According to quick tests I did using
So it's an issue of consolidation between Manifolds.jl and CoordinateTransformations.jl regarding some of the manifolds? CT.jl is all about manipulating 3D points in XYZ space at maximum speed. The bad option is for each user to mix abstraction from Manifolds.jl with performance from CT.jl themselves, but we don't know until we try. So likely that the work here in AMP.jl and RoME.jl might be bit of guinea pig for how that will play out. Happy to move code upstream however is sensible. I'll link future comments on that to this issue for record keeping. I'd add to this that several of the JuliaRobotics packages (beyond the ones I'm directly involved with) and other packages in Julia ecosystem use CT.jl. CT.jl went hard early on performance by using StaticArrays.jl, so think it will be difficult to squeeze more speed out with a different implementation. I would say that CT.jl has good traction in the community, so sharing the load with them is advisable. I'd hope updating both Manifolds.jl and CT.jl will be doable to get best of both on abstraction and performance. |
Looking at CT.jl dependencies, it seems like an isolated package: At worst we can just lift efficient code from CT.jl into a Manifolds.jl related packages, or at best find the right compatibility between JuliaManifolds and JuliaGeometry. |
Oh sorry one more question @mateuszbaran, I'm just checking that the door is still open for using either Vector of elements or Matrix to build a NN tree -- i.e. it doesn't have to be a matrix of points concatenated together? From code above: balltree = BallTree(point_matrix, dist) What I mean is that there still is a route to instead do something like: vector_elem = coords.(pts)
balltree = BallTree(vector_elem, dist) EDIT, looks like it is working |
Tangentially related, just keeping link in this thread (apologies for noise): |
Just a small tip, when you see I know about SLEEFPirates but so far I've been struggling with more high-level performance bottlenecks so I haven't used it yet. There is quite a lot happening in Julia about such optimizations, for example with LoopVectorization and KernelAbstractions so I think it will only get easier to write fast code.
Cool! I don't quite understand what these issues are about but you can ask me about Manifolds.jl if it can help solve them 🙂 .
Maybe not so much a consolidation but I can definitely see a space for interoperability.
Sure, I already know StaticArrays.jl quite well and I care about interoperability between it and Manifolds.jl. I've even added a few things to StaticArrays.jl to make it easier. It definitely makes sense to make Manifolds.jl work well with CT.jl too.
I think it would be easiest to just make Manifolds.jl depend on CT.jl. Manifolds.jl is not intended to be a particularly lightweight package (although we are planning to split out some parts related to distributions to make it a bit lighter). Lightweight interface is in
Yes, sure, I didn't previously check how exactly NN.jl stores points and it actually makes the most sense to give it a
That's quite cool and we already have an issue about it: JuliaManifolds/ManifoldML.jl#4 . |
all good, thanks. Think i have what i need to move forward. Will post again when i have something to show. From there we can move code upstream to Manifolds.jl or however makes most sense. Will look at Manifolds.jl getting CT.jl as a dependency and will comment when i know more, but sounds like the cleanest solution and my initial reaction is yes that sounds like the right path. Last note, likely that i will try make a GeometryBasics.MetricSphere as a more general version of a HyperSphere. Imagine a "ball" in SE2/SE3 ProductManifold ... kinda weird, but no reason not too, right? Triangle Inequality with mechanism to detect shortest path that "might be behind you" (Manopt.jl perhaps) and circumference is now path around union of all subregions (likely non-differentiable). Perhaps piecewise Greens theorem or something like that will show up, either way i'm going to try. Thanks! |
Regular update that preparation work is underway for "vector of manifold elements" that will use content of this issue. Also note some ongoing precursor steps at: |
Hi! I've recently added more functionality related to distributions on manifolds. Here is an example of Pelletier's KDE: https://juliamanifolds.github.io/Manifolds.jl/stable/tutorials/integration.html#Kernel-density-estimation which looks relevant to this issue. That tutorial also has some notes about exponential-wrapped distributions. They are not fully implemented in Manifolds.jl yet but it shows that key tools are present. |
Hi @mateuszbaran, thanks! I looked through the KDE code you added. Just for information/trivia's sake, there are two specific features I am busy implementing which the Pelletier approach does not cover:
Further, something that might also not be obvious at first is that the KDEs we use have an internal Ball Tree structure (similar to NearestNeighbors, as above). We however need both balanced (and future work unbalanced) ball trees. Unfortunately the NearestNeighbor.jl implementation of Ball trees was not usable for our case. I'm implementing (i.e. forced to) all these features from scratch. To highlight the specialized requirements of the implementation, I have called the new on-manifold, metric dependent, balanced/unbalanced ball tree, non-isotropic, kernel agnostic, computational geometry enhanced KDE data structure a 'Manellic tree'. Work currently in progress and hoping to finally close out this years old issue in the near future. |
Hi! That sounds interesting. That Pelletier example is quite basic but the one interesting part of it you can use is the What exactly do you mean by partial dimension cases? |
So @Affie and I have been talking about this quite often. Loosely speaking it's the (sub) manifold equivalent of algebraic operations over say for example three variables with only two equations. So for a robotics localization example, if you have two range measurements from known beacons ( Our legacy code works these "partial" cases by converting to coordinates, conducting the computations on only the affected coordinates; and then retract back to group/manifold after. We're still debating which way we'd like to go forward on this, but it's looking like it will most likely be functional with traits approach. There is also the consideration of deferring "partial" computations to as late as possible and then invoking all the dispatches for projections etc. just before inference. |
This is an interesting problem. I would say that if we have such partial observations, this is no longer kernel density estimation but something more general. The main feature of KDE is that contributions to density from each observation are additive but here they are not. I was considering if KDE could be tricked to handle partial observations but there doesn't seem to be any elegant way to do this. The problem is that each sample in KDE has to be a complete one, and when we marginalize out the joint distributions, everything seems to fall apart. |
What we've done in the past is have a KDE data structure that can be partial but only certain "features" work. We need the product of KDEs as a major algorithm workhorse. So the ability to multiply various partial KDEs together allows us to reduce the partial nature in the posterior. E.g. |
TAC:
On-manifold KDE approx products, e.g.
pqr = p * q * r
, where p,q,r are probability densities, and on say SE(3) manifold, or similar.Note: probability density on manifold here is akin to (but "better than") heatmap on torus, or sphere, etc.
Tasks:
Related
Initial Comment
Simultaneously see #32 and JuliaRobotics/RoME.jl#244
See:
Need to expand on
<: Distances.Metric
such that Manifolds.jl can be used to define construction of a BallTree object:The text was updated successfully, but these errors were encountered: