Beispiel #1
0
class HMMEditor(SAGraphEditor):
    def __init__(self, master=None):
        SAGraphEditor.__init__(self, master)
        self.G = Graph()
        self.G.directed = 1
        self.G.euclidian = 0
        self.G.simple = 0
        self.HMM = HMM(self, self.G)

    def CreateWidgets(self):

        toolbar = Frame(self, cursor='hand2', relief=FLAT)
        toolbar.pack(side=LEFT, fill=Y)  # Allows horizontal growth

        extra = Frame(toolbar, cursor='hand2', relief=SUNKEN, borderwidth=2)
        extra.pack(side=TOP)  # Allows horizontal growth
        extra.rowconfigure(6, weight=1)
        extra.bind("<Enter>", lambda e, gd=self: gd.DefaultInfo())

        px = 0
        py = 3

        self.toolVar = StringVar()

        import GatoIcons
        # Load Icons
        self.vertexIcon = PhotoImage(data=GatoIcons.vertex)
        self.edgeIcon = PhotoImage(data=GatoIcons.edge)
        self.deleteIcon = PhotoImage(data=GatoIcons.delete)
        self.swapIcon = PhotoImage(data=GatoIcons.swap)
        self.editIcon = PhotoImage(data=GatoIcons.edit)
        self.propIcon = PhotoImage(data=GatoIcons.edit)

        b = Radiobutton(extra,
                        width=32,
                        padx=px,
                        pady=py,
                        text='Add or move vertex',
                        command=self.ChangeTool,
                        var=self.toolVar,
                        value='AddOrMoveVertex',
                        indicator=0,
                        image=self.vertexIcon)
        b.grid(row=0, column=0, padx=2, pady=2)
        b.bind("<Enter>",
               lambda e, gd=self: gd.UpdateInfo('Add or move vertex'))
        self.defaultButton = b  # default doesnt work as config option

        b = Radiobutton(extra,
                        width=32,
                        padx=px,
                        pady=py,
                        text='Add edge',
                        command=self.ChangeTool,
                        var=self.toolVar,
                        value='AddEdge',
                        indicator=0,
                        image=self.edgeIcon)
        b.grid(row=1, column=0, padx=2, pady=2)
        b.bind("<Enter>", lambda e, gd=self: gd.UpdateInfo('Add edge'))

        b = Radiobutton(extra,
                        width=32,
                        padx=px,
                        pady=py,
                        text='Delete edge or vertex',
                        command=self.ChangeTool,
                        var=self.toolVar,
                        value='DeleteEdgeOrVertex',
                        indicator=0,
                        image=self.deleteIcon)
        b.grid(row=2, column=0, padx=2, pady=2)
        b.bind("<Enter>",
               lambda e, gd=self: gd.UpdateInfo('Delete edge or vertex'))

        b = Radiobutton(extra,
                        width=32,
                        padx=px,
                        pady=py,
                        text='Swap orientation',
                        command=self.ChangeTool,
                        var=self.toolVar,
                        value='SwapOrientation',
                        indicator=0,
                        image=self.swapIcon)
        b.grid(row=3, column=0, padx=2, pady=2)
        b.bind("<Enter>", lambda e, gd=self: gd.UpdateInfo('Swap orientation'))

        b = Radiobutton(extra,
                        width=32,
                        padx=px,
                        pady=py,
                        text='Edit Weight',
                        command=self.ChangeTool,
                        var=self.toolVar,
                        value='EditWeight',
                        indicator=0,
                        image=self.editIcon)
        b.grid(row=4, column=0, padx=2, pady=2)
        b.bind("<Enter>", lambda e, gd=self: gd.UpdateInfo('Edit Weight'))

        b = Radiobutton(extra,
                        width=32,
                        padx=px,
                        pady=py,
                        text='Edit Properties',
                        command=self.ChangeTool,
                        var=self.toolVar,
                        value='EditProperties',
                        indicator=0,
                        image=self.editIcon)
        b.grid(row=5, column=0, padx=2, pady=2)
        b.bind("<Enter>", lambda e, gd=self: gd.UpdateInfo('Edit Properties'))

        # disable the EditProperties button
        ##        b = Radiobutton(extra, width=32, padx=px, pady=py,
        ##                        text='Edit State',
        ##                        command=self.ChangeTool,
        ##                        var = self.toolVar, value='EditState', indicator=0,
        ##                       image=self.editIcon)
        ##        b.grid(row=6, column=0, padx=2, pady=2)
        ##        b.bind("<Enter>", lambda e, gd=self:gd.UpdateInfo('Edit State'))

        GraphEditor.CreateWidgets(self)

    #----- Tools Menu callbacks
    def ChangeTool(self):
        self.SetEditMode(self.toolVar.get())

    def MouseUp(self, event):
        if self.mode == 'AddOrMoveVertex':
            self.AddOrMoveVertexUp(event)
        elif self.mode == 'AddEdge':
            self.AddEdgeUp(event)
        elif self.mode == 'DeleteEdgeOrVertex':
            self.DeleteEdgeOrVertexUp(event)
        elif self.mode == 'SwapOrientation':
            self.SwapOrientationUp(event)
        elif self.mode == 'EditWeight':
            self.EditWeightUp(event)
        elif self.mode == 'EditProperties':
            self.EditPropertiesUp(event)
        elif self.mode == 'EditState':  # test EditObj
            self.EditHMMObj(event)

    def makeMenuBar(self):
        self.menubar = Menu(self, tearoff=0)

        # Add file menu
        self.fileMenu = Menu(self.menubar, tearoff=0)
        self.fileMenu.add_command(label='New', command=self.NewGraph)
        self.fileMenu.add_command(label='Open ...', command=self.OpenGraph)
        self.fileMenu.add_command(label='Save', command=self.SaveGraph)
        self.fileMenu.add_command(label='Save as ...',
                                  command=self.SaveAsGraph)
        self.fileMenu.add_separator()
        self.fileMenu.add_command(label='Export EPSF...',
                                  command=self.ExportEPSF)
        self.fileMenu.add_separator()
        self.fileMenu.add_command(label='Quit', command=self.Quit)
        self.menubar.add_cascade(label="File", menu=self.fileMenu, underline=0)

        self.graphMenu = Menu(self.menubar, tearoff=0)
        self.graphMenu.add_command(label='Edit HMM', command=self.EditHMM)
        self.graphMenu.add_command(label='Edit Class label',
                                   command=self.EditClassLabel)
        # XXX Note if we change alphabet, we have to change all emissions
        self.graphMenu.add_command(label='Edit Alphabet',
                                   command=self.EditAlphabet)
        self.graphMenu.add_command(label='Edit Prior', command=self.EditPrior)
        self.graphMenu.add_command(label='Edit Background Distributions',
                                   command=self.EditBackgroundDistributions)
        self.graphMenu.add_separator()
        self.graphMenu.add_checkbutton(label='Grid',
                                       command=self.ToggleGridding)
        self.menubar.add_cascade(label="HMM", menu=self.graphMenu, underline=0)

        self.master.configure(menu=self.menubar)

    def SetGraphMenuOptions(self):
        if not self.gridding:
            self.graphMenu.invoke(self.graphMenu.index('Grid'))

    ############################################################
    #
    # Menu Commands
    #
    # The menu commands are passed as call back parameters to
    # the menu items.
    #
    def NewGraph(self, nrOfSymbols=0, newInput=1):

        self.DeleteDrawItems()  # clear screen
        if self.hasGraph == 1:
            self.G.Clear()
            self.HMM.G.Clear()
            self.hasGraph = 0

        self.HMM = HMM(self, self.G)

        self.graphName = "New"
        self.ShowGraph(self.HMM.G, self.graphName)
        self.RegisterGraphInformer(HMMInformer(self.HMM))
        self.fileName = None
        self.SetTitle("HMMEd _VERSION_ - New Graph")
        self.SetGraphMenuOptions()
        if newInput:
            if nrOfSymbols == 0:
                nrOfSymbols = tkSimpleDialog.askinteger(
                    "New HMM", "Enter the number of output symbols")
                for i in xrange(nrOfSymbols):
                    self.HMM.G.vertexWeights[i] = VertexWeight(0.0)

                self.HMM.hmmAlphabet = DiscreteHMMAlphabet(nrOfSymbols)

    def OpenGraph(self):

        self.DeleteDrawItems()  # clear screen
        if self.hasGraph == 1:
            self.G.Clear()
            self.HMM.G.Clear()
            self.HMM.Clear()
            self.hasGraph = 0

        file = askopenfilename(title="Open HMM",
                               defaultextension=".xml",
                               filetypes=(("XML", ".xml"), ))
        if file is "":
            print "cancelled"
        else:
            self.fileName = file
            self.graphName = stripPath(file)
            e = extension(file)

            if e == 'xml':
                self.HMM.OpenXML(file)
            else:
                print "Unknown extension"
                return

        #self.HMM.hmmAlphabet ?
        #self.HMM.hmmClass    ?

            self.ShowGraph(self.HMM.G, self.graphName)
            self.RegisterGraphInformer(HMMInformer(self.HMM))
            self.SetTitle("HMMEd _VERSION_ - " + self.graphName)

            if not self.gridding:
                self.graphMenu.invoke(self.graphMenu.index('Grid'))

    def SaveGraph(self):
        if self.fileName != None:
            self.HMM.SaveAs(self.fileName)
        else:
            self.SaveAsGraph()

    def SaveAsGraph(self):
        file = asksaveasfilename(title="Save HMM",
                                 defaultextension=".xml",
                                 filetypes=(
                                     ("XML", ".xml"),
                                     ("GHMM", ".ghmm"),
                                 ))
        if file is "":
            print "cancelled"
        else:
            print file
            ext = string.lower(os.path.splitext(file)[1])
            if ext == '.xml':
                self.fileName = file
                self.HMM.SaveAs(file)
                self.graphName = stripPath(file)
                self.SetTitle("HMMEd _VERSION_ - " + self.graphName)
            else:
                self.fileName = file
                self.HMM.SaveAsGHMM(file)
                self.graphName = stripPath(file)
                self.SetTitle("HMMEd _VERSION_ - " + self.graphName)

    def EditWeightUp(self, event):
        """ Need to have editors multiple sets of transition probability """
        if event.widget.find_withtag(CURRENT):
            widget = event.widget.find_withtag(CURRENT)[0]
            tags = self.canvas.gettags(widget)
            if "edges" in tags:
                (tail, head) = self.edge[widget]

                transition_probabilities = ProbEditorBasics.ProbDict({})
                for head in self.HMM.G.OutNeighbors(tail):
                    weight = self.HMM.G.edgeWeights[0][(tail, head)]
                    label = "-> %d" % head
                    transition_probabilities.update({label: weight})

                if transition_probabilities.sum == 0:
                    key_list = transition_probabilities.keys()
                    for key in key_list:
                        transition_probabilities[key] = 1.0 / len(key_list)
                e = ProbEditorBasics.emission_data(transition_probabilities)
                d = ProbEditorDialogs.emission_dialog(
                    self, e, "transition probs from state %d" % tail)
                if d.success():
                    # write back normalized probabilities
                    for key in transition_probabilities.keys():
                        head = int(key[3:])
                        self.HMM.G.edgeWeights[0][(
                            tail, head)] = transition_probabilities[
                                key] / transition_probabilities.sum

            else:
                # We have a vertex
                v = self.FindVertex(event)
                if v != None:
                    state = self.HMM.state[v]
                    if state.order > 0:
                        print "Ooops. Cant edit higher order states"
                        return

                    if state.tiedto != '':
                        msg = "The emission parameters of state %s you attempted to edit are tied to those of state %s." % (
                            state.id, state.tiedto)
                        #print "Note:", msg
                        if not askokcancel(
                                "Edit Tied State",
                                msg + "Edit those of state %s instead?" %
                                state.tiedto):
                            return
                        else:
                            state = self.HMM.state[state.tiedto]

                    if state.emissions == []:
                        state.emissions = [1.0 / self.HMM.hmmAlphabet.size()
                                           ] * self.HMM.hmmAlphabet.size()
                    emission_probabilities = ProbEditorBasics.ProbDict({})

                    for code in self.HMM.hmmAlphabet.name.keys():
                        label = self.HMM.hmmAlphabet.name[code]
                        weight = state.emissions[code]
                        emission_probabilities.update({label: weight})

                    # Normalize ... should be member function
                    if abs(emission_probabilities.sum - 1.0) > 0.01:
                        key_list = emission_probabilities.keys()
                        for key in key_list:
                            emission_probabilities[key] = 1.0 / len(key_list)

                    e = ProbEditorBasics.emission_data(emission_probabilities)
                    d = ProbEditorDialogs.emission_dialog(
                        self, e, "emission probs of state %s" % state.id)
                    if d.success():
                        # write back normalized probabilities
                        for key in emission_probabilities.keys():
                            code = self.HMM.hmmAlphabet.name2code[key]
                            state.emissions[code] = emission_probabilities[
                                key] / emission_probabilities.sum

    def EditHMMObj(self, event):
        # register values
        editor.register_attr("itsEditor", None)
        editor.register_attr("G", None)
        editor.register_attr("Pi", None)
        editor.register_attr("id2index", None)

        editor.register_attr("hmmAlphabet", None)
        editor.register_attr("hmmClass", None)

        editor.register_attr("editableAttr", None)

        editor.register_attr("backgroundDistributions", None)

        editor.register_attr("state", editor.EntryEditor)

        # Edit this State with editobj widget
        editobj.edit(self.HMM)

    def EditStateUp(self, event):
        print 'Calling EditObj'
        if event.widget.find_withtag(CURRENT):
            widget = event.widget.find_withtag(CURRENT)[0]
            tags = self.canvas.gettags(widget)
            if not "edges" in tags:
                v = self.FindVertex(event)
                # print "Found Vertex " + "%s" % v
                # hidden attributes set to None
                editor.register_attr("state_class", None)
                editor.register_attr("emissions", None)  # hidden
                editor.register_attr("itsHMM", None)
                editor.register_attr("desc", None)
                editor.register_attr("index", None)
                editor.register_attr("pos", None)
                editor.register_attr("id", None)
                editor.register_attr("order", None)
                editor.register_attr("tiedto", None)
                editor.register_attr("reading_frame", None)
                editor.register_attr("duration", None)
                editor.register_attr("background", None)

                # register values
                editor.register_attr("label", ValidStringEditor)

                # Edit this State with editobj widget
                editobj.edit(self.HMM.state[v])

    def EditPropertiesUp(self, event):
        if event.widget.find_withtag(CURRENT):
            widget = event.widget.find_withtag(CURRENT)[0]
            tags = self.canvas.gettags(widget)
            if not "edges" in tags:
                v = self.FindVertex(event)
                # print "Found Vertex " + "%s" % v

                #XXX if self.HMM.state[v].state_class != -1: # we have attribute state_class
                #d = EditObjectAttributesDialog(self, self.HMM.state[v], HMMState.editableAttr + ['state_class'])
                #else:
                d = EditObjectAttributesDialog(
                    self, self.HMM.state[v],
                    HMMState.editableAttr + ['state_class'])

                # We only show the label out of the editable items
                self.HMM.G.labeling[v] = ValidatingString(
                    "%s" % (self.HMM.state[v].label))  # XXX Hack Aaaargh!
                self.UpdateVertexLabel(v, 0)

    # self.HMM.id2index[self.HMM.state[v].id] = v

    def EditHMM(self):
        d = EditObjectAttributesDialog(self, self.HMM,
                                       self.HMM.editableAttr['HMM'])

    def EditClassLabel(self):
        self.HMM.hmmClass.edit(self)

    def EditAlphabet(self):
        self.HMM.hmmAlphabet.edit(self)

    def EditPrior(self):
        if self.HMM.G.Order() == 0:
            return

        key2id = {}
        emission_probabilities = ProbEditorBasics.ProbDict({})
        for state in self.HMM.state.values():
            label = state.id
            weight = state.initial
            emission_probabilities.update({str(label): weight})
            key2id[str(label)] = state.id

        u = 1.0 / len(emission_probabilities.keys())
        if emission_probabilities.sum == 0.0:
            for key in emission_probabilities.keys():
                id = key2id[key]
                state = self.HMM.state[self.HMM.id2index[id]]
                state.initial = typed_assign(state.initial, u)
                emission_probabilities[key] = u

        if len(emission_probabilities.keys()) > 15:
            color_list = [
                'red', 'green', 'yellow', 'blue', 'black', 'grey', 'orange',
                'pink', 'gold', 'brown', 'tan', 'purple', 'magenta',
                'firebrick', 'deeppink', 'lavender', 'NavajoWhite', 'seagreen',
                'violet', 'LightGreen'
            ]
            colors = color_list
            repeats = len(emission_probabilities.keys()) / 15
            for i in range(repeats - 1):
                color_list += colors

            e = ProbEditorBasics.emission_data(emission_probabilities,
                                               color_list)
        else:
            e = ProbEditorBasics.emission_data(emission_probabilities)
        d = ProbEditorDialogs.emission_dialog(self, e, "initial probabilities")

        if d.success():
            # write back normalized probabilities
            for key in emission_probabilities.keys():
                id = key2id[key]
                state = self.HMM.state[self.HMM.id2index[id]]
                if emission_probabilities.sum == 0.0:
                    state.initial = typed_assign(state.initial, u)
                else:
                    state.initial = typed_assign(
                        state.initial, emission_probabilities[key] /
                        emission_probabilities.sum)

    def EditBackgroundDistributions(self):
        self.HMM.backgroundDistributions.editDistributions(self)

    def AddVertexCanvas(self, x, y):
        v = GraphDisplay.AddVertexCanvas(self, x, y)
        print "AddVertex ", v, "at ", x, y
        index = self.HMM.AddState(v)  # return index to this state
        state = self.HMM.state[index]
        self.HMM.G.embedding[state.index] = self.G.embedding[v]

    def MoveVertex(self, v, x, y, doUpdate=None):
        GraphDisplay.MoveVertex(self, v, x, y, doUpdate)
        state = self.HMM.state[v]  # transfer the coordinate
        self.HMM.G.embedding[state.index] = self.G.embedding[v]

    def AddEdge(self, tail, head):
        GraphDisplay.AddEdge(self, tail, head)
        self.HMM.G.edgeWeights[0][(tail, head)] = 1.0

    def DeleteVertex(self, v):
        self.HMM.DeleteState(v)
        SAGraphEditor.DeleteVertex(self, v)

    def ShowCoords(self, event):
        pass
Beispiel #2
0
class HMM:
    def __init__(self, XMLFileName=None, G=None):
        # self.itsEditor = itsEditor
        if (G is None):
            self.G = Graph()
        else:
            self.G = G

        self.G.directed = 1
        self.G.euclidian = 0
        self.G.simple = 0
        self.Pi = {}
        self.id2index = {}

        # self.hmmAlphabet = DiscreteHMMAlphabet()
        self.hmmClass = HMMClass()

        # in the case of pair HMMs we have several
        self.hmmAlphabets = {}
        self.transitionFunctions = {}

        self.editableAttr = {}
        self.editableAttr['HMM'] = ['desc']
        self.desc = ValidatingString()

        self.state = {}

        self.modelType = 0
        self.name = "NoName"

        self.backgroundDistributions = NamedDistributions(self)

        self.DocumentName = "graphml"
        if XMLFileName != None:
            self.OpenXML(XMLFileName)

    def Clear(self):
        self.G.Clear()
        self.Pi = {}
        self.id2index = {}

        # self.hmmAlphabet = DiscreteHMMAlphabet()
        self.hmmAlphabets = {}
        self.hmmClass = HMMClass()
        self.backgroundDistributions = NamedDistributions(self)

        self.editableAttr = {}
        self.editableAttr['HMM'] = ['desc']
        self.desc = ValidatingString()
        self.state = {}
        self.DocumentName = "graphml"

    def AddState(self, index, label='None'):
        state = HMMState(-1, self)
        if self.id2index.keys() != []:
            state.id = max(self.id2index.keys()) + 1
        else:
            state.id = 1
        state.index = index
        self.id2index[state.id] = state.index
        self.state[state.index] = state  # XXX Use canvas id
        state.label = typed_assign(state.label, state.id)
        self.G.labeling[state.index] = "%s" % (state.label)
        return state.index

    def DeleteState(self, index):
        """ The method only deletes a map between index and its state object.
	    The caller must delete the corresponding vertex in the owner Graph self.G. """
        del self.id2index[self.state[index].id]
        del self.state[index]

    def fromDOM(self, XMLNode):

        # self.hmmClass.fromDOM(XMLNode.getElementsByTagName("hmm:class")[0])
        for tag in XMLNode.getElementsByTagName("hmm:class"):
            self.hmmClass.fromDOM(tag)

        nameNodes = XMLNode.getElementsByTagName("hmm:name")
        if (len(nameNodes) > 0):
            self.modelType = nameNodes[0].firstChild.nodeValue

        # model type node
        modelTypeNodes = XMLNode.getElementsByTagName("hmm:modeltype")
        if (len(modelTypeNodes) > 0):
            self.modelType = modelTypeNodes[0].firstChild.nodeValue
        if (self.modelType == "pairHMM"):
            alphabetNodes = XMLNode.getElementsByTagName("hmm:alphabet")
            for alphabetNode in alphabetNodes:
                alphabet = DiscreteHMMAlphabet()
                alphabet.fromDOM(alphabetNode)
                self.hmmAlphabets[alphabet.id] = alphabet
            transitionFunctionNodes = XMLNode.getElementsByTagName(
                "hmm:transitionfunction")
            for transitionFunctionNode in transitionFunctionNodes:
                transitionFunction = TransitionFunction()
                transitionFunction.fromDom(transitionFunctionNode)
                self.transitionFunctions[
                    transitionFunction.id] = transitionFunction
        else:
            # If it is no pair hmm One "hmm:alphabet" XML element
            self.hmmAlphabets[0] = DiscreteHMMAlphabet()
            self.hmmAlphabets[0].fromDOM(
                XMLNode.getElementsByTagName("hmm:alphabet")[0])

        self.backgroundDistributions.fromDOM(XMLNode)

        nodes = XMLNode.getElementsByTagName("node")
        for n in nodes:
            state = HMMState(-1, self)
            state.fromDOM(n)
            self.state[state.index] = state  # key must be string
            self.id2index[state.id] = state.index
            self.G.embedding[state.index] = state.pos
            self.G.labeling[state.index] = "%s\n%s" % (state.id, state.label
                                                       )  # XXX Hack Aaaargh!

        edges = XMLNode.getElementsByTagName("edge")
        # nr_classes = int(self.hmmClass.high()-self.hmmClass.low())+1
        nr_classes = 1
        # search in all states for the maximal kclasses
        for s in self.state.values():
            if (s.kclasses > nr_classes):
                nr_classes = s.kclasses

        for i in range(nr_classes):
            self.G.edgeWeights[i] = EdgeWeight(self.G)

        for edge in edges:
            i = self.id2index[int(edge.attributes['source'].nodeValue)]
            j = self.id2index[int(edge.attributes['target'].nodeValue)]
            source = self.state[i]
            datas = edge.getElementsByTagName("data")
            for data in datas:
                dataKey = data.attributes['key'].nodeValue
                # dataValue = data.firstChild.nodeValue

            if dataKey == 'prob':
                #p = float(dataValue)
                # collect all strings from childnodes
                dataValue = ""
                for child in data.childNodes:
                    dataValue += child.nodeValue
                p = listFromCSV(dataValue, types.FloatType)
                self.G.AddEdge(i, j)
                if len(p) == 1:  # only one class
                    for cl in range(source.kclasses - 1):
                        p.append(0.0)

                for cl in range(source.kclasses):
                    self.G.edgeWeights[cl][(i, j)] = p[cl]

    def modelCheck(self):

        # Compute sums of initial probabilities for renormalization
        initial_sum = 0.0
        for s in self.state:
            initial_sum = initial_sum + self.state[s].initial

        if initial_sum == 0.0:
            raise NotValidHMMType("Initial state is not specified.")

        if (len(self.hmmAlphabets) == 0):
            raise AlphabetErrorType(
                "Alphabet object is empty. You must create alphabet before saving."
            )

    def toDOM(self, XMLDoc, XMLNode):
        graphml = XMLDoc.createElement("graphml")
        # define namespaces (proper XML and new expat needs it)
        graphml.setAttribute('xmlns', 'http://graphml.graphdrawing.org/xmlns')
        graphml.setAttribute('xmlns:gd', 'gdnamespace')  # find the correct URI
        graphml.setAttribute('xmlns:hmm',
                             'http://www.ghmm.org/xml/')  #arbitrary
        XMLNode.appendChild(graphml)

        # Create key elements
        hmmtype = XMLDoc.createElement("key")
        hmmtype.setAttribute('id', 'emissions')
        hmmtype.setAttribute('gd:type',
                             'HigherDiscreteProbDist')  # what's your type?
        hmmtype.setAttribute('for', 'node')
        graphml.appendChild(hmmtype)

        self.hmmClass.toDOM(XMLDoc, graphml)

        if (self.modelType == "pairHMM"):
            modelType = XMLDoc.createElement("hmm:modeltype")
            modelType.appendChild(XMLDoc.createTextNode("pairHMM"))
            graphml.appendChild(modelType)

        for alphabet in self.hmmAlphabets.values():
            alphabet.toDOM(XMLDoc, graphml)
        self.backgroundDistributions.toDOM(XMLDoc, graphml)

        if len(self.transitionFunctions.keys()) != 0:
            transitionFunctionsNode = XMLDoc.createElement(
                "hmm:transitionfunctions")
            for transitionFunction in self.transitionFunctions.values():
                transitionFunction.toDom(XMLDoc, transitionFunctionsNode)
            graphml.appendChild(transitionFunctionsNode)

        graph = XMLDoc.createElement("graph")

        # Compute sums of initial probabilities for renormalization
        initial_sum = 0.0
        for s in self.state.keys():
            initial_sum = initial_sum + self.state[s].initial

        for s in self.state.keys():
            self.state[s].toDOM(XMLDoc, graph, initial_sum)

        # Compute sums of outgoing probabilities for renormalization of transition probabilities
        # NOTE: need dictionaries here
        out_sum = {}
        nr_classes = int(self.hmmClass.high()) - int(self.hmmClass.low()) + 1
        for v in self.G.vertices:
            out_sum[v] = [0.0] * nr_classes

        for cl in range(1):  # XXX Assuming one transition class
            for e in self.G.Edges():
                if self.G.edgeWeights[cl].has_key(e):
                    out_sum[e[0]][cl] = out_sum[
                        e[0]][cl] + self.G.edgeWeights[cl][e]

        for e in self.G.Edges():
            transitions = []
            edge_elem = XMLDoc.createElement("edge")
            edge_elem.setAttribute('source', "%s" % self.state[e[0]].id)
            edge_elem.setAttribute('target', "%s" % self.state[e[1]].id)
            # writeData(XMLDoc, edge_elem, 'prob', self.G.edgeWeights[cl][e] / out_sum[e[0]])
            # XXX Assuming one transition class for cl in range(nr_classes):
            for cl in range(1):
                if self.G.edgeWeights[cl].has_key(e) and out_sum[e[0]][cl]:
                    transitions.append(self.G.edgeWeights[cl][e] /
                                       out_sum[e[0]][cl])
                else:
                    transitions.append(0.0)

            writeData(XMLDoc, edge_elem, 'prob', csvFromList(transitions))

            graph.appendChild(edge_elem)

        graphml.appendChild(graph)

    def AlphabetType(self):
        """ return the type of emission domain 
	    XXX should call the method in HMMAlphabet
	"""
        return int

    def ClassType(self):
        pass

    def DistributionType(self):
        pass

    def getBackgroundDist(self):
        """ Return a pair of two dictionaries: (distribution, its orders):
            a distribution is a list of real values of length N^(order+1).   
        """
        return (self.backgroundDistributions.dist,
                self.backgroundDistributions.order,
                self.backgroundDistributions.code2name)

    def buildMatrices(self):
        """ return [alphabets_code, A, B, pi, state_orders] """
        pi = []
        B = []
        A = []
        nstates = len(self.state.keys())
        orders = {}
        k = 0  # C style index
        for s in self.state.values():  # ordering from XML
            orders[s.index] = k
            k = k + 1

        state_orders = []
        for s in self.state.values():  # a list of indices
            pi.append(s.initial)
            state_orders.append(s.order)  # state order

            size = self.hmmAlphabets[s.alphabet_id].size()
            if (self.modelType != "pairHMM"
                    and size**(s.order + 1) != len(s.emissions)):
                raise ValueError  # exception: inconsistency between ordering and emission

            B.append(s.emissions)  # emission

            # transition probability
            v = s.index
            outprobs = [0.0] * nstates
            for outid in self.G.OutNeighbors(v)[:]:
                myorder = orders[outid]
                outprobs[myorder] = self.G.edgeWeights[0][(v, outid)]
            A.append(outprobs)

        alphabets = self.hmmAlphabets[0].name.values()  # list of alphabets
        return [alphabets, A, B, pi, state_orders]

    def getStateAlphabets(self):
        alphabets = []
        for s in self.state.values():
            alphabets.append(self.hmmAlphabets[s.alphabet_id])
        return alphabets

    def getAlphabets(self):
        return self.hmmAlphabets

    def getLabels(self):
        """ returns list of state labels and unique labels """
        label_list = []
        labels = {}
        for s in self.state.values():  # a list of indices
            label_list.append(self.hmmClass.code2name[s.state_class])
            labels[label_list[-1]] = 0
        return (label_list, labels.keys())

    def getTiedStates(self):
        """ returns list of tied states, entry is None if a state isn't to
            any other state, returns an empty list, if no state is tied """
        tiedstates = []
        isTied = 0

        orders = {}
        k = 0  # C style index
        for s in self.state.values():  # ordering from XML
            orders[s.id] = k
            k = k + 1

        for s in self.state.values():  # a list of indices
            if s.tiedto == '':
                tiedstates.append(-1)
            else:
                tiedstates.append(orders[int(s.tiedto)])
                isTied = 1

        if not isTied:
            tiedstates = []
        return tiedstates

    def getStateDurations(self):
        """ returns a list of the minimal number of times a state is evaluated
            before the HMM changes to another state."""

        durations = []
        hasduration = 0

        for s in self.state.values():  # a list of indices
            if s.duration == 0:
                durations.append(1)
            else:
                durations.append(s.duration)
                hasduration = 1

        if not hasduration:
            durations = []
        return durations

    def OpenXML(self, fileName_file_or_dom):
        if (not isinstance(fileName_file_or_dom, xml.dom.minidom.Document)):
            dom = xml.dom.minidom.parse(fileName_file_or_dom)
        else:
            dom = fileName_file_or_dom
        if dom.documentElement.tagName == "ghmm":
            sys.stderr.write("Do not support ghmm format")
            raise FormatError
            dom.unlink()
            #self.DocumentName = "ghmm"
            #ghmmdom  = dom
            #ghmml = GHMMXML()
            #dom   = ghmml.GraphMLDOM(ghmmdom)
            #ghmmdom.unlink()
        else:
            assert dom.documentElement.tagName == "graphml"
            self.fromDOM(dom)

# dom.unlink()

    def WriteXML(self, fileName):
        try:
            self.modelCheck()  # raise exceptions here
            doc = xml.dom.minidom.Document()
            self.toDOM(doc, doc)
            file = open(fileName, 'w')
            # xml.dom.ext.PrettyPrint(doc, file)
            file.write(toprettyxml(doc))  # problem with white spaces
            file.close()
            doc.unlink()
        except HMMEdError:
            print "HMMEdError: No file was written due to errors in the model."

    def WriteGHMM(self, fileName):
        self.modelCheck()  # raise exceptions here
        doc = xml.dom.minidom.Document()
        ghmm = doc.createElement("ghmm")
        doc.appendChild(ghmm)
        self.toGHMM(doc, ghmm)
        file = open(fileName, 'w')
        # xml.dom.ext.PrettyPrint(doc, file)
        file.write(toprettyxml(doc))  # problem with white spaces
        file.close()
        doc.unlink()

    def SaveAs(self, fileName):
        if (self.DocumentName == "graphml"):
            self.WriteXML(fileName)
        else:
            self.WriteGHMM(fileName)

    def SaveAsGHMM(self, fileName):
        self.WriteGHMM(fileName)