code: def _rebind_variables(self, new_inputs):
          
          expr = self._expr
  
          # If we have 11+ variables, some of our variable names may be
          # substrings of other variable names. For example, we might have x_1,
          # x_10, and x_100. By enumerating in reverse order, we ensure that
          # every variable name which is a substring of another variable name is
          # processed after the variable of which it is a substring. This
          # guarantees that the substitution of any given variable index only
          # ever affects exactly its own index. For example, if we have variables
          # with indices going up to 100, we will process all of the x_1xx names
          # before x_1x, which will be before x_1, so the substitution of x_1
          # will not affect x_1x, which will not affect x_1xx.
          for idx, input_ in reversed(list(enumerate(self.inputs))):
              old_varname = "x_%d" % idx
              # Temporarily rebind to x_temp_N so that we don't overwrite the
              # same value multiple times.
              temp_new_varname = "x_temp_%d" % new_inputs.index(input_)
              expr = expr.replace(old_varname, temp_new_varname)
          # Clear out the temp variables now that we've finished iteration.
          return expr.replace("_temp_", "_")
docs: Return self._expr with all variables rebound to the indices implied by
          new_inputs.