class_docstr: This class makes it easier to run several L{LocalLinker}s in parallel, and
  offers some control over how each thunk is run.
  
  A wrapper function must be provided, and it can be used to execute the
  thunks, inspect the nodes, print stuff out, etc.
  
  The constructor initializes a WrapLinker.
  
  Parameters
  ----------
  linkers : list of L{LocalLinker} subclasses, whose make_all() method returns
      thunks in the same order.
      For each node in the graph, each linker will provide a
      thunk.  This class makes it possible to iterate over each linker's
      program in parallel.
  wrapper : lambda (i, i_node, i_thunk1, i_thunk2, ...) : None
      Does some user-defined action for the i'th element of the program.
      i_thunk<n> is the thunk returned by the n'th linker. (If you want
      to run the program, make sure to call the necessary thunks in this
      function.)
  
  Notes
  -----
  The outputs of the first linker will be returned.
  
  This linker ensures that each linker has its own storage for inputs and
  outputs and intermediate variables. There is no interference between
  linkers.
code: def make_thunk(self, **kwargs):
      no_recycling = self.no_recycling
  
      make_all = [self.linkers[0].make_all(**kwargs)]
      kwargs.pop("input_storage", None)
      make_all += [l.make_all(**kwargs) for l in self.linkers[1:]]
  
      fns, input_lists, output_lists, thunk_lists, order_lists = zip(
          *make_all
      )
  
      order_list0 = order_lists[0]
      for order_list in order_lists[1:]:
          if not order_list0 == order_list:
              raise Exception(
                  "All linkers to WrapLinker should execute operations in the same order."
              )
  
      inputs0 = input_lists[0]
      outputs0 = output_lists[0]
  
      thunk_groups = list(zip(*thunk_lists))
      order = [x[0] for x in zip(*order_lists)]
  
      to_reset = []
      for thunks, node in izip(thunk_groups, order):
          for j, output in enumerate(node.outputs):
              if output in no_recycling:
                  for thunk in thunks:
                      to_reset.append(thunk.outputs[j])
  
      wrapper = self.wrapper
      pre = self.pre
  
      def f():
          for inputs in input_lists[1:]:
              for input1, input2 in izip(inputs0, inputs):
                  input2.storage[0] = copy(input1.storage[0])
          for x in to_reset:
              x[0] = None
          pre(
              self,
              [input.data for input in input_lists[0]],
              order,
              thunk_groups,
          )
          for i, (thunks, node) in enumerate(izip(thunk_groups, order)):
              try:
                  wrapper(i, node, *thunks)
              except Exception:
                  raise_with_op(node, *thunks)
  
      f.thunk_groups = thunk_groups
  
      return f, inputs0, outputs0
  
imports: from __future__ import absolute_import, print_function, division
  from copy import copy, deepcopy
  from sys import getsizeof
  import sys
  import traceback
  import numpy
  import theano
  from theano.compat import izip
  from six import reraise
  from six.moves import StringIO
  from theano.gof import utils
  from theano.gof import graph
  from theano.gof.type import Type
  from .utils import undef
label_desc: Use image node_id 6 for calling the WrapLinker obj's underlying member method code with example usage: obj.make_thunk() and returns: f,  inputs0,  outputs0