When the unknown is unknown

Hello all, I am currently playing with Paul Hudak's Euterpea (a music program, formely called Haskore) and I am trying to teach it about rhythm. I said that a rhythm is a series of Moments (or Beats), each expressed as fractions of a bar. But each Moment also has volume. So I could model rhythm as Pairs of (Moment, Volume). However I certanly do not want to specify both the Moments and the Volume, but rather compute one from the other. Often the Moments are known and I need to compute the Volumes, but this is not always the case. I might as well start with the volume and compute the moments. The latter would be particularly interesting when trying to find rhythms which are suitable for certain lyrics. In that case I must even be prepared to find more than one series-of-moments which "fit" to the given series-of-volumes. There are countless other problems like this, e.g. when trying to match harmony, melody and tension. In that case I even have three variables and I may want to specify tension first, then melody and have the harmony computed. At first glance this looks like a Prolog-like problem to me. I could say that certain things are always true for [(Moment, Volume)] and let an inference engine figure out the options which are still in consideration. From which angle would you approach problems like this? Should I get my hands on a prolog-in-haskel implementation (which indeed seems to exist)? Or should I roll my own poor-man's prolog? Or is this a constraint-satisfaction-problem? Or is there even a more straight-forward more haskellish pattern, which solves such problems? Any pointers would be much appreciated. -- Martin

On Jun 23, 2010, at 1:50 PM, Martin Drautzburg wrote:
I said that a rhythm is a series of Moments (or Beats), each expressed as fractions of a bar. But each Moment also has volume. So I could model rhythm as Pairs of (Moment, Volume). However I certanly do not want to specify both the Moments and the Volume, but rather compute one from the other.
How about something like: type RhythmScheme = [(Maybe Moment, Maybe Volume)] type Rhythm = [(Moment, Volume)] -- The resolution function will then be a function with type: rhythm_from_scheme :: RhythmScheme -> Rhythm -- Though you might want something like -- rhythm_from_scheme :: RhythmScheme -> IO Rhythm -- or -- rhythm_from_scheme :: Seed -> RhythmScheme -> Rhythm -- so that you can get and use random numbers, for example. I guess the point of my suggestion is to let pattern matching in function definitions deal with unification of constraints. Beta reduction and unification are two sides of a coin.

On Thursday, 24. June 2010 00:04:18 Alexander Solla wrote:
On Jun 23, 2010, at 1:50 PM, Martin Drautzburg wrote:
I said that a rhythm is a series of Moments (or Beats), each expressed as fractions of a bar. But each Moment also has volume. So I could model rhythm as Pairs of (Moment, Volume). However I certanly do not want to specify both the Moments and the Volume, but rather compute one from the other.
How about something like:
type RhythmScheme = [(Maybe Moment, Maybe Volume)] type Rhythm = [(Moment, Volume)]
-- The resolution function will then be a function with type:
rhythm_from_scheme :: RhythmScheme -> Rhythm
-- Though you might want something like -- rhythm_from_scheme :: RhythmScheme -> IO Rhythm -- or -- rhythm_from_scheme :: Seed -> RhythmScheme -> Rhythm -- so that you can get and use random numbers, for example.
I guess the point of my suggestion is to let pattern matching in function definitions deal with unification of constraints. Beta reduction and unification are two sides of a coin.
Nice. But what if I have three or more values (and not just two). Then inside the rhythm_from_scheme function I will probably have functions like a->b->c, i.e. if two values are known I can compute the third. If only one value is known the result would be a partially applied function and I would need additional information to compute the result. So I will need more than just a Mabye, because I can either have Nothing, a value or a function. However I will need one such function for each permutation. The function a->b->c will not help me much if either b or c is known. This means I cannot stuff too many unknowns together, but I will have to layer the problem somehow, such that a 9tuple of unknowns is unified as three triples. I am still uncertain how to do this, but I believe it basically matches musical thinking. A composer cannot juggle 9 unknowns either without grouping them somehow. Another question is: how much past and future knowledge do I need. (I believe the fundamental property of music is that things are ordered). In order to compute Volumes from Moments I can get pretty much away without the past, but computing Moments from Volumes definitely requires knowing "where I am", because each new Moment has to be placed after a preceding Moment. Any ideas? -- Martin

On Jun 24, 2010, at 11:14 AM, Martin Drautzburg wrote:
Another question is: how much past and future knowledge do I need. (I believe the fundamental property of music is that things are ordered). In order to compute Volumes from Moments I can get pretty much away without the past, but computing Moments from Volumes definitely requires knowing "where I am", because each new Moment has to be placed after a preceding Moment.
You can use pattern matching against lists. For example: process_rhythm_scheme :: RhythmScheme -> Rhythm process_rhythm_scheme ( (Just foo, Just bar ) : (Just foo', Just bar') : (Just foo'', Just bar'') : rest ) = undefined will match a RhythmScheme where the first three entries are wholly defined, and will bind its values to foo, bar, foo', bar', rest, and so on. I think "View Patterns" could help control the complexity of these patterns. http://hackage.haskell.org/trac/ghc/wiki/ViewPatterns. That page has quite a few nice constructs that could apply to your problem. ("Both patterns", "iterator style")

Martin Drautzburg wrote:
From which angle would you approach problems like this? Should I get my hands on a prolog-in-haskel implementation (which indeed seems to exist)? Or should I roll my own poor-man's prolog? Or is this a constraint-satisfaction-problem? Or is there even a more straight-forward more haskellish pattern, which solves such problems?
I don't think that a general Prolog implementation is the right approach, but if you do need one, have a look at the demonstration projects for the Hugs interpreter: http://darcs.haskell.org/hugs98/demos/prolog/ Regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com
participants (3)
-
Alexander Solla
-
Heinrich Apfelmus
-
Martin Drautzburg