Search code examples
javagenericshashmappolymorphismhidden-markov-models

Layered HashMap Nth Order HMM implementation


I have implemented 1st, 2nd and 3rd order Hidden Markov Models using HashMaps as opposed to a transition matrix. I use these HMMs to count the number of occurrences of musical notes (modeled as integers 0-128) after 1 note/ 2 notes/ 3 notes, depending on the order.

For example the implementation for the 2nd order is:

public void updateWeigths(ArrayList<Note> notes, HashMap<Integer, HashMap<Integer, HashMap<Integer, Double>>> hm) {
    for (int i=0; i<notes.size()-2; i++) {
        int prevPitch1 = notes.get(i).getPitch();
        int prevPitch2 = notes.get(i+1).getPitch();
        int nextPitch = notes.get(i+2).getPitch();
        if (prevPitch1 > 0 && prevPitch2 > 0 && nextPitch > 0) {
            if (hm.containsKey(prevPitch1)) {
                HashMap<Integer, HashMap<Integer, Double>> nextMapping1 = hm.get(prevPitch1);
                if (nextMapping1.containsKey(prevPitch2)){
                    HashMap<Integer, Double> nextMapping2 = nextMapping1.get(prevPitch2);
                    if (nextMapping2.containsKey(nextPitch)) {
                        double prob = nextMapping2.get(nextPitch);
                        nextMapping2.put(nextPitch, prob+1);
                    }
                    else {
                        nextMapping2.put(nextPitch, 1.0);
                    }
                }
                else {
                    nextMapping1.put(prevPitch2, new HashMap<Integer, Double>());
                }
            }
            else {
                hm.put(prevPitch1, new HashMap<Integer,HashMap<Integer,Double>>());
            }
        }
    }
}

I want to implement an arbitrary order HMM using the same pattern. I tried using polymorphism but I get ClassCastException each time. Not entirely sure how to use Generics on this. The trick I guess is to know when you're on the last HashMap so you can update the count Double value.

Any suggestions would be great!


Solution

  • I managed to solve the problem using Object inheritance and recursion. The weights are now updated by iterating through the notes from the learning data and calling this function on every note.

    To the function you pass in a HashMap<HashMap<Integer, Object> instance which is the data structure that contains the transition probabilities, the order of the HMM and a note index from the array of learning notes.

    public void updateTransitionProb(Object mapping, int ord, int noteIndex)  {
        int note = notesList.get(noteIndex).getPitch();
        HashMap<Integer, Object> hm = (HashMap<Integer, Object>) mapping;
    
        if (ord == 0) {
            hm.put(note, (hm.get(note) != null) ? ((Double) hm.get(note)) + 1.0 : new Double(1.0));
        }
        else {
            if (hm.containsKey(note)) {
                this.updateTransitionProb(hm.get(note), --ord, ++noteIndex);
            }
            else {
                hm.put(note, new HashMap<Integer, Object>());
                this.updateTransitionProb(hm.get(note), --ord, ++noteIndex);
            }
        }
    }