code: def mutually_broadcastable_shapes(
      __reserved=not_set,  # type: Any
      num_shapes=not_set,  # type: Union[UniqueIdentifier, int]
      signature=not_set,  # type: Union[UniqueIdentifier, str]
      base_shape=(),  # type: Shape
      min_dims=0,  # type: int
      max_dims=None,  # type: int
      min_side=1,  # type: int
      max_side=None,  # type: int
  ):
      # type: (...) -> st.SearchStrategy[BroadcastableShapes]
      """Return a strategy for generating a specified number of shapes, N, that are
      mutually-broadcastable with one another and with the provided "base-shape".
  
      The strategy will generate a named-tuple of:
  
      * input_shapes: the N generated shapes
      * result_shape: the resulting shape, produced by broadcasting the
        N shapes with the base-shape
  
      Each shape produced from this strategy shrinks towards a shape with length
      ``min_dims``. The size of an aligned dimension shrinks towards being having
      a size of 1. The size of an unaligned dimension shrink towards ``min_side``.
  
      * ``num_shapes`` The number of mutually broadcast-compatible shapes to generate.
      * ``base-shape`` The shape against which all generated shapes can broadcast.
        The default shape is empty, which corresponds to a scalar and thus does not
        constrain broadcasting at all.
      * ``min_dims`` The smallest length that any generated shape can possess.
      * ``max_dims`` The largest length that any generated shape can possess.
        It cannot exceed 32, which is the greatest supported dimensionality for a
        numpy array. The default-value for ``max_dims`` is
        ``2 + max(len(shape), min_dims)``, capped at 32.
      * ``min_side`` The smallest size that an unaligned dimension can possess.
      * ``max_side`` The largest size that an unaligned dimension can possess.
        The default value is 2 + 'size-of-largest-aligned-dimension'.
  
      The following are some examples drawn from this strategy.
  
      .. code-block:: pycon
  
          >>> # Draw three shapes, and each shape is broadcast-compatible with `(2, 3)`
          >>> for _ in range(5):
          ...     mutually_broadcastable_shapes(num_shapes=3, base_shape=(2, 3)).example()
          BroadcastableShapes(input_shapes=((4, 1, 3), (4, 2, 3), ()), result_shape=(4, 2, 3))
          BroadcastableShapes(input_shapes=((3,), (1,), (2, 1)), result_shape=(2, 3))
          BroadcastableShapes(input_shapes=((3,), (1, 3), (2, 3)), result_shape=(2, 3))
          BroadcastableShapes(input_shapes=((), (), ()), result_shape=(2, 3))
          BroadcastableShapes(input_shapes=((3,), (), (3,)), result_shape=(2, 3))
  
      **Use with Generalised Universal Function signatures**
  
      A :np-ref:`universal function <ufuncs.html>` (or ufunc for short) is a function
      that operates on ndarrays in an element-by-element fashion, supporting array
      broadcasting, type casting, and several other standard features.
      A :np-ref:`generalised ufunc <c-api.generalized-ufuncs.html>` operates on
      sub-arrays rather than elements, based on the "signature" of the function.
      Compare e.g. :obj:`numpy:numpy.add` (ufunc) to :obj:`numpy:numpy.matmul` (gufunc).
  
      To generate shapes for a gufunc, you can pass the ``signature`` argument instead of
      ``num_shapes``.  This must be a gufunc signature string; which you can write by
      hand or access as e.g. ``np.matmul.signature`` on generalised ufuncs.
  
      In this case, the ``side`` arguments are applied to the 'core dimensions' as well,
      ignoring any frozen dimensions.  ``base_shape``  and the ``dims`` arguments are
      applied to the 'loop dimensions', and if necessary, the dimensionality of each
      shape is silently capped to respect the 32-dimension limit.
  
      The generated ``result_shape`` is the real result shape of applying the gufunc
      to arrays of the generated ``input_shapes``, even where this is different to
      broadcasting the loop dimensions.
  
      gufunc-compatible shapes shrink their loop dimensions as above, towards omitting
      optional core dimensions, and smaller-size core dimensions.
  
      .. code-block:: pycon
  
          >>> # np.matmul.signature == "(m?,n),(n,p?)->(m?,p?)"
          >>> for _ in range(3):
          ...     mutually_broadcastable_shapes(signature=np.matmul.signature).example()
          BroadcastableShapes(input_shapes=((2,), (2,)), result_shape=())
          BroadcastableShapes(input_shapes=((3, 4, 2), (1, 2)), result_shape=(3, 4))
          BroadcastableShapes(input_shapes=((4, 2), (1, 2, 3)), result_shape=(4, 3))
      """
      if __reserved is not not_set:
          raise InvalidArgument("Do not pass the __reserved argument.")
  
      arg_msg = "Pass either the `num_shapes` or the `signature` argument, but not both."
      if num_shapes is not not_set:
          check_argument(signature is not_set, arg_msg)
          check_type(integer_types, num_shapes, "num_shapes")
          assert isinstance(num_shapes, integer_types)  # for mypy
          check_argument(
              num_shapes >= 1,
              "num_shapes={} must be at least 1",
              num_shapes,
          )
          parsed_signature = None
          sig_dims = 0
      else:
          check_argument(signature is not not_set, arg_msg)
          if signature is None:
              raise InvalidArgument(
                  "Expected a string, but got invalid signature=None.  "
                  "(maybe .signature attribute of an element-wise ufunc?)"
              )
          check_type(string_types, signature, "signature")
          parsed_signature = _hypothesis_parse_gufunc_signature(
              signature
          )
          sig_dims = min(
              map(
                  len,
                  parsed_signature.input_shapes
                  + (parsed_signature.result_shape,),
              )
          )
          num_shapes = len(parsed_signature.input_shapes)
          assert num_shapes >= 1
  
      check_type(tuple, base_shape, "base_shape")
      strict_check = max_dims is not None
      check_type(integer_types, min_side, "min_side")
      check_type(integer_types, min_dims, "min_dims")
  
      if max_dims is None:
          max_dims = min(
              32 - sig_dims, max(len(base_shape), min_dims) + 2
          )
      else:
          check_type(integer_types, max_dims, "max_dims")
  
      if max_side is None:
          max_side = (
              max(tuple(base_shape[-max_dims:]) + (min_side,)) + 2
          )
      else:
          check_type(integer_types, max_side, "max_side")
  
      order_check("dims", 0, min_dims, max_dims)
      order_check("side", 0, min_side, max_side)
  
      if 32 - sig_dims < max_dims:
          if sig_dims == 0:
              raise InvalidArgument("max_dims cannot exceed 32")
          raise InvalidArgument(
              "max_dims=%r would exceed the 32-dimension limit given signature=%r"
              % (signature, parsed_signature)
          )
  
      dims, bnd_name = (
          (max_dims, "max_dims")
          if strict_check
          else (min_dims, "min_dims")
      )
  
      # check for unsatisfiable min_side
      if not all(
          min_side <= s for s in base_shape[::-1][:dims] if s != 1
      ):
          raise InvalidArgument(
              "Given base_shape=%r, there are no broadcast-compatible "
              "shapes that satisfy: %s=%s and min_side=%s"
              % (base_shape, bnd_name, dims, min_side)
          )
  
      # check for unsatisfiable [min_side, max_side]
      if not (
          min_side <= 1 <= max_side
          or all(s <= max_side for s in base_shape[::-1][:dims])
      ):
          raise InvalidArgument(
              "Given base_shape=%r, there are no broadcast-compatible shapes "
              "that satisfy all of %s=%s, min_side=%s, and max_side=%s"
              % (base_shape, bnd_name, dims, min_side, max_side)
          )
  
      if not strict_check:
          # reduce max_dims to exclude unsatisfiable dimensions
          for n, s in zip(range(max_dims), reversed(base_shape)):
              if s < min_side and s != 1:
                  max_dims = n
                  break
              elif not (min_side <= 1 <= max_side or s <= max_side):
                  max_dims = n
                  break
  
      return MutuallyBroadcastableShapesStrategy(
          num_shapes=num_shapes,
          signature=parsed_signature,
          base_shape=base_shape,
          min_dims=min_dims,
          max_dims=max_dims,
          min_side=min_side,
          max_side=max_side,
      )
  
imports: from __future__ import absolute_import, division, print_function
  import math
  import re
  from collections import namedtuple
  import numpy
  import hypothesis.internal.conjecture.utils
  import hypothesis.strategies._internal.core
  from hypothesis import Verbosity, assume
  from hypothesis._settings import note_deprecation
  from hypothesis.errors import InvalidArgument
  from hypothesis.internal.compat import PY2, hrange, integer_types, quiet_raise, string_types
  from hypothesis.internal.coverage import check_function
  from hypothesis.internal.reflection import proxies, reserved_means_kwonly_star
  from hypothesis.internal.validation import check_type, check_valid_interval
  from hypothesis.reporting import current_verbosity
  from hypothesis.strategies._internal import SearchStrategy
  from hypothesis.utils.conventions import UniqueIdentifier, not_set
label_desc: Use image node_id 25 for calling a global function with example usage: mutually_broadcastable_shapes(__reserved, num_shapes, signature, base_shape, min_dims, max_dims, min_side, max_side) and returns: MutuallyBroadcastableShapesStrategy