# Path: treecat/util.py
# COUNTERS = ProfilingSet(lambda: 0)
# 
# Path: treecat/util.py
# HISTOGRAMS = ProfilingSet(Counter)
# 
# Path: treecat/util.py
# SERIES = ProfilingSet(list)
# 
# Path: treecat/util.py
# TREECAT_JIT = int(os.environ.get('TREECAT_JIT', 1))
# TREECAT_THREADS = int(
#     os.environ.get('TREECAT_THREADS', multiprocessing.cpu_count()))
# PROFILING = int(os.environ.get('TREECAT_PROFILE', 0))
# LOG_LEVEL = int(os.environ.get('TREECAT_LOG_LEVEL', logging.CRITICAL))
# LOG_FILENAME = os.environ.get('TREECAT_LOG_FILE')
# LOG_FORMAT = '%(levelname).1s %(process)d %(name)s %(message)s'
#     N, M = probs.shape
#     V = ragged_index.shape[0] - 1
#     R = ragged_index[-1]
#     N, R = data.shape
#     V = len(ragged_index) - 1
# POOL = None
#         POOL = multiprocessing.Pool(TREECAT_THREADS)
# TIMERS = defaultdict(ProfileTimer)
# COUNTERS = ProfilingSet(lambda: 0)
# HISTOGRAMS = ProfilingSet(Counter)
# SERIES = ProfilingSet(list)
# def TODO(message=''):
# def no_jit(*args, **kwargs):
# def jit_random_seed(seed):
# def set_random_seed(seed):
# def np_printoptions(**kwargs):
# def sample_from_probs(probs):
# def sample_from_probs2(probs, out=None):
# def quantize_from_probs2(probs, resolution):
# def make_ragged_index(columns):
# def make_ragged_mask(ragged_index, mask):
# def count_observations(ragged_index, data):
# def guess_counts(ragged_index, data):
# def parallel_map(fun, args):
#     def __init__(self):
#     def __enter__(self):
#     def __exit__(self, type, value, traceback):
# def profile_timed(fun):
#     def profiled_fun(*args, **kwargs):
# def get_profiling_stats():
# def log_profiling_stats():
# class ProfilingSet(defaultdict):
# class ProfileTimer(object):
# 
# Path: treecat/util.py
# TREECAT_JIT = int(os.environ.get('TREECAT_JIT', 1))
# TREECAT_THREADS = int(
#     os.environ.get('TREECAT_THREADS', multiprocessing.cpu_count()))
# PROFILING = int(os.environ.get('TREECAT_PROFILE', 0))
# LOG_LEVEL = int(os.environ.get('TREECAT_LOG_LEVEL', logging.CRITICAL))
# LOG_FILENAME = os.environ.get('TREECAT_LOG_FILE')
# LOG_FORMAT = '%(levelname).1s %(process)d %(name)s %(message)s'
#     N, M = probs.shape
#     V = ragged_index.shape[0] - 1
#     R = ragged_index[-1]
#     N, R = data.shape
#     V = len(ragged_index) - 1
# POOL = None
#         POOL = multiprocessing.Pool(TREECAT_THREADS)
# TIMERS = defaultdict(ProfileTimer)
# COUNTERS = ProfilingSet(lambda: 0)
# HISTOGRAMS = ProfilingSet(Counter)
# SERIES = ProfilingSet(list)
# def TODO(message=''):
# def no_jit(*args, **kwargs):
# def jit_random_seed(seed):
# def set_random_seed(seed):
# def np_printoptions(**kwargs):
# def sample_from_probs(probs):
# def sample_from_probs2(probs, out=None):
# def quantize_from_probs2(probs, resolution):
# def make_ragged_index(columns):
# def make_ragged_mask(ragged_index, mask):
# def count_observations(ragged_index, data):
# def guess_counts(ragged_index, data):
# def parallel_map(fun, args):
#     def __init__(self):
#     def __enter__(self):
#     def __exit__(self, type, value, traceback):
# def profile_timed(fun):
#     def profiled_fun(*args, **kwargs):
# def get_profiling_stats():
# def log_profiling_stats():
# class ProfilingSet(defaultdict):
# class ProfileTimer(object):
# 
# Path: treecat/util.py
# @jit(nopython=True, cache=True)
# def sample_from_probs(probs):
#     """Sample from a vector of non-normalized probabilitites.
# 
#     Args:
#         probs: An [M]-shaped numpy array of non-normalized probabilities.
# 
#     Returns:
#         An integer in range(M).
#     """
#     cdf = probs.cumsum()
#     return (np.random.rand() * cdf[-1] < cdf).argmax()


import logging
import numba
import numpy as np
from collections import defaultdict
from collections import deque
from scipy.sparse.csgraph import minimum_spanning_tree
from six.moves import range
from six.moves import zip
from treecat.util import COUNTERS
from treecat.util import HISTOGRAMS
from treecat.util import SERIES
from treecat.util import jit
from treecat.util import profile
from treecat.util import sample_from_probs
            if v2 != parent:
                children.append(v2)

    # Construct a flattened program.
    program = np.zeros([V + E + 1 + E + V, 4], np.int16)
    pos = 0
    for v, parent, children in reversed(nested_program):
        program[pos, :] = [OP_UP, v, 0, 0]
        pos += 1
        for child in children:
            program[pos, :] = [OP_IN, v, child, edge_dict[v, child]]
            pos += 1
    program[pos, :] = [OP_ROOT, root, 0, 0]
    pos += 1
    for v, parent, children in nested_program:
        if parent is not None:
            program[pos, :] = [OP_OUT, v, parent, edge_dict[v, parent]]
            pos += 1
        program[pos, :] = [OP_DOWN, v, 0, 0]
        pos += 1
    assert pos == program.shape[0]

    return program


# A jit_set is an array representation of a set of 16-bit integers.
# The memory layout is:
# [ size | item[1] | item[2] | ... | item[size] | unused | ... | unused ]



# Path: treecat/util.py
# COUNTERS = ProfilingSet(lambda: 0)
# 
# Path: treecat/util.py
# HISTOGRAMS = ProfilingSet(Counter)
# 
# Path: treecat/util.py
# SERIES = ProfilingSet(list)
# 
# Path: treecat/util.py
# TREECAT_JIT = int(os.environ.get('TREECAT_JIT', 1))
# TREECAT_THREADS = int(
#     os.environ.get('TREECAT_THREADS', multiprocessing.cpu_count()))
# PROFILING = int(os.environ.get('TREECAT_PROFILE', 0))
# LOG_LEVEL = int(os.environ.get('TREECAT_LOG_LEVEL', logging.CRITICAL))
# LOG_FILENAME = os.environ.get('TREECAT_LOG_FILE')
# LOG_FORMAT = '%(levelname).1s %(process)d %(name)s %(message)s'
#     N, M = probs.shape
#     V = ragged_index.shape[0] - 1
#     R = ragged_index[-1]
#     N, R = data.shape
#     V = len(ragged_index) - 1
# POOL = None
#         POOL = multiprocessing.Pool(TREECAT_THREADS)
# TIMERS = defaultdict(ProfileTimer)
# COUNTERS = ProfilingSet(lambda: 0)
# HISTOGRAMS = ProfilingSet(Counter)
# SERIES = ProfilingSet(list)
# def TODO(message=''):
# def no_jit(*args, **kwargs):
# def jit_random_seed(seed):
# def set_random_seed(seed):
# def np_printoptions(**kwargs):
# def sample_from_probs(probs):
# def sample_from_probs2(probs, out=None):
# def quantize_from_probs2(probs, resolution):
# def make_ragged_index(columns):
# def make_ragged_mask(ragged_index, mask):
# def count_observations(ragged_index, data):
# def guess_counts(ragged_index, data):
# def parallel_map(fun, args):
#     def __init__(self):
#     def __enter__(self):
#     def __exit__(self, type, value, traceback):
# def profile_timed(fun):
#     def profiled_fun(*args, **kwargs):
# def get_profiling_stats():
# def log_profiling_stats():
# class ProfilingSet(defaultdict):
# class ProfileTimer(object):
# 
# Path: treecat/util.py
# TREECAT_JIT = int(os.environ.get('TREECAT_JIT', 1))
# TREECAT_THREADS = int(
#     os.environ.get('TREECAT_THREADS', multiprocessing.cpu_count()))
# PROFILING = int(os.environ.get('TREECAT_PROFILE', 0))
# LOG_LEVEL = int(os.environ.get('TREECAT_LOG_LEVEL', logging.CRITICAL))
# LOG_FILENAME = os.environ.get('TREECAT_LOG_FILE')
# LOG_FORMAT = '%(levelname).1s %(process)d %(name)s %(message)s'
#     N, M = probs.shape
#     V = ragged_index.shape[0] - 1
#     R = ragged_index[-1]
#     N, R = data.shape
#     V = len(ragged_index) - 1
# POOL = None
#         POOL = multiprocessing.Pool(TREECAT_THREADS)
# TIMERS = defaultdict(ProfileTimer)
# COUNTERS = ProfilingSet(lambda: 0)
# HISTOGRAMS = ProfilingSet(Counter)
# SERIES = ProfilingSet(list)
# def TODO(message=''):
# def no_jit(*args, **kwargs):
# def jit_random_seed(seed):
# def set_random_seed(seed):
# def np_printoptions(**kwargs):
# def sample_from_probs(probs):
# def sample_from_probs2(probs, out=None):
# def quantize_from_probs2(probs, resolution):
# def make_ragged_index(columns):
# def make_ragged_mask(ragged_index, mask):
# def count_observations(ragged_index, data):
# def guess_counts(ragged_index, data):
# def parallel_map(fun, args):
#     def __init__(self):
#     def __enter__(self):
#     def __exit__(self, type, value, traceback):
# def profile_timed(fun):
#     def profiled_fun(*args, **kwargs):
# def get_profiling_stats():
# def log_profiling_stats():
# class ProfilingSet(defaultdict):
# class ProfileTimer(object):
# 
# Path: treecat/util.py
# @jit(nopython=True, cache=True)
# def sample_from_probs(probs):
#     """Sample from a vector of non-normalized probabilitites.
# 
#     Args:
#         probs: An [M]-shaped numpy array of non-normalized probabilities.
# 
#     Returns:
#         An integer in range(M).
#     """
#     cdf = probs.cumsum()
#     return (np.random.rand() * cdf[-1] < cdf).argmax()


# Path: treecat/structure.py
import logging
import numba
import numpy as np
from collections import defaultdict
from collections import deque
from scipy.sparse.csgraph import minimum_spanning_tree
from six.moves import range
from six.moves import zip
from treecat.util import COUNTERS
from treecat.util import HISTOGRAMS
from treecat.util import SERIES
from treecat.util import jit
from treecat.util import profile
from treecat.util import sample_from_probs

            if v2 != parent:
                children.append(v2)

    # Construct a flattened program.
    program = np.zeros([V + E + 1 + E + V, 4], np.int16)
    pos = 0
    for v, parent, children in reversed(nested_program):
        program[pos, :] = [OP_UP, v, 0, 0]
        pos += 1
        for child in children:
            program[pos, :] = [OP_IN, v, child, edge_dict[v, child]]
            pos += 1
    program[pos, :] = [OP_ROOT, root, 0, 0]
    pos += 1
    for v, parent, children in nested_program:
        if parent is not None:
            program[pos, :] = [OP_OUT, v, parent, edge_dict[v, parent]]
            pos += 1
        program[pos, :] = [OP_DOWN, v, 0, 0]
        pos += 1
    assert pos == program.shape[0]

    return program


# A jit_set is an array representation of a set of 16-bit integers.
# The memory layout is:
# [ size | item[1] | item[2] | ... | item[size] | unused | ... | unused ]



@jit(nopython=True, cache=True)