Skip to content

Commit

Permalink
Command line interface implemented and polished
Browse files Browse the repository at this point in the history
  • Loading branch information
dwysocki committed May 1, 2015
1 parent 8a17b52 commit d99006d
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 27 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.4-SNAPSHOT"
(defproject hidden-markov-music "0.1.4"
:description "Generate original musical scores by means of a hidden Markov
model."
:url "https://github.com/dwysocki/hidden-markov-music"
Expand Down
5 changes: 4 additions & 1 deletion src/hidden_markov_music/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
"demo" [demo/main "Various demonstrations"]})

(def description
"hidden-markov-music [<options>] <argument> [<args>]")
(->> ["hidden-markov-music [<options>] <argument> [<args>]"
""
"Perform an operation on a musical hidden Markov model."]
(string/join \newline)))

(defn -main
[& args]
Expand Down
23 changes: 20 additions & 3 deletions src/hidden_markov_music/hmm.clj
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@
labels."
[states observations]
{:type :HMM,
:states states,
:states states,
:observations observations,
:initial-prob (stats/random-stochastic-map states),
:transition-prob (stats/random-row-stochastic-map states states),
:initial-prob (stats/random-stochastic-map states),
:transition-prob (stats/random-row-stochastic-map states states),
:observation-prob (stats/random-row-stochastic-map states observations)})

(defn random-LogHMM
Expand All @@ -107,6 +107,23 @@
[states observations]
(HMM->LogHMM (random-HMM states observations)))

(defn uniform-HMM
"Returns an HMM with uniform probabilities, given the state and observation
labels."
[states observations]
{:type :HMM,
:states states
:observations observations
:initial-prob (stats/uniform-stochastic-map states),
:transition-prob (stats/uniform-row-stochastic-map states states),
:observation-prob (stats/uniform-row-stochastic-map states observations)})

(defn uniform-LogHMM
"Returns a logarithmic HMM with uniform probabilities, given the state and
observation labels."
[states observations]
(HMM->LogHMM (uniform-HMM states observations)))

(defn stream->model
"Reads an HMM or LogHMM from an EDN stream, and throws an exception if not
a valid model."
Expand Down
20 changes: 12 additions & 8 deletions src/hidden_markov_music/model/init.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@

(def usage
(util/usage-descriptor
(->> ["Initialize a hidden Markov model with an alphabet read from stdin."
"Alphabet must contain one unique observation symbol per line."
"Mode may either be 'random' or 'uniform', and determines whether"
"the probabilities are initialized randomly or uniformly."
""
"Usage: hidden-markov-music init [<options>] <mode>"]
(->> ["Usage: hidden-markov-music init [<options>] <mode>"
""
"Initialize a hidden Markov model with an alphabet read from stdin."
"Alphabet must contain one unique observation symbol per line."
"Mode may either be 'random' or 'uniform', and determines whether"
"the probabilities are initialized randomly or uniformly."]
(string/join \newline))))

(def cli-options
[["-s" "--states N"
"Number of hidden states"
"Number of hidden states (required)"
:parse-fn util/parse-int
:validate [#(< % 0x10000) "Must be an integer between 0 and 65536"]]
["-h" "--help"]])
Expand All @@ -33,6 +33,9 @@
(not= 1 (count arguments))
(util/exit 1 (usage summary))

(nil? (:states options))
(util/exit 1 (usage summary))

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

Expand All @@ -47,7 +50,8 @@
states (range (:states options))
mode (first arguments)
init-fn (case mode
"random" hmm/random-LogHMM
"random" hmm/random-LogHMM
"uniform" hmm/uniform-LogHMM
(util/exit 1 (str mode " is not a valid mode")))
model (init-fn states alphabet)]
(if (hmm/valid-hmm? model)
Expand Down
8 changes: 4 additions & 4 deletions src/hidden_markov_music/model/sample.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

(def usage
(util/usage-descriptor
(->> ["Takes a model from standard input and writes a sample observation"
"sequence to a file."
""
"Usage: hidden-markov-music sample [<options>] <file>"]
(->> ["Usage: hidden-markov-music sample [<options>] <file>"
""
"Reads a model from standard input and generates a song, and saves it"
"to the given file."]
(string/join \newline))))

(def cli-options
Expand Down
16 changes: 8 additions & 8 deletions src/hidden_markov_music/model/train.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@

(def usage
(util/usage-descriptor
(->> ["Takes a model from standard input and a file containing"
"observations, and writes an updated model to standard output."
"Trains the model using the Baum-Welch algorithm. If the"
"observation file extension is recognized, the file is parsed"
"appropriately, otherwise each line of the file is treated as an"
"observation symbol."
""
"Usage: hidden-markov-music train [<options>] <observations>"]
(->> ["Usage: hidden-markov-music train [<options>] <file>"
""
"Reads a model from standard input, and the given music file."
"Writes an updated model to standard output, as obtained by the"
"Baum-Welch algorithm."
"Tries to recognize the file format from its extension. If format is"
"unsupported, assumes file is a text file where each line contains"
"one observation symbol."]
(string/join \newline))))

(def cli-options
Expand Down
38 changes: 37 additions & 1 deletion src/hidden_markov_music/stats.clj
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@
[size]
(into [] (normalize (repeatedly size rand))))

(defn uniform-stochastic-vector
"Returns a uniformly distributed stochastic vector of the given size."
[size]
(into [] (repeat size (/ 1 size))))

(defn random-stochastic-map
"Returns a hash-map representing a stochastic vector."
"Returns a hash-map representing a randomly distributed stochastic vector."
[keys]
(zipmap keys
(random-stochastic-vector (count keys))))

(defn uniform-stochastic-map
"Returns a hash-map representing a uniformly distributed stochastic vector."
[keys]
(zipmap keys
(uniform-stochastic-vector (count keys))))


(defn random-row-stochastic-map
"Returns a nested hash-map representing a row-stochastic matrix.
The outer key corresponds to a row, and the nested key corresponds to a
Expand All @@ -46,6 +58,30 @@
(let [col-probs (random-stochastic-vector n-cols)]
(zipmap col-keys col-probs)))))

(defn uniform-row-stochastic-map
"Returns a nested hash-map representing a row-stochastic matrix.
The outer key corresponds to a row, and the nested key corresponds to a
column.
For example, take the matrix `X` given by:
```
0 1 2
+-------+
0 | a b c |
1 | d e f |
2 | g h i |
+-------+
```
To obtain the element `d`, one could use `(get-in X [1 0])`, or to obtain the
element `h`, one could use `(get-in X [2 1])`."
[row-keys col-keys]
(let [n-cols (count col-keys)]
(map-for [r row-keys]
(let [col-probs (uniform-stochastic-vector n-cols)]
(zipmap col-keys col-probs)))))

(defn stochastic-map?
"Returns true if the map is stochastic to the given precision."
[m & {:keys [decimal] :or {decimal 10}}]
Expand Down
3 changes: 2 additions & 1 deletion src/hidden_markov_music/util.clj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
(string/join \newline errors)))

(defn exit [status msg]
(println msg)
(binding [*out* *err*]
(println msg))
(System/exit status))

(defn parse-int [x]
Expand Down

0 comments on commit d99006d

Please sign in to comment.