def transform_conv_crop(spec):
    """
    Transforms Conv -> Crop -> BN (if present) -> Activation (if present) into
               Conv -> BN (if present) -> Activation (if present) -> Crop
    This transformation will allow Conv -> BN -> Activation fusion by changing
    the position of the crop layer, which does not affect the computation
    """
    # Collect metadata
    out_degree = _get_blob_out_degree(spec)
    network_output_names = _get_network_output(spec)

    nn_spec = _get_nn_spec(spec)
    nn_layers = nn_spec.layers
    for i in range(0, len(nn_layers) - 2):

        # If Convolution output is being using as a network output or more than one layers
        # that's acceptable
        if not _is_layer(nn_layers[i], "convolution"):
            continue

        # Output of Crop layer must not be network output or used by more than one layer
        if not (
            _is_layer(nn_layers[i + 1], "crop")
            and _get_input(nn_layers[i + 1]) not in network_output_names
            and out_degree[_get_output(nn_layers[i + 1])] == 1
        ):
            continue

        layer_to_shuffle_with = -1

        # Output of Batchnorm layer must not be network output or used by more than one layer
        if (
            _is_layer(nn_layers[i + 2], "batchnorm")
            and out_degree[_get_output(nn_layers[i + 2])] == 1
        ):
            layer_to_shuffle_with = i + 2

        # Output of Activation layer must not be network output or used by more than one layer
        if (
            i + 3 < len(nn_layers)
            and _is_layer(nn_layers[i + 3], "activation")
            and out_degree[_get_output(nn_layers[i + 3])] == 1
        ):
            layer_to_shuffle_with = i + 3

        if layer_to_shuffle_with == -1:
            continue
        # restructure crop layer
        # Conv --->  Crop  ---> BN ---> Activation ---> Layer1
        # In following three steps
        # 1. Conv --------------> BN ---> Activation ---> Layer1
        #        \            /
        #         ---> Crop --
        nn_layers[i].output[0] = nn_layers[i + 1].output[0]
        # 2. Conv ---> BN ---> Activation ---> Layer1
        #      \                           /
        #        -----------------Crop ----
        nn_layers[i + 1].output[0] = nn_layers[layer_to_shuffle_with].output[0]
        # 3. Conv ---> BN ---> Activation ---> Crop ---> Layer1
        nn_layers[layer_to_shuffle_with].output[0] = nn_layers[i + 1].input[0]

        # Add Crop layer at new position and remove from current position
        crop_layer = nn_layers[i + 1]
        nn_layers.remove(crop_layer)
        nn_layers.insert(layer_to_shuffle_with, crop_layer)

    Transforms Conv -> Crop -> BN (if present) -> Activation (if present) into
               Conv -> BN (if present) -> Activation (if present) -> Crop
    This transformation will allow Conv -> BN -> Activation fusion by changing
    the position of the crop layer, which does not affect the computation
    
coremltools/converters/mil/backend/nn/passes/mlmodel_passes.py