Automating Poetry, Pt. 1


So apparently I really like working on lexical and phonetic analysis.  Who knew?  And apparently I like it to the point that when I finally had a weekend with some spare time to (at long last) play The Witcher 3, I instead found myself sitting at my desk working on an algorithm to split words up by their syllables and vowel sounds.  For hours.  Having fun.

And I guess there’s no reason it shouldn’t be fun.  By the time I was fully into the swing of things (which was surprisingly quickly), it felt like a puzzle.  And, since this was about words in the English language, it was even a puzzle where I was already pretty familiar with all of the pieces.

So, the background:

Without saying too much about the project itself (I’m leaving that to be the researcher’s privilege to announce and document as she likes), we’re brainstorming the early stages of a project at Hamilton that would both analyze a particular type of poetry, and give its readers the chance to create some of their own.

Like most poetry, this means there’s a particular set of rules (which are also a fun puzzle to sort out, programmatically) regarding the form, rhyme, meter, etc., of these works.

My job, to get things started, was therefore to start thinking of ways that we can essentially ask a web application, in real time, to look at either a word or a whole string of words (a line, couplet, etc.), and get some of these bits of information back.

Well, lucky for me, these lexical features are available at least in part through the excellent CMU Pronouncing Dictionary, which can tell you (almost) any English word’s phonetic sounds and emphasis.  And while that doesn’t tell us the number of syllables in the word, or provide rhymes, having the rest of that information actually gets us a lot closer than it might seem.

Setting Up

The first hurdle was making this available to a webpage as something that I could query with reckless abandon.  So, while their page shows a searchable input box (which returns the sort of thing you’d hope for), there was no obvious way to set that kind of searchable system up for yourself.  (And, me being my impatient self, I didn’t ask them for their solution.)

Before I go on, I should also give another positive mention here to Steve Hanov and his “A Rhyming Engine” (now turned into the mightier RhymeBrain, and its API), which were also strong contenders for the tool of choice, regarding the rhyming portion.  (I did reach out to Steve, who kindly responded with the suggestion of trying out that API for my purposes.  I didn’t end up going that route, but that’s just the control freak in me — part of me wanted to figure some of this stuff out for myself, and part of me wanted a tool that I could hammer away at, without API call limitations.)

The CMU Dictionary

The CMU Pronouncing Dictionary (“CMUdict”) is essentially just a gigantic (tab-separated) text list of dictionary words followed by their ARPAbet phonemes and lexical stress markers (represented as numerals at the end of the vowel sounds).  So, while that right there is the bulk of the content I think this task needs, it’s not exactly as accessible as we will need it to be.

So, for my next trick, I simply converted this whole dictionary into the world’s simplest MySQL table, so that I could just query it the old-fashioned way.  (I’d love suggestions of a better way to do this.  I did burn a couple of unsatisfying hours trying other tools I found around the web, to equally unsatisfying ends.)

Disclaimer: I am the furthest thing from a database admin, and am usually quite far behind the times on the easiest or sexiest tools for jobs like these.  I used to be pretty intimidated by that, but at this point I’m finding the value in that — which is using approaches like these, describing them to people such as yourselves, and hearing what tool would make this a thousand times easier, or more powerful, the next time around.  (So, let’s hear them, this time!)  In the meantime, it’s nice to know that at least I can accomplish the task, and probably appreciate the power of better tools all the better for knowing how clunky approaches like these really are.

My process: load this entire dictionary text into a text editor (I’ve been using the surprisingly excellent Visual Studio Code for this project — and all projects on Mac recently), and literally just search/replace the spaces with commas, creating a sort of quick-and-easy CSV (comma separated values) file.

(Fun fact, since the word “NULL” is one of the dictionary words, MySQL hates this on its import, and quits out of the import with an error.  I thought it was funny.  Thus, the manual substitution of “NULL” with “fixme”, which I later, of course, fix.)

On the database side of things, I set up a dead-simple two-column table called `words` that had a column for the word itself, and another for the phonetic/lexical stress value.  That gives us our basic structure that maps to this simple CSV, and from there it’s happy enough (after that “fixme” substitution) to let you load it in via phpMyAdmin’s “import” tool.

This isn’t quite enough by itself.  To make it properly editable, the database still needs a unique-key ID column, which is easy enough to add on after the fact.  (I do this after importing the CSV, so that I don’t have to dream up some annoying solution to manually adding IDs to each field in my text file.)  MySQL is happy enough to add that in one query.

That query being:

So, with that finished, we now have a nice little searchable database that’s happy to let you find either exact matches, or partial matches, with queries such as:

WHERE `w_word` = 'searchterm'

or, for partial matches (with the query syntax):

WHERE `w_word` LIKE '%searchterm%'

(And so forth.)  This also lets us use those ‘%’ wildcards at either only the beginning or only the end, to find words that just begin or end with our search terms.  (That becomes big on searches for rhyme.  More on that later.)

Mercifully, this is probably the biggest single line on the project’s to-do list, sorted out (well enough) in a few steps.  (And, in my mind, I had made that part into quite the dragon to slay, so I was smiling at this point already — which is always nice after only an hour or two.)

From here, it’s easy enough to jot down a few generic queries that will get us most of the search/retrieval functionally we’ll need, and then start stuffing those into a PHP script or three, which we’ll feed words into via $_GET or $_POST variables:

Quick and ugly, but it’s already enough functionality to let us access this from a webpage and see the results.  (And almost enough to soon turn into an Ajax version that we can query in real-time, as often as we need, to look up words as the user types them.)

w00t.  (Which, by the way, is a word that is strangely not in the dictionary.  Weird.)


Leave a Reply