achirkin / easytensor

Many-dimensional type-safe numeric ops
https://hackage.haskell.org/package/easytensor
BSD 3-Clause "New" or "Revised" License
46 stars 2 forks source link

Tutorial / walkthrough please #4

Open joelburget opened 6 years ago

joelburget commented 6 years ago

These libraries (dimensions / easytensor) look useful and interesting. Unfortunately it's not clear how to get started. Some examples / tutorial would be much appreciated!

achirkin commented 6 years ago

Thanks for the comment, work on this is in progress!

In the meanwhile, you can find some rudimentary examples documentation-and-tutorials section of the readme.

Rotaerk commented 6 years ago

Just wanted to say I'm learning a lot reading through the implementation of easytensor. And slowly going crazy... :P

achirkin commented 6 years ago

Ha-ha, sorry :) While I don't have time for a full tutorial at this moment, you may ask here about specific parts in the project and I will quickly haddock more elaborate descriptions.

joelburget commented 6 years ago

Documentation of functions / datatypes is not the problem for me. What I need is one or two simple examples for each project of how it's intended to be used.

Rotaerk commented 6 years ago

One thing I'm unclear on is why you would ever use XN n for any n > 0. What do you get out of statically knowing that that dimension has at least n elements?

For instance, in this snippet, the data frame will have 7 vertices; why specify that it's statically unknown but at least 3? Why not 0, or 7?

achirkin commented 6 years ago

@joelburget yes, I understand that. I will add the tutorial as soon as finish writing my thesis (likely in September , sorry for that)

@Rotaerk In general, XN n is meant for data import. Imagine something like this:

importCsv :: FilePath -> IO (Either String (DataFrame Vertex '[XN 1]))

The function above reads a non-empty data frame or fails. We cannot index DataFrame using Nat here, because we don't know dimensionality in advance. On the other hand, we may want to reject the imported file if it has too few lines. In vulkan-triangles, the constraint comes not from the data, but from the program logic: shader pipeline requires at least 3 vertices to draw at least one triangle primitive. I decided, that would be handy to declare explicitly. Added a comment to the code.

Rotaerk commented 6 years ago

Well, I was going to write up something about having issues with getting XFrame matching to work, but it turns out it was resolved by adding more type annotations to unrelated functions in my code... However, the type errors were rather troublesome to make sense of, and I only accidentally got it working.

In the commit https://github.com/Rotaerk/vulkanTest/commit/033701bc307a7e06fbc04f086b1ff0eada30aa58, I wanted to move the XFrame matching outside of the createVertexBuffer and fillVertexbufferMemory functions. So I moved it up into the beginning of main, and indented the entire runResourceT block to be inside of it.

Problems I encountered while working on this change:

  1. It took me some work to figure out what type to give the revised fillVertexBufferMemory function. I initially tried DataFrame Vertex (ns :: [Nat]), but that gave me this error:

    src/Main.hs:723:70: error:
       • Overlapping instances for PrimBytes
                                     (Numeric.DataFrame.Internal.Array.Family.Array Vertex ns)
           arising from a use of ‘bSizeOf’
         Matching instances:
           instance [overlappable] (VulkanMarshalPrim a, Storable a) =>
                                   PrimBytes a
             -- Defined in ‘Graphics.Vulkan.Marshal.Create.DataFrame’
           instance (PrimBytes a, PrimBytes b) => PrimBytes (Either a b)
             -- Defined in ‘Numeric.PrimBytes’
           instance PrimBytes Int16 -- Defined in ‘Numeric.PrimBytes’
           ...plus 15 others
           ...plus 13 instances involving out-of-scope types
           (use -fprint-potential-instances to see them all)
         (The choice depends on the instantiation of ‘ns’
          To pick the first instance above, use IncoherentInstances
          when compiling the other instance declarations)
       • In the second argument of ‘($)’, namely ‘bSizeOf vertices’
         In the fourth argument of ‘mappedMemory’, namely
           ‘(fromIntegral $ bSizeOf vertices)’
         In the first argument of ‘with’, namely
           ‘(mappedMemory
               device vertexBufferMemory 0 (fromIntegral $ bSizeOf vertices))’
       |
    723 |       with (mappedMemory device vertexBufferMemory 0 (fromIntegral $ bSizeOf vertices)) $ \ptr ->
       |                                                                      ^^^^^^^^^^^^^^^^

    I'm still not really sure how I would give it a DataFrame type, but I was able to get it to work with the more generic type that it has now.

  2. In addition to making the obvious code changes, I also had to add type annotations to maxFramesInFlight and to evalStateTWith. Without them, I got some pretty strange type errors. It seems odd that this would be suddenly required just because these things are used inside of the XFrame case.

achirkin commented 6 years ago

Looks like I messed up with instances of PrimBytes. I have created an issue #9 to improve the situation with that.

In general, whenever you encounter Overlapping instances error, you should first check if there is any instance in scope that satisfies the class constraint. In your case, GHC does not know which instance of Array type family is used inside the DataFrame and, thus, cannot find a suitable type class instance for PrimBytes. Thus, you need to provide the instances explicitly. The XNat-indexed DataFrame carries only a few instances for a Nat-indexed DataFrame behind its existential constructor. If you need any more instance in the scope of pattern match, you can use inference functions, e.g.

case xdf of
  XFrame df
    | E <- inferPrim df -> do
       ... -- df has an instance of PrimBytes here

Most commonly required inference functions are given by DataFrameInference class.

As for the second problem, cannot really say right now, without trying it myself.