class_docstr: Combining multiple non-connected components of computational graph.
  
  This class combines each ``chainer.Chain``, which represents one of the
  non-connected component in compuational graph. In ``__call__()``,
  the returned object of ``chainer.Chain`` (which represents pointer)
  are passed to the next ``chainer.Chain``, in order to retain the
  computational graph connected and make backprop work properly.
  
  Users add each ``chainer.Chain`` by ``add_link()`` method. Each chain
  is invoked in forward computation according to the order they are added,
  and in backward computation according to the reversed order.
  
  .. admonition:: Example (basic usage)
  
      This is a simple example of the model which sends its outputs to
      rank=1 machine::
  
          import chainer
          import chainer.functions as F
          import chainermn
  
  
          class SimpleModelSub(chainer.Chain):
  
              def __init__(self, n_in, n_hidden, n_out):
                  super(SimpleModelSub, self).__init__(
                      l1=L.Linear(n_in, n_hidden),
                      l2=L.Linear(n_hidden, n_out))
  
              def __call__(self, x):
                  h1 = F.relu(self.l1(x))
                  return self.l2(h1)
  
  
          class SimpleModel(chainermn.MultiNodeChainList):
  
              def __init__(self, comm, n_in, n_hidden, n_out):
                  super(SimpleModel, self).__init__(comm)
                  self.add_link(
                      SimpleModelSub(n_in, n_hidden, n_out),
                      rank_in=None,
                      rank_out=1)
  
  .. admonition:: Example (split MLP on 2 processes)
  
      This is the other example of two models interacting each other::
  
          import chainer
          import chainer.functions as F
          import chainermn
  
  
          class MLP(chainer.Chain):
  
              def __init__(self, n_in, n_hidden, n_out):
                  super(MLP, self).__init__(
                      l1=L.Linear(n_in, n_hidden),
                      l2=L.Linear(n_hidden, n_hidden),
                      l3=L.Linear(n_hidden, n_out))
  
              def __call__(self, x):
                  h1 = F.relu(self.l1(x))
                  h2 = F.relu(self.l2(h1))
                  return self.l3(h2)
  
  
          class Model0(chainermn.MultiNodeChainList):
  
              def __init__(self, comm):
                  super(Model0, self).__init__(comm)
                  self.add_link(
                      MLP(10000, 5000, 2000),
                      rank_in=None,
                      rank_out=1)
                  self.add_link(
                      MLP(100, 50, 10),
                      rank_in=1,
                      rank_out=None)
  
  
          class Model1(chainermn.MultiNodeChainList):
  
              def __init__(self, comm):
                  super(Model1, self).__init__(comm)
                  self.add_link(MLP(2000, 500, 100), rank_in=0, rank_out=0)
  
  
      ``Model0`` is expected to be on rank=0, and ``Model1`` is expected to
      be on rank=1. The first ``MLP`` in ``Model0`` will send its outputs
      to ``Model1``, then ``MLP`` in ``Model1`` will receive it and send
      its outputs to the second ``MLP`` in ``Model0``.
  
  .. admonition:: Example (sending tuples)
  
      This is the example for sending a tuple::
  
          import chainer
          import chainer.functions as F
          import chainermn
  
          class NN0(chainer.Chain):
              def __call__(self, x):
                  y0 = some_calculation_nn0_0(x)
                  y1 = some_calculation_nn1_1(x)
                  return y0, y1
  
          class NN1(chainer.Chain):
              def __call__(self, y):
                  y0, y1 = y  # unpack tuple from NN0
                  return some_calculation_nn1(y0, y1)
  
          class Model_on_Process_0(chainermn.MultiNodeChainList):
              def __init__(self, comm):
                  super(Model_on_Process_0, self).__init__(comm=comm)
                  self.add_link(NN0(), rank_in=None, rank_out=1)
  
          class Model_on_Process_1(chainermn.MultiNodeChainList):
              def __init__(self, comm):
                  super(Model_on_Process_1, self).__init__(comm=comm)
                  self.add_link(NN1(), rank_in=0, rank_out=None)
  
      In this example, ``Model_on_Process_0`` sends two elemental tuple
      ``(y0, y1)`` (returned by ``NN0.__call__``) to ``Model_on_Process_1``,
      which can be unpacked as shown in ``NN1.__call__``.
  
  Args:
      comm (chainermn.communicators._base.CommunicatorBase):
          ChainerMN communicator.
docstr: Register one connected link with its inout rank.
  
  Args:
      link (chainer.Link): The link object to be registered.
      rank_in (int, list, or None):
          Ranks from which it receives data. If None is specified,
          the model does not receive from any machines.
      rank_out (int, list, or None):
          Ranks to which it sends data. If None is specified,
          the model will not send to any machine.
code: def add_link(self, link, rank_in=None, rank_out=None):
      """Register one connected link with its inout rank.
  
      Args:
          link (chainer.Link): The link object to be registered.
          rank_in (int, list, or None):
              Ranks from which it receives data. If None is specified,
              the model does not receive from any machines.
          rank_out (int, list, or None):
              Ranks to which it sends data. If None is specified,
              the model will not send to any machine.
      """
      super(MultiNodeChainList, self).add_link(link)
      if isinstance(rank_in, int):
          rank_in = [rank_in]
      if isinstance(rank_out, int):
          rank_out = [rank_out]
  
      if rank_out is None:
          for _, _rank_out in self._rank_inouts:
              if _rank_out is None:
                  raise ValueError(
                      "MultiNodeChainList cannot have more than two "
                      "computational graph component whose rank_out is None"
                  )
  
      self._rank_inouts.append((rank_in, rank_out))
  
file_path: chainer/chainermn/links/multi_node_chain_list.py
imports: from six.moves import queue
  import chainer
  import chainermn
  import chainermn.communicators
  import chainermn.functions
label_desc: Use image node_id 2 for calling the MultiNodeChainList obj's underlying member method code with example usage: obj.add_link(link, rank_in, rank_out) without return types