<commit_before>"""DMD class"""

import numpy as N
from vectorspace import VectorSpace
import pod
import util
import parallel as parallel_mod
parallel = parallel_mod.parallel_default_instance
import vectors as V

class DMD(object):
    """Dynamic Mode Decomposition/Koopman Mode Decomposition.

    Kwargs:
        inner_product: Function to compute inner product.

        put_mat: Function to put a matrix out of modred.

      	get_mat: Function to get a matrix into modred.

        max_vecs_per_node: max number of vectors in memory per node.

        verbosity: 0 prints almost nothing, 1 prints progress and warnings

        POD: POD object to use for computations.

    Computes Ritz vectors from vecs.

    Usage::

      myDMD = DMD(my_inner_product)
      myDMD.compute_decomp(vec_handles)
      myDMD.compute_modes(range(50), mode_handles)

    """
    def __init__(self, inner_product,
        get_mat=util.load_array_text, put_mat=util.save_array_text,
        max_vecs_per_node=None, POD=None, verbosity=1):
        """Constructor"""
        self.vec_space = VectorSpace(inner_product=inner_product,
            max_vecs_per_node=max_vecs_per_node, verbosity=verbosity)
        self.get_mat = get_mat
        self.put_mat = put_mat
        self.POD = POD
        self.verbosity = verbosity
        self.ritz_vals = None
        self.build_coeffs = None
        self.mode_norms = None
        self.vec_handles = None
        self.vecs = None

    def sanity_check(self, test_vec_handle):
        """Check user-supplied vector handle.

        Args:
            test_vec_handle: a vector handle.

        See :py:meth:`vectorspace.VectorSpace.sanity_check`.
        """
        self.vec_space.sanity_check(test_vec_handle)

    def sanity_check_in_memory(self, test_vec):
        """Check user-supplied vector object.

        Args:
            test_vec: a vector.

        See :py:meth:`vectorspace.VectorSpace.sanity_check_in_memory`.
        """
        self.vec_space.sanity_check_in_memory(test_vec_handle)

    def get_decomp(self, ritz_vals_source, mode_norms_source,
        build_coeffs_source):
        """Retrieves the decomposition matrices from a source. """
        if self.get_mat is None:
            raise util.UndefinedError('Must specify a get_mat function')
        if parallel.is_rank_zero():
            self.ritz_vals = N.squeeze(N.array(self.get_mat(ritz_vals_source)))
            self.mode_norms = N.squeeze(N.array(self.get_mat(mode_norms_source)))
            self.build_coeffs = self.get_mat(build_coeffs_source)
        else:
            self.ritz_vals = None
            self.mode_norms = None
            self.build_coeffs = None
        if parallel.is_distributed():
            self.ritz_vals = parallel.comm.bcast(self.ritz_vals, root=0)
            self.mode_norms = parallel.comm.bcast(self.mode_norms, root=0)
            self.build_coeffs = parallel.comm.bcast(self.build_coeffs, root=0)

    def put_decomp(self, ritz_vals_dest, mode_norms_dest, build_coeffs_dest):
        """Puts the decomposition matrices in dest."""
        self.put_ritz_vals(ritz_vals_dest)
        self.put_mode_norms(mode_norms_dest)
        self.put_build_coeffs(build_coeffs_dest)

    def put_ritz_vals(self, dest):
        """Puts the Ritz values"""
        if self.put_mat is None and parallel.is_rank_zero():
            raise util.UndefinedError("put_mat is undefined, can't put")
        if parallel.is_rank_zero():
            self.put_mat(self.ritz_vals, dest)
        parallel.barrier()

    def put_mode_norms(self, dest):
        """Puts the mode norms"""
        if self.put_mat is None and parallel.is_rank_zero():
            raise util.UndefinedError("put_mat is undefined, can't put")
        if parallel.is_rank_zero():
            self.put_mat(self.mode_norms, dest)
        parallel.barrier()

    def put_build_coeffs(self, dest):
        """Puts the build coeffs"""
        if self.put_mat is None and parallel.is_rank_zero():
            raise util.UndefinedError("put_mat is undefined, can't put")
        if parallel.is_rank_zero():
            self.put_mat(self.build_coeffs, dest)
        parallel.barrier()

    def compute_decomp(self, vec_handles):
        """Computes decomposition and returns SVD matrices.

        Args:
            vec_handles: list of handles for the vecs.

        Returns:
            vec_handles, ritz_vals, mode_norms, build_coeffs.
        """
        if vec_handles is not None:
            self.vec_handles = util.make_list(vec_handles)
        if self.vec_handles is None:
            raise util.UndefinedError('vec_handles is not given')

        # Compute POD from vecs (excluding last vec)
        if self.POD is None:
            self.POD = pod.POD(inner_product=self.vec_space.inner_product,
                max_vecs_per_node=self.vec_space.max_vecs_per_node,
                verbosity=self.verbosity)
            # Don't use the returned mats, get them later from POD instance.
            dum, dum = self.POD.compute_decomp(
                vec_handles=self.vec_handles[:-1])
        elif self.vec_handles[:-1] != self.POD.vec_handles or \
            len(vec_handles) != len(self.POD.vec_handles)+1:
            raise RuntimeError('vec mismatch between POD and DMD '+\
                'objects.')
        pod_sing_vecs = self.POD.sing_vecs
        pod_sing_vals = self.POD.sing_vals
        _pod_sing_vals_sqrt_mat = N.mat(N.diag(pod_sing_vals** -0.5))

        # Inner product of vecs w/POD modes
        num_vecs = len(self.vec_handles)
        pod_modes_star_times_vecs = N.mat(N.empty((num_vecs-1, num_vecs-1)))
        pod_modes_star_times_vecs[:,:-1] = self.POD.correlation_mat[:,1:]
        pod_modes_star_times_vecs[:,-1] = \
            self.vec_space.compute_inner_product_mat(self.vec_handles[:-1],
                self.vec_handles[-1])
        pod_modes_star_times_vecs = _pod_sing_vals_sqrt_mat * pod_sing_vecs.H *\
            pod_modes_star_times_vecs

        # Reduced order linear system
        low_order_linear_map = pod_modes_star_times_vecs * pod_sing_vecs * \
            _pod_sing_vals_sqrt_mat
        self.ritz_vals, low_order_eig_vecs = N.linalg.eig(low_order_linear_map)

        # Scale Ritz vectors
        ritz_vecs_star_times_init_vec = low_order_eig_vecs.H * _pod_sing_vals_sqrt_mat * \
            pod_sing_vecs.H * self.POD.correlation_mat[:,0]
        ritz_vec_scaling = N.linalg.inv(low_order_eig_vecs.H * low_order_eig_vecs) *\
            ritz_vecs_star_times_init_vec

        ritz_vec_scaling = N.mat(N.diag(N.array(N.array(
            ritz_vec_scaling).squeeze(),ndmin=1)))

        # Compute mode energies
        self.build_coeffs = pod_sing_vecs * _pod_sing_vals_sqrt_mat *\
            low_order_eig_vecs * ritz_vec_scaling
        self.mode_norms = N.diag(self.build_coeffs.H *
            self.POD.correlation_mat * self.build_coeffs).real
        return self.ritz_vals, self.mode_norms, self.build_coeffs

    def compute_decomp_in_memory(self, vecs):
        """Same as ``compute_decomp`` but takes vecs instead of handles."""
        self.vecs = util.make_list(vecs)
        vec_handles = [V.InMemoryVecHandle(v) for v in self.vecs]
        return self.compute_decomp(vec_handles)

    def compute_modes(self, mode_nums, mode_handles, vec_handles=None,
        index_from=0):
        """Computes modes and calls ``mode_handle.put`` on them.

        Args:
            mode_nums: list of mode numbers, ``range(10)`` or ``[3, 2, 5]``.

            mode_handles: list of handles for modes.

        Kwargs:
            vec_handles: list of handles for vecs, can omit if given in
            :py:meth:`compute_decomp`.

            index_from: integer to start numbering modes from, 0, 1, or other.
        """
        if self.build_coeffs is None:
            raise util.UndefinedError('Must define self.build_coeffs')
        # User should specify ALL vecs, even though all but last are used
        if vec_handles is not None:
            self.vec_handles = util.make_list(vec_handles)
        self.vec_space.compute_modes(mode_nums, mode_handles,
            self.vec_handles[:-1], self.build_coeffs, index_from=index_from)

    def compute_modes_in_memory(self, mode_nums, vecs=None,
        index_from=0):
        """Computes modes and returns them.

        Args:
            mode_nums: list of mode numbers, ``range(10)`` or ``[3, 2, 5]``.

        Kwargs:
            vecs: list of handles for vecs, can omit if given in
            :py:meth:`compute_decomp`.

            index_from: integer to start numbering modes from, 0, 1, or other.

        Returns:
            a list of all modes.

        In parallel, each MPI worker returns all modes.
        See :py:meth:`compute_modes`.
        """
        if self.build_coeffs is None:
            raise util.UndefinedError('Must define self.build_coeffs')
        # User should specify ALL vecs, even though all but last are used
        if vecs is not None:
            self.vecs = util.make_list(vecs)
        return self.vec_space.compute_modes_in_memory(mode_nums,
            self.vecs[:-1], self.build_coeffs, index_from=index_from)

<commit_msg>Fixed typo in DMD documentation.<commit_after>"""DMD class"""

import numpy as N
from vectorspace import VectorSpace
import pod
import util
import parallel as parallel_mod
parallel = parallel_mod.parallel_default_instance
import vectors as V

class DMD(object):
    """Dynamic Mode Decomposition/Koopman Mode Decomposition.

    Kwargs:
        inner_product: Function to compute inner product.

        put_mat: Function to put a matrix out of modred.

      	get_mat: Function to get a matrix into modred.

        max_vecs_per_node: max number of vectors in memory per node.

        verbosity: 0 prints almost nothing, 1 prints progress and warnings

        POD: POD object to use for computations.

    Computes Ritz vectors from vecs.

    Usage::

      myDMD = DMD(my_inner_product)
      myDMD.compute_decomp(vec_handles)
      myDMD.compute_modes(range(50), mode_handles)

    """
    def __init__(self, inner_product,
        get_mat=util.load_array_text, put_mat=util.save_array_text,
        max_vecs_per_node=None, POD=None, verbosity=1):
        """Constructor"""
        self.vec_space = VectorSpace(inner_product=inner_product,
            max_vecs_per_node=max_vecs_per_node, verbosity=verbosity)
        self.get_mat = get_mat
        self.put_mat = put_mat
        self.POD = POD
        self.verbosity = verbosity
        self.ritz_vals = None
        self.build_coeffs = None
        self.mode_norms = None
        self.vec_handles = None
        self.vecs = None

    def sanity_check(self, test_vec_handle):
        """Check user-supplied vector handle.

        Args:
            test_vec_handle: a vector handle.

        See :py:meth:`vectorspace.VectorSpace.sanity_check`.
        """
        self.vec_space.sanity_check(test_vec_handle)

    def sanity_check_in_memory(self, test_vec):
        """Check user-supplied vector object.

        Args:
            test_vec: a vector.

        See :py:meth:`vectorspace.VectorSpace.sanity_check_in_memory`.
        """
        self.vec_space.sanity_check_in_memory(test_vec_handle)

    def get_decomp(self, ritz_vals_source, mode_norms_source,
        build_coeffs_source):
        """Retrieves the decomposition matrices from a source. """
        if self.get_mat is None:
            raise util.UndefinedError('Must specify a get_mat function')
        if parallel.is_rank_zero():
            self.ritz_vals = N.squeeze(N.array(self.get_mat(ritz_vals_source)))
            self.mode_norms = N.squeeze(N.array(self.get_mat(mode_norms_source)))
            self.build_coeffs = self.get_mat(build_coeffs_source)
        else:
            self.ritz_vals = None
            self.mode_norms = None
            self.build_coeffs = None
        if parallel.is_distributed():
            self.ritz_vals = parallel.comm.bcast(self.ritz_vals, root=0)
            self.mode_norms = parallel.comm.bcast(self.mode_norms, root=0)
            self.build_coeffs = parallel.comm.bcast(self.build_coeffs, root=0)

    def put_decomp(self, ritz_vals_dest, mode_norms_dest, build_coeffs_dest):
        """Puts the decomposition matrices in dest."""
        self.put_ritz_vals(ritz_vals_dest)
        self.put_mode_norms(mode_norms_dest)
        self.put_build_coeffs(build_coeffs_dest)

    def put_ritz_vals(self, dest):
        """Puts the Ritz values"""
        if self.put_mat is None and parallel.is_rank_zero():
            raise util.UndefinedError("put_mat is undefined, can't put")
        if parallel.is_rank_zero():
            self.put_mat(self.ritz_vals, dest)
        parallel.barrier()

    def put_mode_norms(self, dest):
        """Puts the mode norms"""
        if self.put_mat is None and parallel.is_rank_zero():
            raise util.UndefinedError("put_mat is undefined, can't put")
        if parallel.is_rank_zero():
            self.put_mat(self.mode_norms, dest)
        parallel.barrier()

    def put_build_coeffs(self, dest):
        """Puts the build coeffs"""
        if self.put_mat is None and parallel.is_rank_zero():
            raise util.UndefinedError("put_mat is undefined, can't put")
        if parallel.is_rank_zero():
            self.put_mat(self.build_coeffs, dest)
        parallel.barrier()

    def compute_decomp(self, vec_handles):
        """Computes decomposition and returns SVD matrices.

        Args:
            vec_handles: list of handles for the vecs.

        Returns:
            ritz_vals, mode_norms, build_coeffs.
        """
        if vec_handles is not None:
            self.vec_handles = util.make_list(vec_handles)
        if self.vec_handles is None:
            raise util.UndefinedError('vec_handles is not given')

        # Compute POD from vecs (excluding last vec)
        if self.POD is None:
            self.POD = pod.POD(inner_product=self.vec_space.inner_product,
                max_vecs_per_node=self.vec_space.max_vecs_per_node,
                verbosity=self.verbosity)
            # Don't use the returned mats, get them later from POD instance.
            dum, dum = self.POD.compute_decomp(
                vec_handles=self.vec_handles[:-1])
        elif self.vec_handles[:-1] != self.POD.vec_handles or \
            len(vec_handles) != len(self.POD.vec_handles)+1:
            raise RuntimeError('vec mismatch between POD and DMD '+\
                'objects.')
        pod_sing_vecs = self.POD.sing_vecs
        pod_sing_vals = self.POD.sing_vals
        _pod_sing_vals_sqrt_mat = N.mat(N.diag(pod_sing_vals** -0.5))

        # Inner product of vecs w/POD modes
        num_vecs = len(self.vec_handles)
        pod_modes_star_times_vecs = N.mat(N.empty((num_vecs-1, num_vecs-1)))
        pod_modes_star_times_vecs[:,:-1] = self.POD.correlation_mat[:,1:]
        pod_modes_star_times_vecs[:,-1] = \
            self.vec_space.compute_inner_product_mat(self.vec_handles[:-1],
                self.vec_handles[-1])
        pod_modes_star_times_vecs = _pod_sing_vals_sqrt_mat * pod_sing_vecs.H *\
            pod_modes_star_times_vecs

        # Reduced order linear system
        low_order_linear_map = pod_modes_star_times_vecs * pod_sing_vecs * \
            _pod_sing_vals_sqrt_mat
        self.ritz_vals, low_order_eig_vecs = N.linalg.eig(low_order_linear_map)

        # Scale Ritz vectors
        ritz_vecs_star_times_init_vec = low_order_eig_vecs.H * _pod_sing_vals_sqrt_mat * \
            pod_sing_vecs.H * self.POD.correlation_mat[:,0]
        ritz_vec_scaling = N.linalg.inv(low_order_eig_vecs.H * low_order_eig_vecs) *\
            ritz_vecs_star_times_init_vec

        ritz_vec_scaling = N.mat(N.diag(N.array(N.array(
            ritz_vec_scaling).squeeze(),ndmin=1)))

        # Compute mode energies
        self.build_coeffs = pod_sing_vecs * _pod_sing_vals_sqrt_mat *\
            low_order_eig_vecs * ritz_vec_scaling
        self.mode_norms = N.diag(self.build_coeffs.H *
            self.POD.correlation_mat * self.build_coeffs).real
        return self.ritz_vals, self.mode_norms, self.build_coeffs

    def compute_decomp_in_memory(self, vecs):
        """Same as ``compute_decomp`` but takes vecs instead of handles."""
        self.vecs = util.make_list(vecs)
        vec_handles = [V.InMemoryVecHandle(v) for v in self.vecs]
        return self.compute_decomp(vec_handles)

    def compute_modes(self, mode_nums, mode_handles, vec_handles=None,
        index_from=0):
        """Computes modes and calls ``mode_handle.put`` on them.

        Args:
            mode_nums: list of mode numbers, ``range(10)`` or ``[3, 2, 5]``.

            mode_handles: list of handles for modes.

        Kwargs:
            vec_handles: list of handles for vecs, can omit if given in
            :py:meth:`compute_decomp`.

            index_from: integer to start numbering modes from, 0, 1, or other.
        """
        if self.build_coeffs is None:
            raise util.UndefinedError('Must define self.build_coeffs')
        # User should specify ALL vecs, even though all but last are used
        if vec_handles is not None:
            self.vec_handles = util.make_list(vec_handles)
        self.vec_space.compute_modes(mode_nums, mode_handles,
            self.vec_handles[:-1], self.build_coeffs, index_from=index_from)

    def compute_modes_in_memory(self, mode_nums, vecs=None,
        index_from=0):
        """Computes modes and returns them.

        Args:
            mode_nums: list of mode numbers, ``range(10)`` or ``[3, 2, 5]``.

        Kwargs:
            vecs: list of handles for vecs, can omit if given in
            :py:meth:`compute_decomp`.

            index_from: integer to start numbering modes from, 0, 1, or other.

        Returns:
            a list of all modes.

        In parallel, each MPI worker returns all modes.
        See :py:meth:`compute_modes`.
        """
        if self.build_coeffs is None:
            raise util.UndefinedError('Must define self.build_coeffs')
        # User should specify ALL vecs, even though all but last are used
        if vecs is not None:
            self.vecs = util.make_list(vecs)
        return self.vec_space.compute_modes_in_memory(mode_nums,
            self.vecs[:-1], self.build_coeffs, index_from=index_from)

<|endoftext|>


<commit_before>import io
import os
import tempfile
import unittest

import mock

from chainer import datasets
from chainer import testing

class TestPickleDataset(unittest.TestCase):

    def setUp(self):
        self.io = io.BytesIO()

    def test_write_read(self):
        writer = datasets.PickleDatasetWriter(self.io)
        writer.write(1)
        writer.write('hello')
        writer.write(1.5)
        writer.flush()

        dataset = datasets.PickleDataset(self.io)
        assert len(dataset) == 3
        assert dataset[0] == 1
        assert dataset[2] == 1.5
        assert dataset[1] == 'hello'

class TestPickleDatasetHelper(unittest.TestCase):

    def setUp(self):
        _, self.path = tempfile.mkstemp()

    def tearDown(self):
        if os.path.exists(self.path):
            os.remove(self.path)

    def test_write_read(self):
        with datasets.open_pickle_dataset_writer(self.path) as writer:
            writer.write(1)

        with datasets.open_pickle_dataset(self.path) as dataset:
            assert dataset[0] == 1

testing.run_module(__name__, __file__)
<commit_msg>Fix import<commit_after>import io
import os
import tempfile
import unittest

from chainer import datasets
from chainer import testing

class TestPickleDataset(unittest.TestCase):

    def setUp(self):
        self.io = io.BytesIO()

    def test_write_read(self):
        writer = datasets.PickleDatasetWriter(self.io)
        writer.write(1)
        writer.write('hello')
        writer.write(1.5)
        writer.flush()

        dataset = datasets.PickleDataset(self.io)
        assert len(dataset) == 3
        assert dataset[0] == 1
        assert dataset[2] == 1.5
        assert dataset[1] == 'hello'

class TestPickleDatasetHelper(unittest.TestCase):

    def setUp(self):
        _, self.path = tempfile.mkstemp()

    def tearDown(self):
        if os.path.exists(self.path):
            os.remove(self.path)

    def test_write_read(self):
        with datasets.open_pickle_dataset_writer(self.path) as writer:
            writer.write(1)

        with datasets.open_pickle_dataset(self.path) as dataset:
            assert dataset[0] == 1

testing.run_module(__name__, __file__)
<|endoftext|>


<commit_before>#
# -*- coding: utf-8 -*-
#

from site import *
from functools import wraps

from twisted.internet.defer import _inlineCallbacks, _DefGen_Return

from django.utils import translation

from dtx.core import logger
log = logger.log(__name__)

currentRequest = None

class saveRequest:
    def __init__(self, g):
        self.g = g
    def __enter__(self):
        global currentRequest
        #log.msg('=== RESTORING REQUEST ===')
        #log.msg(unicode(self.g.r))
        currentRequest = self.g.r
        log.msg(u'Activating language: {}'.format(currentRequest.language))
        translation.activate(currentRequest.language)
    def __exit__(self, type, value, tb):
        global currentRequest
        #log.msg('===== SAVING REQUEST ====')
        #log.msg(unicode(currentRequest))
        self.g.r = currentRequest

class Generator:
    def __init__(self, g):
        global currentRequest
        self.g = g
        #log.msg('===== SAVING REQUEST ====')
        #log.msg(unicode(currentRequest))
        self.r = currentRequest

    def send(self, x):
        with saveRequest(self):
            return self.g.send(x)

    def throw(self, *args, **kwargs):
        with saveRequest(self):
            return self.g.throw(*args, **kwargs)

def webMethod(f):
    @wraps(f)
    def unwindGenerator(*args, **kwargs):
        try:
            gen = f(*args, **kwargs)
        except _DefGen_Return:
            raise TypeError(
                "webMethod requires %r to produce a generator; instead"
                "caught returnValue being used in a non-generator" % (f,))
        if not isinstance(gen, types.GeneratorType):
            raise TypeError(
                "webMethod requires %r to produce a generator; "
                "instead got %r" % (f, gen))
        return _inlineCallbacks(None, Generator(gen), Deferred())
        f._without_request = True
    return unwindGenerator

__all__ = [
    'webMethod',
]
<commit_msg>Logging<commit_after>#
# -*- coding: utf-8 -*-
#

from site import *
from functools import wraps

from twisted.internet.defer import _inlineCallbacks, _DefGen_Return

from django.utils import translation

from dtx.core import logger
log = logger.log(__name__)

currentRequest = None

class saveRequest:
    def __init__(self, g):
        self.g = g
    def __enter__(self):
        global currentRequest
        #log.msg('=== RESTORING REQUEST ===')
        #log.msg(unicode(self.g.r))
        currentRequest = self.g.r
        log.debug(u'Activating language: {}'.format(currentRequest.language))
        translation.activate(currentRequest.language)
    def __exit__(self, type, value, tb):
        global currentRequest
        #log.msg('===== SAVING REQUEST ====')
        #log.msg(unicode(currentRequest))
        self.g.r = currentRequest

class Generator:
    def __init__(self, g):
        global currentRequest
        self.g = g
        #log.msg('===== SAVING REQUEST ====')
        #log.msg(unicode(currentRequest))
        self.r = currentRequest

    def send(self, x):
        with saveRequest(self):
            return self.g.send(x)

    def throw(self, *args, **kwargs):
        with saveRequest(self):
            return self.g.throw(*args, **kwargs)

def webMethod(f):
    @wraps(f)
    def unwindGenerator(*args, **kwargs):
        try:
            gen = f(*args, **kwargs)
        except _DefGen_Return:
            raise TypeError(
                "webMethod requires %r to produce a generator; instead"
                "caught returnValue being used in a non-generator" % (f,))
        if not isinstance(gen, types.GeneratorType):
            raise TypeError(
                "webMethod requires %r to produce a generator; "
                "instead got %r" % (f, gen))
        return _inlineCallbacks(None, Generator(gen), Deferred())
        f._without_request = True
    return unwindGenerator

__all__ = [
    'webMethod',
]
<|endoftext|>


<commit_before>#!/usr/bin/env python
# encoding: utf-8

from __future__ import (absolute_import, division,
                        print_function, unicode_literals)

import os, sys, re
import logging
import argparse
import collections
import numpy
import time

import TGraph
import TNode
import Node_path
import Node_alignment
from GraphCycleException import GraphCycleException
import Topological_sort
import DP_matrix

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

class Gene_splice_modeler:

    """
    Builds supertranscipts.

    object instance members:

        gene_id : str

        alignments : list of Node_alignment objects

    """

    def __init__(self, gene_id, node_path_obj_list):

        """
        initialize alignments list with simple single 'alignment' objects with
        each path as an individual alignment with just its path nodes.

        params:

        gene_id : str

        node_path_obj_list : list of Node_path objects, each Node_path corresponding to an individual Trinity isoform

        """

        self.gene_id = gene_id
        self.alignments = list()

        logger.debug("Gene_splice_modeler inputs: {}".format(node_path_obj_list))

        for node_path_obj in node_path_obj_list:
            transcript_name = node_path_obj.get_transcript_name()
            alignment_obj = Node_alignment.Node_alignment.get_single_seq_node_alignment(node_path_obj)

            self.alignments.append(alignment_obj)

    def get_gene_id(self):
        return self.gene_id

    def build_splice_model(self):
        """
        method to construct the super transcript.

        Tries 2 approaches:
            a.  If there isn't an obvious repetitive node structure and so the graph formas a DAG,
                we build a splice graph and perform topological sorting of the nodes.
            b.  If there is some repetitive structure, we resort to performing a multiple alignment-based method to
                organize relationships among nodes in isoforms, and the multiple alignment produces the linear ordering
                for the supertranscript.

        """

        if not self.alignment_contains_repeat_node():
            # no obvious cycles
            try:
                return self.topological_order_splice_model()
            except GraphCycleException:
                # have a more complex cycle here...
                # try again w/ mult align approach.
                return self.multiple_alignment_splice_model()

        else:
            return self.multiple_alignment_splice_model()

    def alignment_contains_repeat_node(self):

        for alignment in self.alignments:
            loc_ids = set()
            for i in range(0, alignment.width()):
                node_obj = alignment.get_representative_column_node(i)
                loc_id = node_obj.get_loc_id()
                if loc_id in loc_ids:
                    return True
                loc_ids.add(loc_id)

        return False

    def topological_order_splice_model(self):
        """
        Build supertranscript using simpler topological sorting of the nodes.
        """

        logger.info("\tusing topological sort method.\n");
        gene_id = self.get_gene_id()
        generic_name = "^^{}^^".format(gene_id)

        ## make a generic graph.
        graph = TGraph.TGraph(generic_name)
        for alignment in self.alignments:
            logger.debug("topological_order_splice_model, input alignment: " + str(alignment))
            node_list = alignment.get_aligned_nodes()[0] # should be unaligned here, so just ordered path list.
            transcript_name = alignment.get_transcript_names()[0]
            logger.debug("topological_order_splice_model, node list: " + str(node_list))
            for i in range(0, len(node_list)):
                node_obj = node_list[i]
                loc_id = node_obj.get_loc_id()
                generic_node = graph.get_node(transcript_name, loc_id, node_obj.get_seq()) # rely on Node class caching system
                logger.debug("generic node: " + str(generic_node))

                if i > 0:
                    # set prev node info
                    prev_node_obj = node_list[i-1]
                    prev_generic_node = graph.get_node(transcript_name, prev_node_obj.get_loc_id(), prev_node_obj.get_seq())
                    generic_node.add_prev_node(prev_generic_node)

                if i < len(node_list) - 1:
                    next_node_obj = node_list[i+1]
                    next_generic_node = graph.get_node(transcript_name, next_node_obj.get_loc_id(), next_node_obj.get_seq())
                    generic_node.add_next_node(next_generic_node)

        logger.debug("Before sorting nodes: " + str(graph))

        topologically_sorted_nodes = Topological_sort.Topological_sort.topologically_sort(graph.get_all_nodes())

        logger.debug("Topologically sorted nodes: " + str(topologically_sorted_nodes))

        # index loc node ids
        aligned_loc_id_pos = dict()
        for i in range(0, len(topologically_sorted_nodes)):
            loc_id = topologically_sorted_nodes[i].get_loc_id()
            aligned_loc_id_pos[loc_id] = i

        new_alignments = list()
        transcript_ids = list()
        for alignment in self.alignments:
            transcript_ids.append(alignment.get_transcript_names()[0]) # really should only be one here.
            new_alignment = [None for i in topologically_sorted_nodes]
            for node in alignment.get_aligned_nodes()[0]:
                loc_id = node.get_loc_id()
                new_idx = aligned_loc_id_pos[loc_id]
                new_alignment[new_idx] = node
            new_alignments.append(new_alignment)

        splice_graph_model = Node_alignment.Node_alignment(transcript_ids, new_alignments)

        logger.debug("Splice graph model: " + str(splice_graph_model))

        return splice_graph_model

    def multiple_alignment_splice_model(self):
        """
        Multiple alignment algorithm for dealing with repeat nodes:
        For each best matching pair of transcripts (or aligned transcripts),
        perform alignment, and replace aligned pair with a single alignment object.
        """

        logger.info("\tusing mult alignment method.\n");

        alignments = self.alignments

        if len(alignments) == 1:
            # no alignment is necessary.
            return alignments[0]

        # determine initial path similarity
        similarity_matrix = Gene_splice_modeler.compute_similarity_matrix(self.alignments)
        logger.debug("Similarity matrix:\n" + str(similarity_matrix))

        ## build multiple alignment in a hierarchical way
        while len(similarity_matrix) > 1:

            # set diag to -1 to avoid any zero ties w/ self-vals
            for i in range(0,len(alignments)):
                similarity_matrix[ i ][ i ] = -1

            ## find best pair
            best_pair_idx = int(numpy.argmax(similarity_matrix))
            num_alignments = len(similarity_matrix)
            best_pair_idx_1 = int(best_pair_idx / num_alignments)
            best_pair_idx_2 = best_pair_idx % num_alignments

            ## merge pair into single alignment
            align_a = alignments[ best_pair_idx_1 ]
            align_b = alignments[ best_pair_idx_2 ]

            align_merged = Gene_splice_modeler.merge_alignments(align_a, align_b)

            ## recompute matrix
            new_alignment_list = list()
            for i in range(0, len(alignments)):
                if i not in (best_pair_idx_1, best_pair_idx_2):
                    new_alignment_list.append(alignments[ i ])
            new_alignment_list.append(align_merged)

            alignments = new_alignment_list

            logger.debug("\nUpdated alignments:\n" + str(alignments))

            similarity_matrix = Gene_splice_modeler.compute_similarity_matrix(alignments)
            logger.debug("Similarity matrix:\n" + str(similarity_matrix))

        if len(alignments) > 1:
            raise RuntimeError("Error, should only have one alignment but have {} alignments after merge".format(len(alignments)))

        return alignments[0]

    @staticmethod
    def compute_similarity_matrix(alignments_list):
        """
        similarity matrix indicates number of shared nodes between each pair of isoforms.
        """

        num_alignments = len(alignments_list)
        sim_matrix = numpy.zeros( (num_alignments, num_alignments), dtype='int_' )

        for i in range(0, num_alignments-1):
            align_i = alignments_list[i]
            for j in range(i+1, num_alignments):
                align_j = alignments_list[j]

                common_nodes = Node_alignment.Node_alignment.compute_number_common_nodes(align_i, align_j)
                num_common_nodes = len(common_nodes)

                sim_matrix[ i ][ j ] = num_common_nodes

        return sim_matrix

    @staticmethod
    def merge_alignments(align_a, align_b):
        """
        Computes a mismatch-free multiple alignment (just matches and gaps) between two Node_alignment objects

        returns single Node_alignment object containing the contents of aligned align_a and align_b as aligned.

        """

        logger.debug("Merging alignments {} and {}".format(align_a, align_b))

        ## ensure the transcripts are disjoint
        transcript_names_align_A = set(align_a.get_transcript_names())
        transcript_names_align_B = set(align_b.get_transcript_names())

        if not set.isdisjoint(transcript_names_align_A, transcript_names_align_B):
            raise RuntimeError("Error, transcripts in alignments to merge are not disjoint: {} and {}".format(transcript_names_align_A, transcript_names_align_B))

        width_a = align_a.width()
        width_b = align_b.width()

        # do global alignments w/o penalizing end gaps
        dp_matrix = DP_matrix.DP_matrix.build_DP_matrix(width_a, width_b)

        # put align B across top (cols) and align A at side (row)
        # init the matrix zero rows
        for i in range(1, width_a+1):
            dp_matrix[ i ][ 0 ]['bt'] = 'DEL_B' # UP
        for j in range(1, width_b+1):
            dp_matrix[ 0 ][ j ]['bt'] = 'DEL_A' # LEFT

        # score the DP matrix
        for i in range(1, width_a+1):
            for j in range(1, width_b+1):

                score_cell_match = Gene_splice_modeler.get_match_score(align_a, i-1, align_b, j-1) # score matrix is 1-based, align is 0-based

                score_diag = dp_matrix[ i-1 ][ j-1 ]['score'] + score_cell_match

                score_del_a = dp_matrix[ i ][ j-1 ]['score']

                score_del_b = dp_matrix[ i-1 ][ j ]['score']

                if score_cell_match > 0 and score_diag >= score_del_a and score_diag >= score_del_b:
                    dp_matrix[ i ][ j ]['score'] = score_diag
                    dp_matrix[ i ][ j ]['bt'] = 'DIAG'
                elif score_del_a >= score_del_b:
                    dp_matrix[ i ][ j ]['score'] = score_del_a
                    dp_matrix[ i ][ j ]['bt'] = 'DEL_A'
                else:
                    dp_matrix[ i ][ j ]['score'] = score_del_b
                    dp_matrix[ i ][ j ]['bt'] = 'DEL_B'

        #logger.debug("DP_matrix:\n" + DP_matrix.toString(dp_matrix))

        """
        # get max score
        max_score = 0
        max_i = -1
        max_j = -1
        for i in range(0,width_a+1):
            score = dp_matrix[ i ][ width_b ]['score']
            if score > max_score:
                max_score = score
                max_i = i
                max_j = width_b
        for j in range(0, width_b+1):
            score = dp_matrix[ width_a ][ j ]['score']
            if score > max_score:
                max_score = score
                max_i = width_a
                max_j = j

        logger.info("found max score {} at position: ({},{})".format(max_score, max_i, max_j))
        """

        # keep as global alignment
        max_i = width_a
        max_j = width_b

        # backtrack
        i = max_i
        j = max_j
        all_merged_alignment_nodes_list = list()
        while i > 0 or j > 0:
            score_struct = dp_matrix[ i ][ j ]

            nodes_align_a = align_a.get_node_LIST_at_column_pos(i-1) # again, remember align has zero-based coords, whereas dp_matrix is 1-based
            nodes_align_b = align_b.get_node_LIST_at_column_pos(j-1)

            align_nodes = list()

            bt_dir = score_struct['bt']

            #logger.debug("backtrack-dir: " + bt_dir)

            if bt_dir == 'DIAG':
                i -= 1
                j -= 1
                align_nodes = nodes_align_a + nodes_align_b

            elif bt_dir == 'DEL_B':   # UP
                i -= 1

                align_nodes += nodes_align_a
                for x in range(0,len(nodes_align_b)):
                    align_nodes.append(None)

            elif bt_dir == 'DEL_A':  # LEFT
                j -= 1

                for x in range(0,len(nodes_align_a)):
                    align_nodes.append(None)
                align_nodes += nodes_align_b

            else:
                raise RuntimeError("bt: ({},{}), bt_dir not defined".format(i,j))

            all_merged_alignment_nodes_list.append(align_nodes)

        all_merged_alignment_nodes_list.reverse()
        logger.debug("Merged alignment nodes list: " + str(all_merged_alignment_nodes_list) )

        # prep merged alignment obj
        merged_transcript_name_list = align_a.get_transcript_names() + align_b.get_transcript_names()
        node_obj_matrix = list()
        # interate through each node list, reorganize into a matrix
        for i in range(0,len(merged_transcript_name_list)):
            row = list()
            for node_obj_list in all_merged_alignment_nodes_list:
                row.append(node_obj_list[i])
            node_obj_matrix.append(row)

        logger.debug("merged alignment node matrix:\n" + str(node_obj_matrix))

        merged_alignment_obj = Node_alignment.Node_alignment(merged_transcript_name_list, node_obj_matrix)

        logger.debug("merged alignment obj:\n" + str(merged_alignment_obj))

        #sys.exit(1) # DEBUG

        return merged_alignment_obj

    @staticmethod
    def get_match_score(align_a, idx_a, align_b, idx_b):
        """
        just determines if indices in two transcripts have the same node identifier
        """

        node_set_a = align_a.get_node_set_at_column_pos(idx_a)
        node_set_b = align_b.get_node_set_at_column_pos(idx_b)

        node_set_a = Node_alignment.Node_alignment.get_node_loc_ids(node_set_a)
        node_set_b = Node_alignment.Node_alignment.get_node_loc_ids(node_set_b)

        if (set.intersection(node_set_a, node_set_b)):
            return 1 # match
        else:
            return 0 # no match

    @staticmethod
    def write_malign(gene_name, malign_dict, ofh, align_width=100):
        """
        writes the multiply aligned isoform sequences to an output filehandle
        """

        transcript_names = malign_dict.keys()

        alignment_length = len(malign_dict[ transcript_names[ 0 ] ])

        align_start = 0

        align_text = ""

        while align_start < alignment_length:
            for transcript_name in transcript_names:
                align_region = malign_dict[ transcript_name ][ align_start : min(alignment_length, align_start + align_width) ]
                align_text += transcript_name + "\t" + align_region + "\n"
            align_text += "\n" # spacer between alignment blocks
            align_start += align_width

        ofh.write("// {}\n\n{}\n".format(gene_name, align_text))

<commit_msg>less verbose in info mode<commit_after>#!/usr/bin/env python
# encoding: utf-8

from __future__ import (absolute_import, division,
                        print_function, unicode_literals)

import os, sys, re
import logging
import argparse
import collections
import numpy
import time

import TGraph
import TNode
import Node_path
import Node_alignment
from GraphCycleException import GraphCycleException
import Topological_sort
import DP_matrix

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

class Gene_splice_modeler:

    """
    Builds supertranscipts.

    object instance members:

        gene_id : str

        alignments : list of Node_alignment objects

    """

    def __init__(self, gene_id, node_path_obj_list):

        """
        initialize alignments list with simple single 'alignment' objects with
        each path as an individual alignment with just its path nodes.

        params:

        gene_id : str

        node_path_obj_list : list of Node_path objects, each Node_path corresponding to an individual Trinity isoform

        """

        self.gene_id = gene_id
        self.alignments = list()

        logger.debug("Gene_splice_modeler inputs: {}".format(node_path_obj_list))

        for node_path_obj in node_path_obj_list:
            transcript_name = node_path_obj.get_transcript_name()
            alignment_obj = Node_alignment.Node_alignment.get_single_seq_node_alignment(node_path_obj)

            self.alignments.append(alignment_obj)

    def get_gene_id(self):
        return self.gene_id

    def build_splice_model(self):
        """
        method to construct the super transcript.

        Tries 2 approaches:
            a.  If there isn't an obvious repetitive node structure and so the graph formas a DAG,
                we build a splice graph and perform topological sorting of the nodes.
            b.  If there is some repetitive structure, we resort to performing a multiple alignment-based method to
                organize relationships among nodes in isoforms, and the multiple alignment produces the linear ordering
                for the supertranscript.

        """

        if not self.alignment_contains_repeat_node():
            # no obvious cycles
            try:
                return self.topological_order_splice_model()
            except GraphCycleException:
                # have a more complex cycle here...
                # try again w/ mult align approach.
                return self.multiple_alignment_splice_model()

        else:
            return self.multiple_alignment_splice_model()

    def alignment_contains_repeat_node(self):

        for alignment in self.alignments:
            loc_ids = set()
            for i in range(0, alignment.width()):
                node_obj = alignment.get_representative_column_node(i)
                loc_id = node_obj.get_loc_id()
                if loc_id in loc_ids:
                    return True
                loc_ids.add(loc_id)

        return False

    def topological_order_splice_model(self):
        """
        Build supertranscript using simpler topological sorting of the nodes.
        """

        logger.debug("\tusing topological sort method.\n");
        gene_id = self.get_gene_id()
        generic_name = "^^{}^^".format(gene_id)

        ## make a generic graph.
        graph = TGraph.TGraph(generic_name)
        for alignment in self.alignments:
            logger.debug("topological_order_splice_model, input alignment: " + str(alignment))
            node_list = alignment.get_aligned_nodes()[0] # should be unaligned here, so just ordered path list.
            transcript_name = alignment.get_transcript_names()[0]
            logger.debug("topological_order_splice_model, node list: " + str(node_list))
            for i in range(0, len(node_list)):
                node_obj = node_list[i]
                loc_id = node_obj.get_loc_id()
                generic_node = graph.get_node(transcript_name, loc_id, node_obj.get_seq()) # rely on Node class caching system
                logger.debug("generic node: " + str(generic_node))

                if i > 0:
                    # set prev node info
                    prev_node_obj = node_list[i-1]
                    prev_generic_node = graph.get_node(transcript_name, prev_node_obj.get_loc_id(), prev_node_obj.get_seq())
                    generic_node.add_prev_node(prev_generic_node)

                if i < len(node_list) - 1:
                    next_node_obj = node_list[i+1]
                    next_generic_node = graph.get_node(transcript_name, next_node_obj.get_loc_id(), next_node_obj.get_seq())
                    generic_node.add_next_node(next_generic_node)

        logger.debug("Before sorting nodes: " + str(graph))

        topologically_sorted_nodes = Topological_sort.Topological_sort.topologically_sort(graph.get_all_nodes())

        logger.debug("Topologically sorted nodes: " + str(topologically_sorted_nodes))

        # index loc node ids
        aligned_loc_id_pos = dict()
        for i in range(0, len(topologically_sorted_nodes)):
            loc_id = topologically_sorted_nodes[i].get_loc_id()
            aligned_loc_id_pos[loc_id] = i

        new_alignments = list()
        transcript_ids = list()
        for alignment in self.alignments:
            transcript_ids.append(alignment.get_transcript_names()[0]) # really should only be one here.
            new_alignment = [None for i in topologically_sorted_nodes]
            for node in alignment.get_aligned_nodes()[0]:
                loc_id = node.get_loc_id()
                new_idx = aligned_loc_id_pos[loc_id]
                new_alignment[new_idx] = node
            new_alignments.append(new_alignment)

        splice_graph_model = Node_alignment.Node_alignment(transcript_ids, new_alignments)

        logger.debug("Splice graph model: " + str(splice_graph_model))

        return splice_graph_model

    def multiple_alignment_splice_model(self):
        """
        Multiple alignment algorithm for dealing with repeat nodes:
        For each best matching pair of transcripts (or aligned transcripts),
        perform alignment, and replace aligned pair with a single alignment object.
        """

        logger.debug("\tusing mult alignment method.\n");

        alignments = self.alignments

        if len(alignments) == 1:
            # no alignment is necessary.
            return alignments[0]

        # determine initial path similarity
        similarity_matrix = Gene_splice_modeler.compute_similarity_matrix(self.alignments)
        logger.debug("Similarity matrix:\n" + str(similarity_matrix))

        ## build multiple alignment in a hierarchical way
        while len(similarity_matrix) > 1:

            # set diag to -1 to avoid any zero ties w/ self-vals
            for i in range(0,len(alignments)):
                similarity_matrix[ i ][ i ] = -1

            ## find best pair
            best_pair_idx = int(numpy.argmax(similarity_matrix))
            num_alignments = len(similarity_matrix)
            best_pair_idx_1 = int(best_pair_idx / num_alignments)
            best_pair_idx_2 = best_pair_idx % num_alignments

            ## merge pair into single alignment
            align_a = alignments[ best_pair_idx_1 ]
            align_b = alignments[ best_pair_idx_2 ]

            align_merged = Gene_splice_modeler.merge_alignments(align_a, align_b)

            ## recompute matrix
            new_alignment_list = list()
            for i in range(0, len(alignments)):
                if i not in (best_pair_idx_1, best_pair_idx_2):
                    new_alignment_list.append(alignments[ i ])
            new_alignment_list.append(align_merged)

            alignments = new_alignment_list

            logger.debug("\nUpdated alignments:\n" + str(alignments))

            similarity_matrix = Gene_splice_modeler.compute_similarity_matrix(alignments)
            logger.debug("Similarity matrix:\n" + str(similarity_matrix))

        if len(alignments) > 1:
            raise RuntimeError("Error, should only have one alignment but have {} alignments after merge".format(len(alignments)))

        return alignments[0]

    @staticmethod
    def compute_similarity_matrix(alignments_list):
        """
        similarity matrix indicates number of shared nodes between each pair of isoforms.
        """

        num_alignments = len(alignments_list)
        sim_matrix = numpy.zeros( (num_alignments, num_alignments), dtype='int_' )

        for i in range(0, num_alignments-1):
            align_i = alignments_list[i]
            for j in range(i+1, num_alignments):
                align_j = alignments_list[j]

                common_nodes = Node_alignment.Node_alignment.compute_number_common_nodes(align_i, align_j)
                num_common_nodes = len(common_nodes)

                sim_matrix[ i ][ j ] = num_common_nodes

        return sim_matrix

    @staticmethod
    def merge_alignments(align_a, align_b):
        """
        Computes a mismatch-free multiple alignment (just matches and gaps) between two Node_alignment objects

        returns single Node_alignment object containing the contents of aligned align_a and align_b as aligned.

        """

        logger.debug("Merging alignments {} and {}".format(align_a, align_b))

        ## ensure the transcripts are disjoint
        transcript_names_align_A = set(align_a.get_transcript_names())
        transcript_names_align_B = set(align_b.get_transcript_names())

        if not set.isdisjoint(transcript_names_align_A, transcript_names_align_B):
            raise RuntimeError("Error, transcripts in alignments to merge are not disjoint: {} and {}".format(transcript_names_align_A, transcript_names_align_B))

        width_a = align_a.width()
        width_b = align_b.width()

        # do global alignments w/o penalizing end gaps
        dp_matrix = DP_matrix.DP_matrix.build_DP_matrix(width_a, width_b)

        # put align B across top (cols) and align A at side (row)
        # init the matrix zero rows
        for i in range(1, width_a+1):
            dp_matrix[ i ][ 0 ]['bt'] = 'DEL_B' # UP
        for j in range(1, width_b+1):
            dp_matrix[ 0 ][ j ]['bt'] = 'DEL_A' # LEFT

        # score the DP matrix
        for i in range(1, width_a+1):
            for j in range(1, width_b+1):

                score_cell_match = Gene_splice_modeler.get_match_score(align_a, i-1, align_b, j-1) # score matrix is 1-based, align is 0-based

                score_diag = dp_matrix[ i-1 ][ j-1 ]['score'] + score_cell_match

                score_del_a = dp_matrix[ i ][ j-1 ]['score']

                score_del_b = dp_matrix[ i-1 ][ j ]['score']

                if score_cell_match > 0 and score_diag >= score_del_a and score_diag >= score_del_b:
                    dp_matrix[ i ][ j ]['score'] = score_diag
                    dp_matrix[ i ][ j ]['bt'] = 'DIAG'
                elif score_del_a >= score_del_b:
                    dp_matrix[ i ][ j ]['score'] = score_del_a
                    dp_matrix[ i ][ j ]['bt'] = 'DEL_A'
                else:
                    dp_matrix[ i ][ j ]['score'] = score_del_b
                    dp_matrix[ i ][ j ]['bt'] = 'DEL_B'

        #logger.debug("DP_matrix:\n" + DP_matrix.toString(dp_matrix))

        """
        # get max score
        max_score = 0
        max_i = -1
        max_j = -1
        for i in range(0,width_a+1):
            score = dp_matrix[ i ][ width_b ]['score']
            if score > max_score:
                max_score = score
                max_i = i
                max_j = width_b
        for j in range(0, width_b+1):
            score = dp_matrix[ width_a ][ j ]['score']
            if score > max_score:
                max_score = score
                max_i = width_a
                max_j = j

        logger.info("found max score {} at position: ({},{})".format(max_score, max_i, max_j))
        """

        # keep as global alignment
        max_i = width_a
        max_j = width_b

        # backtrack
        i = max_i
        j = max_j
        all_merged_alignment_nodes_list = list()
        while i > 0 or j > 0:
            score_struct = dp_matrix[ i ][ j ]

            nodes_align_a = align_a.get_node_LIST_at_column_pos(i-1) # again, remember align has zero-based coords, whereas dp_matrix is 1-based
            nodes_align_b = align_b.get_node_LIST_at_column_pos(j-1)

            align_nodes = list()

            bt_dir = score_struct['bt']

            #logger.debug("backtrack-dir: " + bt_dir)

            if bt_dir == 'DIAG':
                i -= 1
                j -= 1
                align_nodes = nodes_align_a + nodes_align_b

            elif bt_dir == 'DEL_B':   # UP
                i -= 1

                align_nodes += nodes_align_a
                for x in range(0,len(nodes_align_b)):
                    align_nodes.append(None)

            elif bt_dir == 'DEL_A':  # LEFT
                j -= 1

                for x in range(0,len(nodes_align_a)):
                    align_nodes.append(None)
                align_nodes += nodes_align_b

            else:
                raise RuntimeError("bt: ({},{}), bt_dir not defined".format(i,j))

            all_merged_alignment_nodes_list.append(align_nodes)

        all_merged_alignment_nodes_list.reverse()
        logger.debug("Merged alignment nodes list: " + str(all_merged_alignment_nodes_list) )

        # prep merged alignment obj
        merged_transcript_name_list = align_a.get_transcript_names() + align_b.get_transcript_names()
        node_obj_matrix = list()
        # interate through each node list, reorganize into a matrix
        for i in range(0,len(merged_transcript_name_list)):
            row = list()
            for node_obj_list in all_merged_alignment_nodes_list:
                row.append(node_obj_list[i])
            node_obj_matrix.append(row)

        logger.debug("merged alignment node matrix:\n" + str(node_obj_matrix))

        merged_alignment_obj = Node_alignment.Node_alignment(merged_transcript_name_list, node_obj_matrix)

        logger.debug("merged alignment obj:\n" + str(merged_alignment_obj))

        #sys.exit(1) # DEBUG

        return merged_alignment_obj

    @staticmethod
    def get_match_score(align_a, idx_a, align_b, idx_b):
        """
        just determines if indices in two transcripts have the same node identifier
        """

        node_set_a = align_a.get_node_set_at_column_pos(idx_a)
        node_set_b = align_b.get_node_set_at_column_pos(idx_b)

        node_set_a = Node_alignment.Node_alignment.get_node_loc_ids(node_set_a)
        node_set_b = Node_alignment.Node_alignment.get_node_loc_ids(node_set_b)

        if (set.intersection(node_set_a, node_set_b)):
            return 1 # match
        else:
            return 0 # no match

    @staticmethod
    def write_malign(gene_name, malign_dict, ofh, align_width=100):
        """
        writes the multiply aligned isoform sequences to an output filehandle
        """

        transcript_names = malign_dict.keys()

        alignment_length = len(malign_dict[ transcript_names[ 0 ] ])

        align_start = 0

        align_text = ""

        while align_start < alignment_length:
            for transcript_name in transcript_names:
                align_region = malign_dict[ transcript_name ][ align_start : min(alignment_length, align_start + align_width) ]
                align_text += transcript_name + "\t" + align_region + "\n"
            align_text += "\n" # spacer between alignment blocks
            align_start += align_width

        ofh.write("// {}\n\n{}\n".format(gene_name, align_text))

<|endoftext|>


<commit_before>"""This module implements a collection of unit tests for
the async_model_actions.py module."""

import mock
import unittest
import uuid

from ..async_model_actions import AsyncModelRetriever
from ..async_model_actions import AsyncModelsRetriever
from ..async_model_actions import AsyncPersister
from ..async_model_actions import AsyncUserStoreHealthCheck
from ..model import Model
from .. import async_model_actions  # noqa, needed for patching using relative path

class Patcher(object):

    def __init__(self, patcher):
        object.__init__(self)
        self._patcher = patcher

    def __enter__(self):
        self._patcher.start()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self._patcher.stop()

class AsyncUserStoreActionPatcher(Patcher):

    def __init__(self, is_ok, is_conflict, models, _id, _rev):

        def fetch_patch(ausa, callback):
            callback(is_ok, is_conflict, models, _id, _rev, ausa)

        patcher = mock.patch(
            __name__ + ".async_model_actions._AsyncUserStoreAction.fetch",
            fetch_patch)

        Patcher.__init__(self, patcher)

class AsyncModelRetrieverUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncModelRetriever class."""

    def test_ctr_with_async_state(self):
        design_doc = uuid.uuid4().hex
        key = uuid.uuid4().hex
        async_state = uuid.uuid4().hex

        amr = AsyncModelRetriever(
            design_doc,
            key,
            async_state)

        self.assertTrue(amr.design_doc is design_doc)
        self.assertTrue(amr.key is key)
        self.assertTrue(amr.async_state is async_state)

    def test_error(self):
        the_is_ok = False
        the_is_conflict = False
        the_models = None
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            key = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelRetriever(
                design_doc,
                key,
                async_state)

            def on_the_amr_fetch_done(is_ok, model, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(model is None)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_not_found(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = []
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            key = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelRetriever(
                design_doc,
                key,
                async_state)

            def on_the_amr_fetch_done(is_ok, model, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(model is None)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_all_good(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = [Model()]
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            key = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelRetriever(
                design_doc,
                key,
                async_state)

            def on_the_amr_fetch_done(is_ok, model, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(model is the_models[0])
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_implementation_for_create_model_from_doc_required(self):
        """AsyncModelRetriever is an abstract base class.
        Concrete derived classes must provide an implementation
        of create_model_from_doc() otherwise an exception is
        raised. This test verifies the exception is raised
        when an implementation is not provided."""

        class Bad(AsyncModelRetriever):
            pass

        bad = Bad(None, None, None)
        with self.assertRaises(NotImplementedError):
            bad.create_model_from_doc({})

        class Good(Bad):
            def create_model_from_doc(self, doc):
                pass

        good = Good(None, None, None)
        good.create_model_from_doc({})

class AsyncModelsRetrieverUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncModelsRetriever class."""

    def test_ctr(self):
        design_doc = uuid.uuid4().hex
        async_state = uuid.uuid4().hex

        amr = AsyncModelsRetriever(
            design_doc,
            async_state)

        self.assertTrue(amr.design_doc is design_doc)
        self.assertTrue(amr.async_state is async_state)

    def test_error(self):
        the_is_ok = False
        the_is_conflict = False
        the_models = None
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelsRetriever(
                design_doc,
                async_state)

            def on_the_amr_fetch_done(is_ok, models, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(models is the_models)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_all_good(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = [Model()]
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelsRetriever(
                design_doc,
                async_state)

            def on_the_amr_fetch_done(is_ok, models, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(models is the_models)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_implementation_for_create_model_from_doc_required(self):
        """AsyncModelsRetriever is an abstract base class.
        Concrete derived classes must provide an implementation
        of create_model_from_doc() otherwise an exception is
        raised. This test verifies the exception is raised
        when an implementation is not provided."""

        class Bad(AsyncModelsRetriever):
            pass

        bad = Bad(None, None)
        with self.assertRaises(NotImplementedError):
            bad.create_model_from_doc({})

        class Good(Bad):
            def create_model_from_doc(self, doc):
                pass

        good = Good(None, None)
        good.create_model_from_doc({})

class AsyncPersisterUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncPersister class."""

    def test_ctr(self):
        model = mock.Mock()
        model_as_dict_for_store_args = mock.Mock()
        async_state = mock.Mock()

        ap = AsyncPersister(model, model_as_dict_for_store_args, async_state)

        self.assertTrue(ap.model is model)
        self.assertTrue(ap.model_as_dict_for_store_args is model_as_dict_for_store_args)
        self.assertTrue(ap.async_state is async_state)

    def test_create_new(self):
        """Verify AsyncPersister.persist() when creating a new document
        in the User Store.
        """

        the_model = Model()
        the_model_as_dict_args = []
        the_ap = AsyncPersister(the_model, the_model_as_dict_args, None)

        the_id = uuid.uuid4().hex
        the_rev = uuid.uuid4().hex

        self.assertIsNone(the_model._id)
        self.assertNotEqual(the_model._id, the_id)
        self.assertIsNone(the_model._rev)
        self.assertNotEqual(the_model._rev, the_rev)

        def callback(is_ok, is_conflict, ap):
            self.assertTrue(is_ok)
            self.assertIsNotNone(the_model._id)
            self.assertEqual(the_model._id, the_id)
            self.assertIsNotNone(the_model._rev)
            self.assertEqual(the_model._rev, the_rev)
            self.assertFalse(is_conflict)
            self.assertTrue(ap is the_ap)

        with AsyncUserStoreActionPatcher(True, False, [], the_id, the_rev):
            the_ap.persist(callback)

    def test_update_existing(self):
        """Verify AsyncPersister.persist() when updating a document
        that already exists in the User Store.
        """

        the_model = Model(doc={"_id": uuid.uuid4().hex, "_rev": uuid.uuid4().hex})
        the_model_as_dict_args = []
        the_ap = AsyncPersister(the_model, the_model_as_dict_args, None)

        the_next_rev = uuid.uuid4().hex
        self.assertIsNotNone(the_model._rev)
        self.assertNotEqual(the_model._rev, the_next_rev)

        def callback(is_ok, is_conflict, ap):
            self.assertTrue(is_ok)
            self.assertIsNotNone(the_model._rev)
            self.assertEqual(the_model._rev, the_next_rev)
            self.assertFalse(is_conflict)
            self.assertTrue(ap, the_ap)

        with AsyncUserStoreActionPatcher(True, False, [], the_model._id, the_next_rev):
            the_ap.persist(callback)

class AsyncUserStoreHealthCheckCheckUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncUserStoreHealthCheck class."""

    def test_ctr_with_async_state(self):
        async_state = uuid.uuid4().hex

        aushc = AsyncUserStoreHealthCheck(async_state)

        self.assertTrue(aushc.async_state is async_state)

    def test_ctr_without_async_state(self):
        aushc = AsyncUserStoreHealthCheck()

        self.assertIsNone(aushc.async_state)

    def test_all_good(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = []
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            the_aushc = AsyncUserStoreHealthCheck()

            def callback(is_ok, aushc):
                self.assertTrue(is_ok)
                self.assertTrue(aushc is the_aushc)

            the_aushc.check(callback)

    def test_unreachable_user_store(self):
        the_is_ok = False
        the_is_conflict = False
        the_models = []
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            the_aushc = AsyncUserStoreHealthCheck()

            def callback(is_ok, aushc):
                self.assertFalse(is_ok)
                self.assertTrue(aushc is the_aushc)

            the_aushc.check(callback)
<commit_msg>simplify async_model_actions_unit_tests.py<commit_after>"""This module implements a collection of unit tests for
the async_model_actions.py module."""

import mock
import unittest
import uuid

from ..async_model_actions import AsyncModelRetriever
from ..async_model_actions import AsyncModelsRetriever
from ..async_model_actions import AsyncPersister
from ..async_model_actions import AsyncUserStoreHealthCheck
from ..model import Model
from .. import async_model_actions  # noqa, needed for patching using relative path

class AsyncUserStoreActionPatcher(object):

    def __init__(self, is_ok, is_conflict, models, _id, _rev):

        def fetch_patch(ausa, callback):
            callback(is_ok, is_conflict, models, _id, _rev, ausa)

        self._patcher = mock.patch(
            __name__ + ".async_model_actions._AsyncUserStoreAction.fetch",
            fetch_patch)

    def __enter__(self):
        self._patcher.start()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self._patcher.stop()

class AsyncModelRetrieverUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncModelRetriever class."""

    def test_ctr_with_async_state(self):
        design_doc = uuid.uuid4().hex
        key = uuid.uuid4().hex
        async_state = uuid.uuid4().hex

        amr = AsyncModelRetriever(
            design_doc,
            key,
            async_state)

        self.assertTrue(amr.design_doc is design_doc)
        self.assertTrue(amr.key is key)
        self.assertTrue(amr.async_state is async_state)

    def test_error(self):
        the_is_ok = False
        the_is_conflict = False
        the_models = None
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            key = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelRetriever(
                design_doc,
                key,
                async_state)

            def on_the_amr_fetch_done(is_ok, model, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(model is None)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_not_found(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = []
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            key = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelRetriever(
                design_doc,
                key,
                async_state)

            def on_the_amr_fetch_done(is_ok, model, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(model is None)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_all_good(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = [Model()]
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            key = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelRetriever(
                design_doc,
                key,
                async_state)

            def on_the_amr_fetch_done(is_ok, model, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(model is the_models[0])
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_implementation_for_create_model_from_doc_required(self):
        """AsyncModelRetriever is an abstract base class.
        Concrete derived classes must provide an implementation
        of create_model_from_doc() otherwise an exception is
        raised. This test verifies the exception is raised
        when an implementation is not provided."""

        class Bad(AsyncModelRetriever):
            pass

        bad = Bad(None, None, None)
        with self.assertRaises(NotImplementedError):
            bad.create_model_from_doc({})

        class Good(Bad):
            def create_model_from_doc(self, doc):
                pass

        good = Good(None, None, None)
        good.create_model_from_doc({})

class AsyncModelsRetrieverUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncModelsRetriever class."""

    def test_ctr(self):
        design_doc = uuid.uuid4().hex
        async_state = uuid.uuid4().hex

        amr = AsyncModelsRetriever(
            design_doc,
            async_state)

        self.assertTrue(amr.design_doc is design_doc)
        self.assertTrue(amr.async_state is async_state)

    def test_error(self):
        the_is_ok = False
        the_is_conflict = False
        the_models = None
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelsRetriever(
                design_doc,
                async_state)

            def on_the_amr_fetch_done(is_ok, models, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(models is the_models)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_all_good(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = [Model()]
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            design_doc = uuid.uuid4().hex
            async_state = uuid.uuid4().hex

            the_amr = AsyncModelsRetriever(
                design_doc,
                async_state)

            def on_the_amr_fetch_done(is_ok, models, amr):
                self.assertEqual(is_ok, the_is_ok)
                self.assertTrue(models is the_models)
                self.assertTrue(amr is the_amr)

            the_amr.fetch(on_the_amr_fetch_done)

    def test_implementation_for_create_model_from_doc_required(self):
        """AsyncModelsRetriever is an abstract base class.
        Concrete derived classes must provide an implementation
        of create_model_from_doc() otherwise an exception is
        raised. This test verifies the exception is raised
        when an implementation is not provided."""

        class Bad(AsyncModelsRetriever):
            pass

        bad = Bad(None, None)
        with self.assertRaises(NotImplementedError):
            bad.create_model_from_doc({})

        class Good(Bad):
            def create_model_from_doc(self, doc):
                pass

        good = Good(None, None)
        good.create_model_from_doc({})

class AsyncPersisterUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncPersister class."""

    def test_ctr(self):
        model = mock.Mock()
        model_as_dict_for_store_args = mock.Mock()
        async_state = mock.Mock()

        ap = AsyncPersister(model, model_as_dict_for_store_args, async_state)

        self.assertTrue(ap.model is model)
        self.assertTrue(ap.model_as_dict_for_store_args is model_as_dict_for_store_args)
        self.assertTrue(ap.async_state is async_state)

    def test_create_new(self):
        """Verify AsyncPersister.persist() when creating a new document
        in the User Store.
        """

        the_model = Model()
        the_model_as_dict_args = []
        the_ap = AsyncPersister(the_model, the_model_as_dict_args, None)

        the_id = uuid.uuid4().hex
        the_rev = uuid.uuid4().hex

        self.assertIsNone(the_model._id)
        self.assertNotEqual(the_model._id, the_id)
        self.assertIsNone(the_model._rev)
        self.assertNotEqual(the_model._rev, the_rev)

        def callback(is_ok, is_conflict, ap):
            self.assertTrue(is_ok)
            self.assertIsNotNone(the_model._id)
            self.assertEqual(the_model._id, the_id)
            self.assertIsNotNone(the_model._rev)
            self.assertEqual(the_model._rev, the_rev)
            self.assertFalse(is_conflict)
            self.assertTrue(ap is the_ap)

        with AsyncUserStoreActionPatcher(True, False, [], the_id, the_rev):
            the_ap.persist(callback)

    def test_update_existing(self):
        """Verify AsyncPersister.persist() when updating a document
        that already exists in the User Store.
        """

        the_model = Model(doc={"_id": uuid.uuid4().hex, "_rev": uuid.uuid4().hex})
        the_model_as_dict_args = []
        the_ap = AsyncPersister(the_model, the_model_as_dict_args, None)

        the_next_rev = uuid.uuid4().hex
        self.assertIsNotNone(the_model._rev)
        self.assertNotEqual(the_model._rev, the_next_rev)

        def callback(is_ok, is_conflict, ap):
            self.assertTrue(is_ok)
            self.assertIsNotNone(the_model._rev)
            self.assertEqual(the_model._rev, the_next_rev)
            self.assertFalse(is_conflict)
            self.assertTrue(ap, the_ap)

        with AsyncUserStoreActionPatcher(True, False, [], the_model._id, the_next_rev):
            the_ap.persist(callback)

class AsyncUserStoreHealthCheckCheckUnitTaseCase(unittest.TestCase):
    """A collection of unit tests for the AsyncUserStoreHealthCheck class."""

    def test_ctr_with_async_state(self):
        async_state = uuid.uuid4().hex

        aushc = AsyncUserStoreHealthCheck(async_state)

        self.assertTrue(aushc.async_state is async_state)

    def test_ctr_without_async_state(self):
        aushc = AsyncUserStoreHealthCheck()

        self.assertIsNone(aushc.async_state)

    def test_all_good(self):
        the_is_ok = True
        the_is_conflict = False
        the_models = []
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            the_aushc = AsyncUserStoreHealthCheck()

            def callback(is_ok, aushc):
                self.assertTrue(is_ok)
                self.assertTrue(aushc is the_aushc)

            the_aushc.check(callback)

    def test_unreachable_user_store(self):
        the_is_ok = False
        the_is_conflict = False
        the_models = []
        the_id = None
        the_rev = None
        with AsyncUserStoreActionPatcher(the_is_ok, the_is_conflict, the_models, the_id, the_rev):
            the_aushc = AsyncUserStoreHealthCheck()

            def callback(is_ok, aushc):
                self.assertFalse(is_ok)
                self.assertTrue(aushc is the_aushc)

            the_aushc.check(callback)
<|endoftext|>