class_docstr: Parameters
  ----------
  inputs
      Inputs of the inner function of scan.
  outputs
      Outputs of the inner function of scan.
  info
      Dictionary containing different properties of the scan op (like number
      of different types of arguments, name, mode, if it should run on GPU or
      not, etc.).
  typeConstructor
      Function that constructs an equivalent to Theano TensorType.
  
  Notes
  -----
  ``typeConstructor`` had been added to refactor how
  Theano deals with the GPU. If it runs on the GPU, scan needs
  to construct certain outputs (those who reside in the GPU
  memory) as the GPU-specific type.  However we can not import
  gpu code in this file (as it is in sandbox, and not available
  on each machine) so the workaround is that the GPU
  optimization passes to the constructor of this class a
  function that is able to construct a GPU type. This way the
  class Scan does not need to be aware of the details for the
  GPU, it just constructs any tensor using this function (which
  by default constructs normal tensors).
class_docstr_tok: ["Parameters","--","--","--","--","--","inputs","Inputs","of","the","inner","function","of","scan",".","outputs","Outputs","of","the","inner","function","of","scan",".","info","Dictionary","containing","different","properties","of","the","scan","op","(","like","number","of","different","types","of","arguments",",","name",",","mode",",","if","it","should","run","on","GPU","or","not",",","etc.",")",".","typeConstructor","Function","that","constructs","an","equivalent","to","Theano","TensorType",".","Notes","--","--","-","``","typeConstructor","``","had","been","added","to","refactor","how","Theano","deals","with","the","GPU",".","If","it","runs","on","the","GPU",",","scan","needs","to","construct","certain","outputs","(","those","who","reside","in","the","GPU","memory",")","as","the","GPU-specific","type",".","However","we","can","not","import","gpu","code","in","this","file","(","as","it","is","in","sandbox",",","and","not","available","on","each","machine",")","so","the","workaround","is","that","the","GPU","optimization","passes","to","the","constructor","of","this","class","a","function","that","is","able","to","construct","a","GPU","type",".","This","way","the","class","Scan","does","not","need","to","be","aware","of","the","details","for","the","GPU",",","it","just","constructs","any","tensor","using","this","function","(","which","by","default","constructs","normal","tensors",")","."]
code: def R_op(self, inputs, eval_points):
      # Step 0. Prepare some shortcut variable
      self_inputs = self.inputs
      rop_of_inputs = (
          self_inputs[: self.n_seqs + self.n_outs]
          + self_inputs[
              self.n_seqs + self.n_outs + self.n_shared_outs :
          ]
      )
      self_outputs = self.outputs
  
      # Step 1. Compute the R_op of the inner function
      inner_eval_points = [
          scan_utils.safe_new(x, "_evalpoint") for x in rop_of_inputs
      ]
      if self.as_while:
          rop_self_outputs = self_outputs[:-1]
      else:
          rop_self_outputs = self_outputs
      if self.info["n_shared_outs"] > 0:
          rop_self_outputs = rop_self_outputs[
              : -self.info["n_shared_outs"]
          ]
      rop_outs = tensor.Rop(
          rop_self_outputs, rop_of_inputs, inner_eval_points
      )
      if type(rop_outs) not in (list, tuple):
          rop_outs = [rop_outs]
      # Step 2. Figure out what corresponds to what in the scan
  
      # When doing the R-op of scan, you end up having double of each type of
      # input, because for each sequence you need also its eval point, for
      # each mit_mot, mit_sot, sit_sot or other type of inputs the same.
      # Interestingly enough, all these types of eval points behave the same
      # way as the input to which they correspond
      # The only exception is the eval point for the number of sequences, and
      # evan point for the number of nit_sot which I think should just be
      # ignored (?)
      info = OrderedDict()
      info["n_seqs"] = self.n_seqs * 2
      info["n_mit_sot"] = self.n_mit_sot * 2
      info["n_sit_sot"] = self.n_sit_sot * 2
      info["n_mit_mot"] = self.n_mit_mot * 2
      info["n_nit_sot"] = self.n_nit_sot * 2
      info["n_shared_outs"] = self.n_shared_outs
      info["gpua"] = False
      info["as_while"] = self.as_while
      info["profile"] = self.profile
      info["truncate_gradient"] = self.truncate_gradient
      if self.name:
          info["name"] = "rop_of_" + self.name
      else:
          info["name"] = None
      info["mode"] = self.mode
      info["allow_gc"] = self.allow_gc
      info["mit_mot_out_slices"] = self.mit_mot_out_slices * 2
      info["destroy_map"] = OrderedDict()
      new_tap_array = []
      b = 0
      e = self.n_mit_mot
      new_tap_array += self.tap_array[b:e] * 2
      b = e
      e += self.n_mit_sot
      new_tap_array += self.tap_array[b:e] * 2
      b = e
      e += self.n_sit_sot
      new_tap_array += self.tap_array[b:e] * 2
      info["tap_array"] = new_tap_array
  
      # Sequences ...
      b = 1
      ib = 0
      e = 1 + self.n_seqs
      ie = self.n_seqs
      clean_eval_points = []
      for inp, evp in zip(inputs[b:e], eval_points[b:e]):
          if evp is not None:
              clean_eval_points.append(evp)
          else:
              clean_eval_points.append(inp.zeros_like())
  
      scan_seqs = inputs[b:e] + clean_eval_points
      inner_seqs = self_inputs[ib:ie] + inner_eval_points[ib:ie]
  
      # MIT_MOT sequences ...
      b = e
      e = e + self.n_mit_mot
      ib = ie
      ie = ie + int(
          np.sum([len(x) for x in self.tap_array[: self.n_mit_mot]])
      )
      clean_eval_points = []
      for inp, evp in zip(inputs[b:e], eval_points[b:e]):
          if evp is not None:
              clean_eval_points.append(evp)
          else:
              clean_eval_points.append(inp.zeros_like())
  
      scan_mit_mot = inputs[b:e] + clean_eval_points
      inner_mit_mot = self_inputs[ib:ie] + inner_eval_points[ib:ie]
  
      # MIT_SOT sequences ...
      b = e
      e = e + self.n_mit_sot
      ib = ie
      ie = ie + int(
          np.sum(
              [
                  len(x)
                  for x in self.tap_array[
                      self.n_mit_mot : self.n_mit_mot + self.n_mit_sot
                  ]
              ]
          )
      )
      clean_eval_points = []
      for inp, evp in zip(inputs[b:e], eval_points[b:e]):
          if evp is not None:
              clean_eval_points.append(evp)
          else:
              clean_eval_points.append(inp.zeros_like())
  
      scan_mit_sot = inputs[b:e] + eval_points[b:e]
      inner_mit_sot = self_inputs[ib:ie] + inner_eval_points[ib:ie]
  
      # SIT_SOT sequences ...
      b = e
      e = e + self.n_sit_sot
      ib = ie
      ie = ie + self.n_sit_sot
      clean_eval_points = []
      for inp, evp in zip(inputs[b:e], eval_points[b:e]):
          if evp is not None:
              clean_eval_points.append(evp)
          else:
              clean_eval_points.append(inp.zeros_like())
  
      scan_sit_sot = inputs[b:e] + clean_eval_points
      inner_sit_sot = self_inputs[ib:ie] + inner_eval_points[ib:ie]
  
      # Shared outs ...
      b = e
      e = e + self.n_shared_outs
      ib = ie
      ie = ie + self.n_shared_outs
      scan_shared = inputs[b:e]
      inner_shared = self_inputs[ib:ie]
  
      # NIT_SOT sequences
      b = e
      e = e + self.n_nit_sot
      scan_nit_sot = inputs[b:e] * 2
  
      # All other arguments
      clean_eval_points = []
      for inp, evp in zip(inputs[e:], eval_points[e:]):
          if evp is not None:
              clean_eval_points.append(evp)
          else:
              clean_eval_points.append(inp.zeros_like())
      scan_other = inputs[e:] + clean_eval_points
      # inner_eval_points do not have entries for shared variables
      inner_other = self_inputs[ie:] + inner_eval_points[ib:]
  
      # Outputs
      n_mit_mot_outs = int(
          np.sum([len(x) for x in self.mit_mot_out_slices])
      )
      info["n_mit_mot_outs"] = n_mit_mot_outs * 2
      b = 0
      e = n_mit_mot_outs
      inner_out_mit_mot = self_outputs[b:e] + rop_outs[b:e]
      b = e
      e = e + self.n_mit_sot
      inner_out_mit_sot = self_outputs[b:e] + rop_outs[b:e]
      b = e
      e = e + self.n_sit_sot
      inner_out_sit_sot = self_outputs[b:e] + rop_outs[b:e]
      b = e
      e = e + self.n_nit_sot
      inner_out_nit_sot = self_outputs[b:e] + rop_outs[b:e]
      b = e
      e = e + self.n_shared_outs
      inner_out_shared = self_outputs[b:e]
  
      inner_ins = (
          inner_seqs
          + inner_mit_mot
          + inner_mit_sot
          + inner_sit_sot
          + inner_shared
          + inner_other
      )
      inner_outs = (
          inner_out_mit_mot
          + inner_out_mit_sot
          + inner_out_sit_sot
          + inner_out_nit_sot
          + inner_out_shared
      )
  
      if self.as_while:
          inner_outs += [self_outputs[-1]]
      scan_inputs = (
          [inputs[0]]
          + scan_seqs
          + scan_mit_mot
          + scan_mit_sot
          + scan_sit_sot
          + scan_shared
          + scan_nit_sot
          + scan_other
      )
  
      local_op = Scan(inner_ins, inner_outs, info)
      outputs = local_op(*scan_inputs)
      if type(outputs) not in (list, tuple):
          outputs = [outputs]
      # Select only the result of the R_op results
      final_outs = []
      b = self.n_mit_mot
      e = self.n_mit_mot * 2
      final_outs += outputs[b:e]
      b = e + self.n_mit_sot
      e = e + self.n_mit_sot * 2
      final_outs += outputs[b:e]
      b = e + self.n_sit_sot
      e = e + self.n_sit_sot * 2
      final_outs += outputs[b:e]
      b = e + self.n_nit_sot
      e = e + self.n_nit_sot * 2
      final_outs += outputs[b:e]
      final_outs += [None] * self.n_shared_outs
  
      return final_outs
  
imports: from __future__ import absolute_import, print_function, division
  import copy
  import itertools
  import logging
  import time
  from collections import OrderedDict
  import numpy
  from six import iteritems, integer_types, raise_from
  from six.moves import xrange
  import theano
  from theano.compile import function, In, Out
  from theano.compile.mode import AddFeatureOptimizer
  from theano import compile, config, gradient, gof, tensor
  from theano.gof import PureOp, Apply
  from theano.gof.graph import io_connection_pattern
  from theano.gof.toolbox import NoOutputFromInplace
  from theano.compat import izip
  from theano.tensor import as_tensor_variable, TensorType
  from theano.tensor.opt import Shape_i
  from theano.gradient import grad_undefined, DisconnectedType, NullType
  from six import string_types
  from theano.compile.profiling import ScanProfileStats
  from theano.scan_module import scan_utils
  from theano.scan_module.scan_utils import safe_new, forced_replace
label_desc: Use image node_id 40 for calling the Scan obj's underlying member method code with example usage: obj.R_op(inputs, eval_points) and returns: final_outs