code: import numpy as np
  from copy import copy
  
  class _Validator:
      """Performs internal validations on various input types."""
  
      def observation_sequences(self, X, allow_single=False):
          """Validates observation sequence(s).
  
          Parameters
          ----------
          X: numpy.ndarray or List[numpy.ndarray]
              An individual observation sequence or a list of multiple observation sequences.
  
          allow_single: bool
              Whether to allow an individual observation sequence.
  
          Returns
          -------
          X: numpy.ndarray or List[numpy.ndarray]
              The original input observation sequence(s) if valid.
          """
          X = copy(X)
          if isinstance(X, (list, np.ndarray) if allow_single else list):
              if isinstance(X, list):
                  for i, x in enumerate(X):
                      if not isinstance(x, np.ndarray):
                          raise TypeError('Each observation sequence must be a numpy.ndarray')
                      if not x.ndim <= 2:
                          raise ValueError('Each observation sequence must be at most two-dimensional')
                      x = X[i] = (x if x.ndim == 2 else np.atleast_2d(x).T).astype(float)
                      if not x.shape[1] == X[0].shape[1]:
                          raise ValueError('Each observation sequence must have the same dimensionality')
              elif isinstance(X, np.ndarray):
                  if not X.ndim <= 2:
                      raise ValueError('Observation sequence must be at most two-dimensional')
                  X = (X if X.ndim == 2 else np.atleast_2d(X).T).astype(float)
          else:
              if allow_single:
                  raise TypeError('Expected an individual observation sequence or a list of multiple observation sequences, each of type numpy.ndarray')
              else:
                  raise TypeError('Expected a list of observation sequences, each of type numpy.ndarray')
          return X
  
      def observation_sequences_and_labels(self, X, y):
          """Validates observation sequences and corresponding labels.
  
          Parameters
          ----------
          X: List[numpy.ndarray]
              A list of multiple observation sequences.
  
          y: List[str]
              A list of labels for the observation sequences.
  
          Returns
          -------
          X: List[numpy.ndarray]
              The original input observation sequences if valid.
  
          y: List[str]
              The original input labels if valid.
          """
          self.observation_sequences(X, allow_single=False)
          self.list_of_strings(y, desc='labels')
          if not len(X) == len(y):
              raise ValueError('Expected the same number of observation sequences and labels')
          return X, y
  
      def integer(self, item, desc):
          """Validates an integer.
  
          Parameters
          ----------
          item: int
              The item to validate.
  
          desc: str
              A description of the item being validated.
  
          Returns
          -------
          item: int
              The original input item if valid.
          """
          if not isinstance(item, int):
              raise TypeError("Expected {} to be an integer".format(desc))
          return item
  
      def string(self, item, desc):
          """Validates a string.
  
          Parameters
          ----------
          item: str
              The item to validate.
  
          desc: str
              A description of the item being validated.
  
          Returns
          -------
          item: str
              The original input item if valid.
          """
          if not isinstance(item, str):
              raise TypeError("Expected {} to be a string".format(desc))
          return item
  
      def boolean(self, item, desc):
          """Validates a boolean.
  
          Parameters
          ----------
          item: bool
              The item to validate.
  
          desc: str
              A description of the item being validated.
  
          Returns
          -------
          item: bool
              The original input item if valid.
          """
          if not isinstance(item, bool):
              raise TypeError("Expected {} to be a boolean".format(desc))
          return item
  
      def one_of(self, item, items, desc):
          """Validates that an item is one of some permitted values.
  
          Parameters
          ----------
          item: Any
              The item to validate.
  
          items: List[Any]
              The list of permitted values to check against.
  
          desc: str
              A description of the item being validated.
  
          Returns
          -------
          item: Any
              The original input item if valid.
          """
          if not item in items:
              raise ValueError('Expected {} to be one of {}'.format(desc, items))
          return item
  
      def restricted_integer(self, item, condition, desc, expected):
          """Validates an integer and checks that it satisfies some condition.
  
          Parameters
          ----------
          item: int
              The item to validate.
  
          condition: lambda
              A condition to check the item against.
  
          desc: str
              A description of the item being validated.
  
          expected: str
              A description of the condition, or expected value.
  
          Returns
          -------
          item: int
              The original input item if valid.
          """
          if isinstance(item, int):
              if not condition(item):
                  raise ValueError('Expected {} to be {}'.format(desc, expected))
          else:
              raise TypeError("Expected {} to be an integer".format(desc))
          return item
  
      def restricted_float(self, item, condition, desc, expected):
          """Validates a float and checks that it satisfies some condition.
  
          Parameters
          ----------
          item: float
              The item to validate.
  
          condition: lambda
              A condition to check the item against.
  
          desc: str
              A description of the item being validated.
  
          expected: str
              A description of the condition, or expected value.
  
          Returns
          -------
          item: float
              The original input item if valid.
          """
          if isinstance(item, float):
              if not condition(item):
                  raise ValueError('Expected {} to be {}'.format(desc, expected))
          else:
              raise TypeError("Expected {} to be a float".format(desc))
          return item
  
      def list_of_strings(self, items, desc):
          """Validates a list and checks that it consists entirely of strings.
  
          Parameters
          ----------
          items: List[str]
              The item to validate.
  
          desc: str
              A description of the item being validated.
  
          Returns
          -------
          items: List[str]
              The original input items if valid.
          """
          if isinstance(items, list):
              if not all(isinstance(item, str) for item in items):
                  raise ValueError('Expected all {} to be strings'.format(desc))
          else:
              raise TypeError('Expected {} to be a list of strings'.format(desc))
          return items
  
      def random_state(self, state):
          """Validates a random state object or seed.
  
          Parameters
          ----------
          state: None, int, numpy.random.RandomState
              A random state object or seed.
  
          Returns
          -------
          state: numpy.random.RandomState
              A random state object.
          """
          if state is None:
              return np.random.RandomState(seed=0)
          elif isinstance(state, int):
              return np.random.RandomState(seed=state)
          elif isinstance(state, np.random.RandomState):
              return state
          else:
              raise TypeError('Expected random state to be of type: None, int, or numpy.random.RandomState') import numpy as np
  from .topology import _Topology
  
  class _LeftRightTopology(_Topology):
      """Represents the topology for a left-right HMM, imposing an upper-triangular transition matrix.
  
      Parameters
      ----------
      n_states: int
          Number of states in the HMM.
  
      random_state: numpy.random.RandomState
          A random state object for reproducible randomness.
      """
  
      def __init__(self, n_states: int, random_state: np.random.RandomState):
          super().__init__(n_states, random_state)
  
      def uniform_transitions(self) -> np.ndarray:
          """Sets the transition matrix as uniform (equal probability of transitioning
              to all other possible states from each state) corresponding to the topology.
  
          Returns
          -------
          transitions: numpy.ndarray
              The uniform transition matrix of shape `(n_states, n_states)`.
          """
          upper_ones = np.triu(np.ones((self._n_states, self._n_states)))
          upper_divisors = np.triu(np.tile(np.arange(self._n_states, 0, -1), (self._n_states, 1)).T)
          lower_ones = np.tril(np.ones(self._n_states), k=-1) # One-pad lower triangle to prevent zero division
          return upper_ones / (upper_divisors + lower_ones)
  
      def random_transitions(self) -> np.ndarray:
          """Sets the transition matrix as random (random probability of transitioning
          to all other possible states from each state) by sampling probabilities
          from a Dirichlet distribution - according to the topology.
  
          Parameters
          ----------
          transitions: numpy.ndarray
              The random transition matrix of shape `(n_states, n_states)`.
          """
          transitions = self._random_state.dirichlet(np.ones(self._n_states), size=self._n_states)
          lower_sums = np.sum(np.tril(transitions, k=-1), axis=1) # Amount to be redistributed per row
          quantities = np.arange(self._n_states, 0, -1) # Number of elements per row to redistribute evenly to
          upper_ones = np.triu(np.ones((self._n_states, self._n_states)))
          redist = (lower_sums / quantities).reshape(-1, 1) * upper_ones
          return np.triu(transitions) + redist
  
      def validate_transitions(self, transitions: np.ndarray) -> None:
          """Validates a transition matrix according to the topology's restrictions.
  
          Parameters
          ----------
          transitions: numpy.ndarray
              The transition matrix to validate.
          """
          super().validate_transitions(transitions)
          if not np.allclose(transitions, np.triu(transitions)):
              raise ValueError('Left-right transition matrix must be upper-triangular')
apis: numpy.random.RandomState numpy.atleast_2d
apis: numpy.arange numpy.ones numpy.tril numpy.triu