code: def make_pipeline(
      *models: "_ct.models.MLModel",
      compute_units: _Union[None, _ct.ComputeUnit] = None,
  ) -> "_ct.models.MLModel":
      """
      Makes a pipeline with the given models.
  
      Parameters
      ----------
      *models :
          Two or more instances of ``ct.models.MLModel``.
  
      compute_units :
          The set of processing units that all models in the pipeline can use to make predictions.
          Can be ``None`` or ``coremltools.ComputeUnit``.
  
          * If ``None``, the ``compute_unit`` will be inferred from the ``compute_unit`` values of the models.
            If all models do not have the same ``compute_unit`` values, this parameter must be specified.
  
          * ``coremltools.ComputeUnit`` is an enum with four possible values:
              - ``coremltools.ComputeUnit.ALL``: Use all compute units available, including the
                neural engine.
              - ``coremltools.ComputeUnit.CPU_ONLY``: Limit the model to only use the CPU.
              - ``coremltools.ComputeUnit.CPU_AND_GPU``: Use both the CPU and GPU,
                but not the neural engine.
              - ``coremltools.ComputeUnit.CPU_AND_NE``: Use both the CPU and neural engine, but
                not the GPU. Available only for macOS >= 13.0.
  
      Returns
      -------
      ct.models.MLModel
  
      Examples
      --------
      .. sourcecode:: python
  
          my_model1 = ct.models.MLModel('/tmp/m1.mlpackage')
          my_model2 = ct.models.MLModel('/tmp/m2.mlmodel')
  
          my_pipeline_model = ct.utils.make_pipeline(my_model1, my_model2)
  
          y = my_pipeline_model.predict({'x': 12})
  
          my_pipeline_model.save('/tmp/my_pipeline.mlpackage')
          new_my_pipeline = ct.model.MLModel('/tmp/my_pipeline.mlpackage')
  
      """
  
      def updateBlobFileName(proto_message, new_path):
          if type(proto_message) == _mil_proto.Value:
              # Value protobuf message. This is what might need to be updated.
              if proto_message.WhichOneof("value") == "blobFileValue":
                  assert (
                      proto_message.blobFileValue.fileName
                      == "@model_path/weights/weight.bin"
                  )
                  proto_message.blobFileValue.fileName = new_path
          elif hasattr(proto_message, "ListFields"):
              # Normal protobuf message
              for f in proto_message.ListFields():
                  updateBlobFileName(f[1], new_path)
          elif hasattr(proto_message, "values"):
              # Protobuf map
              for v in proto_message.values():
                  updateBlobFileName(v, new_path)
          elif isinstance(proto_message, _Iterable) and not isinstance(
              proto_message, str
          ):
              # Repeated protobuf message
              for e in proto_message:
                  updateBlobFileName(e, new_path)
  
      assert len(models) > 1
      if compute_units is not None and not isinstance(
          compute_units, _ComputeUnit
      ):
          raise TypeError(
              '"compute_units" parameter must be None or of type coremltools.ComputeUnit'
          )
  
      if compute_units is None:
          all_compute_units_the_same = all(
              map(
                  lambda m: models[0].compute_unit is m.compute_unit,
                  models[1:],
              )
          )
          if not all_compute_units_the_same:
              raise ValueError(
                  'Models have different compute_unit values. The "compute_units" parameter must be specified.'
              )
          compute_units = models[0].compute_unit
  
      if (
          compute_units == _ComputeUnit.CPU_AND_NE
          and _is_macos()
          and _macos_version() < (13, 0)
      ):
          raise ValueError(
              "coremltools.ComputeUnit.CPU_AND_NE is only available on macOS >= 13.0"
          )
  
      input_specs = list(map(lambda m: m.get_spec(), models))
  
      pipeline_spec = _ct.proto.Model_pb2.Model()
      pipeline_spec.specificationVersion = max(
          map(lambda spec: spec.specificationVersion, input_specs)
      )
  
      # If a later model doesn't get an input from a previous model, it must be
      # an input to the pipeline.
      available_as_input = set()
      for cur_spec in input_specs:
          for cur_input in cur_spec.description.input:
              if cur_input.name not in available_as_input:
                  pipeline_spec.description.input.add().MergeFrom(
                      cur_input
                  )
                  available_as_input.add(cur_input.name)
          available_as_input.update(
              [i.name for i in cur_spec.description.output]
          )
  
      # If an output for a model is not used as input for a later model, assume it
      # should be an output to the pipeline.
      used_as_input = set()
      for cur_spec in input_specs[
          ::-1
      ]:  # iterate overs specs in reverse
          for cur_output in cur_spec.description.output:
              if cur_output.name not in used_as_input:
                  pipeline_spec.description.output.add().MergeFrom(
                      cur_output
                  )
          used_as_input.update(
              [i.name for i in cur_spec.description.input]
          )
  
      # Map input shapes to output shapes
      var_name_to_type = {}
      for i in range(len(input_specs) - 1):
          for j in input_specs[i + 1].description.input:
              var_name_to_type[j.name] = j.type
  
          for j in input_specs[i].description.output:
              # If shape is already present, don't override it
              if (
                  j.type.WhichOneof("Type") == "multiArrayType"
                  and len(j.type.multiArrayType.shape) != 0
              ):
                  continue
  
              if j.name in var_name_to_type:
                  j.type.CopyFrom(var_name_to_type[j.name])
  
      # Update each model's spec to have a unique weight filename
      for i, cur_spec in enumerate(input_specs):
          if cur_spec.WhichOneof("Type") == "mlProgram":
              new_file_path = f"@model_path/weights/{i}-weight.bin"
              updateBlobFileName(cur_spec.mlProgram, new_file_path)
          pipeline_spec.pipeline.models.append(cur_spec)
  
      mlpackage_path = _create_mlpackage(pipeline_spec)
      dst = (
          mlpackage_path
          + "/Data/"
          + _MLPACKAGE_AUTHOR_NAME
          + "/"
          + _WEIGHTS_DIR_NAME
      )
      _os.mkdir(dst)
  
      # Copy and rename each model's weight file
      for i, cur_model in enumerate(models):
          if cur_model.weights_dir is not None:
              weight_file_path = (
                  cur_model.weights_dir + "/" + _WEIGHTS_FILE_NAME
              )
              if _os.path.exists(weight_file_path):
                  _shutil.copyfile(
                      weight_file_path, dst + f"/{i}-weight.bin"
                  )
  
      return _ct.models.MLModel(
          pipeline_spec, compute_units=compute_units, weights_dir=dst
      )
  
imports: from collections.abc import Iterable
  from functools import lru_cache
  import math
  import os
  import shutil
  import subprocess
  import sys
  import tempfile
  from typing import Optional, Union
  import warnings
  import numpy
  import coremltools
  from coremltools import ComputeUnit
  from coremltools.converters.mil.mil.passes.defs.preprocess import NameSanitizer
  from coremltools.proto import Model_pb2
  import coremltools.proto.MIL_pb2
  from .._deps import _HAS_SCIPY
label_desc: Use image node_id 31 for calling a global function with example usage: make_pipeline() and returns: _ct