class_docstr: Represents a quantum circuit as a directed acyclic graph.
  
  In this representation the :class:`~.Operator` instances are the nodes of the graph,
  and each directed edge represent a subsystem (or a group of subsystems) on which the two
  Operators act subsequently. This representation can describe the causal relationships
  between arbitrary quantum channels and measurements, not just unitary gates.
  
  Args:
      ops (Iterable[.Operator]): quantum operators constituting the circuit, in temporal order
      obs (Iterable[.MeasurementProcess]): terminal measurements, in temporal order
      wires (.Wires): The addressable wire registers of the device that will be executing this graph
      par_info (list[dict]): Parameter information. For each index, the entry is a dictionary containing an operation
      and an index into that operation's parameters.
      trainable_params (set[int]): A set containing the indices of parameters that support
          differentiability. The indices provided match the order of appearence in the
          quantum circuit.
class_docstr_tok: ["Represents","a","quantum","circuit","as","a","directed","acyclic","graph",".","In","this","representation","the",":","class",":","`","~.Operator","`","instances","are","the","nodes","of","the","graph",",","and","each","directed","edge","represent","a","subsystem","(","or","a","group","of","subsystems",")","on","which","the","two","Operators","act","subsequently",".","This","representation","can","describe","the","causal","relationships","between","arbitrary","quantum","channels","and","measurements",",","not","just","unitary","gates",".","Args",":","ops","(","Iterable","[",".Operator","]",")",":","quantum","operators","constituting","the","circuit",",","in","temporal","order","obs","(","Iterable","[",".MeasurementProcess","]",")",":","terminal","measurements",",","in","temporal","order","wires","(",".Wires",")",":","The","addressable","wire","registers","of","the","device","that","will","be","executing","this","graph","par_info","(","list","[","dict","]",")",":","Parameter","information",".","For","each","index",",","the","entry","is","a","dictionary","containing","an","operation","and","an","index","into","that","operation","'s","parameters",".","trainable_params","(","set","[","int","]",")",":","A","set","containing","the","indices","of","parameters","that","support","differentiability",".","The","indices","provided","match","the","order","of","appearence","in","the","quantum","circuit","."]
code: def __init__(
      self, ops, obs, wires, par_info=None, trainable_params=None
  ):
      self._operations = ops
      self._observables = obs
      self.par_info = par_info
      self.trainable_params = trainable_params
  
      queue = ops + obs
  
      self._depth = None
  
      self._grid = {}
      """dict[int, list[Operator]]: dictionary representing the quantum circuit as a grid.
          Here, the key is the wire number, and the value is a list containing the operators on that wire.
          """
  
      self._indices = {}
      # Store indices for the nodes of the DAG here
  
      self.wires = wires
      """Wires: wires that are addressed in the operations.
          Required to translate between wires and indices of the wires on the device."""
      self.num_wires = len(wires)
      """int: number of wires the circuit contains"""
      for k, op in enumerate(queue):
          # meas_wires = wires or None  # cannot use empty wire list in MeasurementProcess
          op.queue_idx = k  # store the queue index in the Operator
  
          for w in wires if len(op.wires) == 0 else op.wires:
              # get the index of the wire on the device
              wire = wires.index(w)
              # add op to the grid, to the end of wire w
              self._grid.setdefault(wire, []).append(op)
  
      # TODO: State preparations demolish the incoming state entirely, and therefore should have no incoming edges.
  
      self._graph = rx.PyDiGraph(
          multigraph=False
      )  #: rx.PyDiGraph: DAG representation of the quantum circuit
  
      # Iterate over each (populated) wire in the grid
      for wire in self._grid.values():
          # Add the first operator on the wire to the graph
          # This operator does not depend on any others
  
          # Check if wire[0] in self._grid.values()
          # is already added to the graph; this
          # condition avoids adding new nodes with
          # the same value but different indexes
          if all(wire[0] is not op for op in self._graph.nodes()):
              _ind = self._graph.add_node(wire[0])
              self._indices.setdefault(id(wire[0]), _ind)
  
          for i in range(1, len(wire)):
              # For subsequent operators on the wire:
              if all(wire[i] is not op for op in self._graph.nodes()):
                  # Add them to the graph if they are not already
                  # in the graph (multi-qubit operators might already have been placed)
                  _ind = self._graph.add_node(wire[i])
                  self._indices.setdefault(id(wire[i]), _ind)
  
              # Create an edge between this and the previous operator
              # There isn't any default value for the edge-data in
              # rx.PyDiGraph.add_edge(); this is set to an empty string
              self._graph.add_edge(
                  self._indices[id(wire[i - 1])],
                  self._indices[id(wire[i])],
                  "",
              )
  
      # For computing depth; want only a graph with the operations, not
      # including the observables
      self._operation_graph = None
  
      # Required to keep track if we need to handle multiple returned
      # observables per wire
      self._max_simultaneous_measurements = None
  
imports: from numbers import Number
  from collections import namedtuple
  import numpy
  import rustworkx
  from pennylane.measurements import MeasurementProcess
  from pennylane.resource import ResourcesOperation
label_desc: Use image node_id 1 to create a new CircuitGraph object  with example: obj = CircuitGraph(ops, obs, wires, par_info, trainable_params)