Repository navigation
Conversation
ValerianRey
left a comment
There was a problem hiding this comment.
Very cool thanks! We still need to solve the complexity problem of the InvAffine.map. It's not trivial. I can help with that if you need.
| // Dropping the middle axis is affine too, and sends several indices onto each | ||
| // one, which the inverse finds all of. | ||
| #[test] | ||
| fn an_inv_affine_of_a_projection_matches_the_inv_projection() { | ||
| let inv_affine = InvAffine::new(Affine::new( | ||
| HRect::new(vec![2, 3, 4]), | ||
| vec![vec![1, 0, 0], vec![0, 0, 1]], | ||
| vec![0, 0], | ||
| HRect::new(vec![2, 4]), | ||
| )); | ||
| let inv_proj = InvProjection::new(HRect::new(vec![2, 3, 4]), 1); | ||
|
|
||
| for m in 0..2 { | ||
| for p in 0..4 { | ||
| assert_eq!(inv_affine.map(&[m, p]), inv_proj.map(&[m, p])); | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Nice, I think this is a compilation example btw
| fn map(&self, index: &[usize]) -> Powerset { | ||
| // A scan of the codomain, which holds for any basis, whether several | ||
| // indices land on `index` or none do. | ||
| indices(self.signature.codomain().shape()) | ||
| .into_iter() | ||
| .filter(|x| HRectMapping::map(&self.affine, x) == index) | ||
| .collect() | ||
| } |
There was a problem hiding this comment.
I think this is not efficient enough. The complexity is O(k) where k is the number of elements of the codomain (which is typically much smaller than the domain, but still too large to make it ok).
I think we can change that into something that grows like O(m * n), where m is the number of domain dimensions and n is the number of codomain dimensions.
First, let's think of the simple case where f is the InvAffine mapping with m=n=1, with offset 0 and basis [[3]].
For example, the physical tensor would be P = [1 2 3 4 5] and the virtual tensor would be V = [1 0 0 2 0 0 3 0 0 4 0 0 5]
We can see that f is such that:
f(0) = {0}
f(1) = {}
f(2) = {}
f(3) = {1}
...
f(12) = {4}
More generally, we have f(i) = {i / 3} if i % 3 == 0, else {}
Note that things are a bit more complex when the shape is so that we stop using elements from the physical after some point (e.g. [1 0 0 2 0 0 3 0 0 4 0 0 5 0 0 0 0 0 ... 0]). We'd have to add a condition depending on the shape to handle those cases.
So we can see that for the base case m=n=1, this mapping can be implemented very efficiently (O(m * n) = O(1)).
Now the question is how to generalize this to arbitrary m and n?
Let's take now the example of a sparse "diagonal" matrix, with physical P=[1 2 3] and virtual V =
[[1 0 0 0 0 0 0]
[0 0 0 0 0 0 0]
[0 0 0 2 0 0 0]
[0 0 0 0 0 0 0]
[0 0 0 0 0 0 3]]
This corresponds to an InvMapping with m=2 and n=1 (still one phyiscal dimension but 2 virtual dimensions). The basis would be
[[2]
[3]]
i.e. when moving by 1 in the physical we move by 2 rows in the virtual and 3 columns in the virtual.
Here, the mapping would be:
f(0, 0) = {0}
f(2, 3) = {1}
f(4, 6) = {2}
f(x, y) = {} in all other cases
this can be translated into
f(x, y) = {x / 2} if 2y == 3x, else {}
See that the values 2 and 3 come from the basis.
Let's try to go in this direction and generalize even more (handle n > 1, handle offset, handle shape), while keeping a very efficient algorithm for this.
There was a problem hiding this comment.
I think when the basis columns are independent (like in both your examples, diagonal, padding etc) we can just solve it directly, so each read is only a few divisions and checks.
The harder case is when there are more physical dims than that, like repeat or block-diagonal. There the shape bounds decide which x is the right one. My idea was to solve what we can and only loop over the leftover dims, which is like 2 or 3 steps in those examples.
What do u think? Do u have any other ideas for that case? I Would be happy to get ur help on this.
# Conflicts: # tests/logic.rs # tests/mapping.rs
Closes #14: adds
Affine(physical to virtual,basis·x + offset) and its inverseInvAffine, which holds theAffineit inverts.A sparse latticed tensor is a
SumoverInvAffine, so it reads 0 off the lattice, andsparse_latticed_tensor(physical, basis, offset, shape)builds one.The examples from the subclass_zoo issue linked in #14 are added as tests.