Skip to content

Tricky interface for AffineMap(::LinearMap, ::Vector) #90

Open
@RomeoV

Description

@RomeoV

I just encountered a very tricky silent bug while slinging around AffineMap, LinearMap, Transformation etc.

In summary, I have some 3D rotation matrices stored as LinearMap and tried to combine it with a translation stored as Vector. Spot the error in this code:

using CoordinateTransformations, Rotations
rot = LinearMap(RotY(1/2*pi))
# ...
loc = rand(3)
# ...
pose_map = AffineMap(rot, loc)
# ...
f(pmap::AffineMap) = ...
@test f(pose_map) == test_value # <- compiles just fine, but result completely wrong

It turns out that AffineMap(::LinearMap, ::Vector) actually calls an overloaded function AffineMap(::Transformation, ::Any) and returns an AffineMap just fine, but with AffineMap.v == zeros(3)!

function AffineMap(trans::Transformation, x0)
dT = transform_deriv(trans, x0)
Tx = trans(x0)
AffineMap(dT, Tx - dT*x0)
end

I.e.

# continued
@assert pose_map.linear == rot.linear # true
@assert pose_map.translation == loc # false. instead
@assert pose_map.translation == zeros(length(loc)) # <- 🤯

This is a super tricky bug, as it's completely silent, and occurs from a "misuse" of the interface that is very subtle.

Perhaps it would make sense to rename the function

function AffineMap(trans::Transformation, x0)
    dT = transform_deriv(trans, x0)
    Tx = trans(x0)
    AffineMap(dT, Tx - dT*x0)
end

to something like AffineMapApprox (or something similar), although I realize that would be a breaking change.
Alternatively, we could overload AffineMap(::LinearTransformation, ::Any), e.g. giving a warning like

@warn "AffineMap(rot::LinearTransformation, ::Any) might not do what you want. Try AffineMap(rot.linear, x) instead."

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions