docs: def create_factor(self, tool, sources, sink, alignment_node=None):
          """
          Creates a factor. Instantiates a single tool for all of the plates, and connects the source and sink nodes with
          that tool.
  
          Note that the tool parameters these are currently fixed over a plate. For parameters that vary over a plate,
          an extra input stream should be used
  
          :param alignment_node:
          :param tool: The tool to use. This is either an instantiated Tool object or a dict with "name" and "parameters"
          :param sources: The source nodes
          :param sink: The sink node
          :return: The factor object
          :type tool: Tool | dict
          :type sources: list[Node] | tuple[Node] | None
          :type sink: Node
          :type alignment_node: Node | None
          :rtype: Factor
          """
          # if isinstance(tool, dict):
          #     tool = self.channels.get_tool(**tool)
  
          if not isinstance(tool, BaseTool):
              raise ValueError("Expected Tool, got {}".format(type(tool)))
  
          if sink.plates:
              if isinstance(tool, (AggregateTool, SelectorTool)):
                  if not sources or len(sources) > 2:
                      raise FactorDefinitionError("{} requires one or two source nodes".format(type(tool)))
  
                  if len(sources) == 2 and sources[0].plates:
                      raise FactorDefinitionError("{} requires the first source to have no plates".format(type(tool)))
  
                  if not sources[-1].plates:
                      raise FactorDefinitionError("{} source must live on a plate".format(type(tool)))
  
                  if len(sources[-1].plates) != 1:
                      # Make sure that there are exactly two plates that don't match: one from each side
                      diff, counts, is_sub_plate = sources[-1].difference(sink)
                      if counts == [1, 1]:
                          # TODO: This sub-plate selection is deprecated
                          if not is_sub_plate:
                              raise IncompatiblePlatesError("Sink plate is not a simplification of source plate")
                      else:
                          # If there are two plates, and one (or both) of them is a root plate, than assume that we are
                          # simplifying by removing that plate
                          if next(p.is_root for p in sources[-1].plates):
                              if len(sink.plates) != 1:
                                  raise IncompatiblePlatesError(
                                      "Multiple sink plates defined. "
                                      "Did you intend a simplification of 2 source plates to a sink plate?")
                              if sink.plates[0] not in sources[-1].plates:
                                  raise IncompatiblePlatesError(
                                      "Source and sink plates do not match. "
                                      "Did you intend a simplification of 2 source plates to a sink plate?")
                          else:
                              if len(sink.plates) > 1:
                                  raise NotImplementedError
                              source_plates = sources[-1].plates
                              sink_plate = sink.plates[0]
                              if len(source_plates) != 2:
                                  raise IncompatiblePlatesError(
                                      "Sink plate is not a simplification of source plate (source must be 2 plates)")
                              plate_diff = set(source_plates).difference({sink_plate, })
                              if len(plate_diff) != 1:
                                  raise IncompatiblePlatesError(
                                      "Sink plate is not a simplification of source plate "
                                      "(the number of plates in the set difference of source and sink is not 1")
                              plate_diff = list(plate_diff)[0]
                              if plate_diff.parent != sink_plate.parent:
                                  raise IncompatiblePlatesError(
                                      "Sink plate is not a simplification of source plate (parents do not match)")
                  else:
                      # Check if the parent plate is valid instead
                      source_plate = sources[-1].plates[0]
                      sink_plate = sink.plates[0]
  
                      error = self.check_plate_compatibility(tool, source_plate, sink_plate)
                      if error is not None:
                          raise IncompatiblePlatesError(error)
              else:
                  if sources:
                      # Check that the plates are compatible
                      source_plates = list(itertools.chain(*(source.plate_ids for source in sources)))
                      for p in sink.plate_ids:
                          if p not in set(source_plates):
                              raise IncompatiblePlatesError("{} not in source plates".format(p))
                      for p in source_plates:
                          if p not in set(sink.plate_ids):
                              raise IncompatiblePlatesError("{} not in sink plates".format(p))
              plates = sink.plates
          else:
              plates = None
  
          factor = Factor(tool=tool, source_nodes=sources,
                          sink_node=sink, alignment_node=alignment_node,
                          plates=plates)
  
          self._add_factor(factor)
          return factor