Closed lancelet closed 4 years ago
Update: I think I've figured out what's going on. It does look like a floating-point issue of some kind:
First, I replaced escapeTime
with a version that returned the complex value contained in the fold
state:
escapeTime : Int -> Complex -> (Real & Complex) =
\j c. fold (0.0, czero) $ \i:(Fin j) (n, z).
z' = update c z
(n + b2r (inBounds z'), z')
This reveals that as NaN
s are introduced, the function starts increasing the escape time:
:p escapeTime 1 pt
> (1.0, (1.0, 0.0))
:p escapeTime 2 pt
> (1.0, (2.0, 0.0))
:p escapeTime 3 pt
> (1.0, (5.0, 0.0))
:p escapeTime 4 pt
> (1.0, (26.0, 0.0))
:p escapeTime 5 pt
> (1.0, (677.0, 0.0))
:p escapeTime 6 pt
> (1.0, (458330.0, 0.0))
:p escapeTime 7 pt
> (1.0, (2.1006639e11, 0.0))
:p escapeTime 8 pt
> (1.0, (4.4127886e22, 0.0))
:p escapeTime 9 pt
> (1.0, (Infinity, 0.0))
:p escapeTime 10 pt
> (1.0, (Infinity, 0.0))
:p escapeTime 11 pt
> (1.0, (Infinity, 0.0))
:p escapeTime 12 pt
> (1.0, (Infinity, 0.0))
:p escapeTime 13 pt
> (2.0, (Infinity, NaN))
:p escapeTime 14 pt
> (3.0, (NaN, NaN))
:p escapeTime 15 pt
> (4.0, (NaN, NaN))
(Side note: interestingly, here Infinity
is clearly not behaving as a singleton value, because the escapeTime
function returns different values for Infinity
on different evaluations. Does Infinity
have some kind of underlying representation that is actually participating in the calculations here? - I'll have to look that up!)
And indeed the NaN
s are interacting with <
in the inBounds
function in all kinds of hilarious ways:
>=> infty = 1./0.
>=> infty
Infinity
>=> nan = 0./0.
>=> nan
NaN
>=> infty < 2.0
False
>=> nan < 2.0
True
which explains why the escape time starts increasing.
I'm not really sure how to handle this though, or why the behavior changed. I'll take a look at what was happening when it was still working correctly.
Good digging! I'm afk right now and can't check, but my guess is that it's from the dictionary synthesis stuff, where we put Ord in userspace. I (sloppily) made the Ord typeclass dictionary just a single >
function, with <
defined, in the prelude, using >
and ==
. Not a good thing to do for NaNs! Replacing <
with <=
might fix the example, but we should really do Ord properly.
Thanks for the confirmation. I expanded Ord
a bit in PR #122.
I'm not 100% sure if that's what you had in mind. I've noticed there have been some developments toward sum types. Do you have any plans for labelled products / records / (I'm not 100% sure what to call them - like data
in Haskell 😄) that could replace the current tupling approach for type classes?
Thanks for the PR!
Do you have any plans for labelled products / records
Yes, definitely. Building everything out of nested pairs and accessing them with compositions of fst
and snd
gets old quickly. One of the goals with Dex is to explore typed relational/dataframe programming, and labeled fields will be essential there. The plan is to have some sort of extensible record system. I really like Daan Leijen's take on row polymorphism so I think we'll start there. Any ideas or suggestions would very welcome.
Somewhere between 9877efb233e7fc37db8cc516e1dbcf91eade049b and fd2c8acd70c28c6189fd92a1d7373bf0a0c57d4a, the
mandelbrot.dx
demo became broken somehow. It currently looks like this for me: If I reduce the number of iterations to 50, it starts looking more correct:Unfortunately quite a lot changed between the last commit where it worked and the first commit where it failed, so I haven't been able to track down what's causing the problem. Frustratingly, the individual operations in the example all seem to work as expected. I've been wondering if there's some small floating-point error or similar that might be causing it.
btw: It might be convenient to validate this example's output in the tests if, after displaying the 300x200 matrix, it was evaluated over a smaller number of elements (say 7x5) and then dumped using
:p
.