<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def compute_memory_contents_under_schedule(self, schedule):
 """The in-memory tensors present when executing each operation in schedule. Simulates running operations in the order given by a schedule. Keeps track of the tensors in memory at every point in time, and outputs a list (one entry for each point in time) of all sets of all memory contents (i.e. a frozenset of strings) ever seen in this execution. It is assumed (but not checked) that schedule is a valid topological sort of the operations in this graph. Args: schedule: A list of integer ids; the order to run operations in. Returns: a list of frozenset of strings, where the ith entry describes the tensors in memory when executing operation i (where schedule[i] is an index into get_all_operation_names()). """

    out_degree = self._compute_initial_out_degree()

    curr_memory_contents = set()
    memory_contents_for_each_operation = []

    for operation_id in schedule:
      operation_name = self._operations[operation_id].name
      # Allocate new memory to perform the computation at this node.
      for output_name in self.get_operation_output_names(operation_name):
        curr_memory_contents.add(output_name)
      memory_contents_for_each_operation.append(frozenset(curr_memory_contents))

      # Free any tensors which are no longer needed.
      for output_name in self.get_operation_output_names(operation_name):
        if out_degree[output_name] == 0:
          curr_memory_contents.remove(output_name)
      for input_name in self.get_operation_input_names(operation_name):
        out_degree[input_name] -= 1
        if out_degree[input_name] == 0:
          curr_memory_contents.remove(input_name)

    return memory_contents_for_each_operation