code: def insert_deepcopy(fgraph, wrapped_inputs, wrapped_outputs):
      """
      Insert deepcopy in the fgraph to break aliasing of outputs
      """
      # This loop was inserted to remove aliasing between outputs when
      # they all evaluate to the same value. Originally it was OK for
      # outputs to be aliased, but some of the outputs can be shared
      # variables, and is not good for shared variables to be
      # aliased. It might be possible to optimize this by making sure
      # there is no aliasing only between shared variables.
  
      # If some outputs are constant, we add deep copy to respect the
      # memory contract
  
      # We don't insert deep copy when the output.borrow is True for all
      # concerned outputs.
  
      assert len(wrapped_inputs) == len(fgraph.inputs)
      assert len(wrapped_outputs) == len(fgraph.outputs)
      reason = "insert_deepcopy"
      updated_fgraph_inputs = set(
          [
              fgraph_i
              for i, fgraph_i in zip(wrapped_inputs, fgraph.inputs)
              if getattr(i, "update", False)
          ]
      )
  
      # We can't use fgraph.inputs as this don't include Constant Value.
      all_graph_inputs = gof.graph.inputs(fgraph.outputs)
      has_destroyers_attr = hasattr(fgraph, "has_destroyers")
  
      for i in xrange(len(fgraph.outputs)):
          views_of_output_i = set()
          view_tree_set(
              alias_root(fgraph.outputs[i]), views_of_output_i
          )
          copied = False
          # do not allow outputs to be aliased
          for j in xrange(i + 1, len(fgraph.outputs)):
              # We could don't put deep copy if both outputs have borrow==True
              # and not(wrapped_outputs[i].borrow and wrapped_outputs[j].borrow):
              if fgraph.outputs[j] in views_of_output_i:
                  if (
                      wrapped_outputs[i].borrow
                      and wrapped_outputs[j].borrow
                  ):
                      fgraph.change_input(
                          "output",
                          i,
                          view_op(fgraph.outputs[i]),
                          reason=reason,
                      )
                  else:
                      fgraph.change_input(
                          "output",
                          i,
                          deep_copy_op(fgraph.outputs[i]),
                          reason=reason,
                      )
                  copied = True
                  break
  
          if not copied:
              for input_j in all_graph_inputs:
                  # do not allow outputs to be aliased to an inputs (j), unless
                  # a) that j'th input has been 'destroyed' by
                  #    e.g. in-place computations
                  # b) that j'th input is a shared variable that is also
                  #    being updated
                  if input_j in updated_fgraph_inputs:
                      continue
                  if input_j in views_of_output_i and not (
                      has_destroyers_attr
                      and fgraph.has_destroyers([input_j])
                  ):
                      # We don't put deep_copy_op if the input and the
                      # output have borrow==True
                      if input_j in fgraph.inputs:
                          j = fgraph.inputs.index(input_j)
                          if (
                              wrapped_outputs[i].borrow
                              and wrapped_inputs[j].borrow
                          ):
                              fgraph.change_input(
                                  "output",
                                  i,
                                  view_op(fgraph.outputs[i]),
                                  reason="insert_deepcopy",
                              )
                              break
                          else:
                              fgraph.change_input(
                                  "output",
                                  i,
                                  deep_copy_op(fgraph.outputs[i]),
                                  reason="insert_deepcopy",
                              )
                              break
                      elif wrapped_outputs[i].borrow:
                          fgraph.change_input(
                              "output",
                              i,
                              view_op(fgraph.outputs[i]),
                              reason="insert_deepcopy",
                          )
                          break
                      else:
                          fgraph.change_input(
                              "output",
                              i,
                              deep_copy_op(fgraph.outputs[i]),
                              reason="insert_deepcopy",
                          )
                          break
  
imports: from __future__ import absolute_import, print_function, division
  import copy
  from six import string_types, iteritems, iterkeys
  from six.moves import xrange
  import six.moves.copyreg
  import six.moves.cPickle
  from itertools import chain
  import time
  import warnings
  import numpy
  import theano
  from theano import config, gof
  from theano.compat import izip
  from theano.gof import graph
  import theano.compile.profiling
  from theano.compile.io import In, SymbolicInput, SymbolicOutput
  from theano.compile.ops import deep_copy_op, view_op
  from theano.gof.graph import is_same_graph
  from theano.gof.op import ops_with_inner_function
  import logging
label_desc: Use image node_id 8 for calling a global function with example usage: insert_deepcopy(fgraph, wrapped_inputs, wrapped_outputs) without return types