diagrams / diagrams-core

Core types and utilities
https://diagrams.github.io/
Other
94 stars 43 forks source link

Better handling of singular transformations #33

Open byorgey opened 11 years ago

byorgey commented 11 years ago

The impetus for this ticket is the unfortunate code

scale :: (Transformable t, Fractional (Scalar (V t)), Eq (Scalar (V t)))
      => Scalar (V t) -> t -> t
scale 0 = error "scale by zero! Halp!" -- XXX what should be done here?

(currently found here: https://github.com/diagrams/diagrams-core/blob/master/src/Diagrams/Core/Transform.hs#L286).

What should be done, indeed?

One idea (from @mgsloan, in a comment here), is to add a tzero method to Transformable, like

class Transformable t where
  ...
  tzero :: t

where tzero is defined to be the result of applying a singular transformation.

It also occurs to me we might be able to move this even closer to the source by having a special representation for singular transformations. Then tzero is implicit in the fact that Transformable instances have to decide what to do about singular transformations.

One important thing to note is that not all singular transformations are equal. (To be clear, by "singular" I mean "linear transformation whose corresponding matrix is non-invertible".) For example, scale 0 and scaleX 0 are both singular, but they are not the same and it may be useful to distinguish them. For example, scaleX 0 can usefully be applied to any Path R2 (resulting in the "projection" of the path onto the y-axis). But scale 0 and scaleX 0 have the same result on Diagrams (that is, currently, bottom; but ideally, mempty).

mgsloan commented 11 years ago

I think that having a special representation for singular transformations would be best. Not sure what this would look like given the current representation of Transformation.

It's a bit tricky, because you end up wanting to use linear equations as the output of the inverse of your linear transformation.. (e.g. for scaleX 0, you end up with a bunch of horizontal lines when asking for the). The domain of the inverse is weird too, since it ought to be restricted to the codomain of the forward transformation..

Writing this stuff in a way that doesn't cut corners, and is mostly statically checked, while maintaining usability, could be quite a challenge.. Interesting problem, though!

byorgey commented 11 years ago

Well, I guess I was thinking more along the lines that a singular transformation simply doesn't have an inverse. Whether this would be enforced statically or dynamically is still an interesting question.

mgsloan commented 11 years ago

The main things that would break are the Transformable instances for Query / Envelope / Trace.

Random idea:

data (:-:) u v = (u :-* (u, v)) :-: (v :-* (v, u))

-- Used to be
data (:-:) u v = (u :-* v) :-: (v :-* u)

What're those extra tupled values? They let you know what actual value the input was mapped to, in order to be within the domain. So, apply (inv $ scalingY 2 <> scalingX 0) (1 & 2) would result in ((0, 2), (0, 1)). This lets you know about the singularness, while giving decent default behavior for the "actual" result.

byorgey commented 11 years ago

Hmm... maybe I'm just being dense but I don't think I quite understand. What do the output tuples represent, exactly? And can you give some simpler examples, e.g. what would be the result of apply (scalingX 0) (1 & 2)?

mgsloan commented 11 years ago

apply (scalingX 0) (1 & 2) would be ((0, 2), (0, 2)). Since it's singular in the same way as the previous example, the "domain" vector is the same.

You're not being dense, I'm just being terse and vague. I haven't actually worked out whether these linear maps would function properly, but there are instances for tuples. Intuitively it seems like it has good chances of working.

The idea is that you should be able to query the inverse of a singular matrix if you have a default way of removing the singularity. In this case that's projecting on to the span of the matrix. So, instead it's a "pseudo-inverse", maybe even this one:

Moore Penrose pseudoinverse

mgsloan commented 11 years ago

Also, need to figure out what this means for the otherwise broken Transformable instances..

byorgey commented 11 years ago

I am not following this at all. But it doesn't help that I am sick. I will come back and think about it more carefully once my head doesn't feel like it's full of cotton. =)

Mathnerd314 commented 10 years ago

I'm wondering if Deform solves the use cases for scaling by 0. If so, we can just document that scale and friends are only defined on non-zero values.

bergey commented 10 years ago

Debugging the performance of deform is on my list. I agree that we should document somewhere that deform parallelX0 is the type-correct alternative to scaleX 0, &c.

byorgey commented 10 years ago

Drat, I always get confused and think the "close" button means "close this comment box".