Skip to content

Commit

Permalink
Implemented random walk samplers
Browse files Browse the repository at this point in the history
  • Loading branch information
dwysocki committed Mar 17, 2015
1 parent 6f2d8eb commit c2e4d90
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 6 deletions.
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject hidden-markov-music "0.1.1-SNAPSHOT"
(defproject hidden-markov-music "0.1.1"
:description "Generate original musical scores by means of a hidden Markov
model."
:url "https://github.com/dwysocki/hidden-markov-music"
Expand Down
102 changes: 98 additions & 4 deletions src/hidden_markov_music/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,75 @@
Currently no such interface exists."
(:require [clojure.pprint :refer [pprint]]
[hidden-markov-music.hmm :as hmm]
[hidden-markov-music.stats :as stats]
[clojure.tools.cli :refer [parse-opts]]
[clojure.string :as string])
(:import [hidden_markov_music.hmm HMM])
(:gen-class))

(def random-weather-model
(hmm/random-hmm [:rainy :sunny]
[:run :clean :shop]))

(def song-states
[:beginning :middle :chorus :finale :end])

(def song-notes
[:A :B :C :D :E :F :G])

(def song-model
(HMM. song-states
song-notes
{:beginning {:beginning 0.8,
:middle 0.2,
:chorus 0.0,
:finale 0.0,
:end 0.0},
:middle {:beginning 0.0,
:middle 0.5,
:chorus 0.4,
:finale 0.1,
:end 0.0},
:chorus {:beginning 0.0,
:middle 0.2,
:chorus 0.8,
:finale 0.0,
:end 0.0}
:finale {:beginning 0.0,
:middle 0.0,
:chorus 0.0,
:finale 0.8,
:end 0.2}
:end {:beginning 0.0,
:middle 0.0,
:chorus 0.0,
:finale 0.0,
:end 1.0}}
(stats/random-row-stochastic-map song-states song-notes)
{:beginning 1.0,
:middle 0.0,
:chorus 0.0,
:finale 0.0,
:end 0.0}))

(def models
{"weather" random-weather-model,
"song" song-model})


(def cli-options
[["-h" "--help"]])
[["-m" "--model MODEL" "Name of the model"
:default "weather"
:validate [#{"weather" "song"}
(str "Must be one of [weather, song]")]]
["-n" "--number N" "Number of weather events to sample"
:default 10
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
["-h" "--help"]])

(defn usage [options-summary]
(->> ["Doesn't do anything ... yet."
(->> ["Produces random emissions and states from walking an HMM."
""
"Usage: hidden-markov-music [options]"
""
Expand All @@ -29,5 +89,39 @@

(defn -main
[& args]
(pprint (hmm/random-hmm [:rainy :sunny]
[:run :clean :shop])))
(let [{:keys [options arguments summary errors]}
(parse-opts args cli-options)]
(cond
(:help options)
(exit 0 (usage summary))

(not= (count arguments) 0)
(exit 1 (usage summary))

errors
(exit 1 (error-msg errors)))

(let [model (-> (:model options)
models)
states (hmm/sample-states model)
emissions (hmm/sample-emissions model states)]
(println "MODEL:")
(pprint model)
(println)
(println "SAMPLE:")
(doall
(case (:model options)
"song"
(map (fn [state emission]
(println (str "S = " state ", E = " emission)))
(take-while (fn [state] (not= state :end))
states)
emissions)
"weather"
(take (:number options)
(map (fn [state emission]
(println (str "S = " state ", E = " emission)))
states
emissions))
nil))
nil)))
47 changes: 46 additions & 1 deletion src/hidden_markov_music/hmm.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns hidden-markov-music.hmm
"General implementation of a hidden Markov model, and associated algorithms."
(:require [hidden-markov-music.stats :as stats]))
(:require [hidden-markov-music.stats :as stats]
[hidden-markov-music.random :refer [select-random-key]]))

(defrecord HMM
[states
Expand Down Expand Up @@ -335,3 +336,47 @@
{:likelihood likelihood
;; state sequence was constructed via backtracking, and must be reversed
:state-sequence (reverse optimal-state-sequence)}))

(defn random-initial-state
"Randomly selects an initial state from the model, weighed by the initial
probability distribution."
[model]
(select-random-key (:initial-prob model)))

(defn random-transition
"Randomly selects a state to transition to from the current state, weighed
by the transition probability distribution."
[model state]
(-> model
(get-in [:transition-prob state])
select-random-key))

(defn random-emission
"Randomly emits an observation from the current state, weighed by the
observation probability distribution."
[model state]
(-> model
(get-in [:observation-prob state])
select-random-key))

(defn sample-states
"Randomly walks through the states of the model, returning an infinite lazy
seq of those states.
See [[random-initial-state]] and [[random-transition]] for details on the
decisions made at each step."
[model]
(iterate (partial random-transition model)
(random-initial-state model)))

(defn sample-emissions
"Randomly walks through the states of the model, returning an infinite lazy
seq of emissions from those states. One can optionally provide predetermined
states, and emissions will be made from it randomly.
See [[sample-states]] and [[random-emission]] for details."
([model]
(sample-emissions model (sample-states model)))
([model states]
(map (partial random-emission model)
states)))

0 comments on commit c2e4d90

Please sign in to comment.