def annotate_binary_op_act_pattern(
    model: _torch.fx.GraphModule,
    pattern_gm: _torch.fx.GraphModule,
    quantization_config: _Optional[_AnnotationConfig],
    filter_fn: _Optional[_Callable[[_Node], bool]] = None,
) -> _Optional[_List[_List[_Node]]]:
    """
    Annotates all nodes in ``model``, which match the pattern specified by ``pattern_gm``
    using ``quantization_config``.

    ``pattern_gm`` captures patterns of the following type:

    input_1 ---
               \
                --> binary_op -> activation -> output
               /
    input_2 ---

    activation may or may not be applied in the pattern.

    Only annotates those patterns in which all nodes return True when ``filter_fn`` is applied
    to them.
    """
    model.graph.eliminate_dead_code()
    model.recompile()

    matcher = _SubgraphMatcherWithNameNodeMap(pattern_gm, ignore_literals=True)
    matches = matcher.match(model.graph)

    annotated_partitions = []
    for match in matches:
        name_node_map = match.name_node_map
        binary_op_node: _Node = name_node_map["binary_op"]
        if "output" in name_node_map:
            output_node = name_node_map["output"]
            # In this case, an activation is applied to the weighted module output
            output_node = name_node_map["output"]
            # If the output is same as binary_op_node, it means we have an inplace activation,
            # so we need to correct the binary_op_node
            if binary_op_node == output_node:
                binary_op_node = binary_op_node.args[0]
            partition = [output_node, binary_op_node]
        else:
            output_node = None
            partition = [binary_op_node]

        if output_node is not None and len(binary_op_node.users) > 1:
            raise ValueError("Binary op with activation has more than one users.")

        if _is_annotated(partition):
            continue

        if filter_fn and any(not filter_fn(n) for n in partition):
            continue

        input_act_qspec = (
            quantization_config.input_activation if quantization_config else None
        )
        output_act_qspec = (
            quantization_config.output_activation if quantization_config else None
        )

        input_qspec_map = {}
        input_act0 = binary_op_node.args[0]
        if isinstance(input_act0, _Node):
            input_qspec_map[input_act0] = input_act_qspec

        input_act1 = binary_op_node.args[1]
        if isinstance(input_act1, _Node):
            input_qspec_map[input_act1] = input_act_qspec

        if output_node is None:
            binary_op_node.meta["quantization_annotation"] = _QuantizationAnnotation(
                input_qspec_map=input_qspec_map,
                output_qspec=output_act_qspec,
                _annotated=True,
            )
        else:
            binary_op_node.meta["quantization_annotation"] = _QuantizationAnnotation(
                input_qspec_map=input_qspec_map,
                _annotated=True,
            )
            output_act_qspec = _adjust_activation_qspec(
                node=output_node, qspec=output_act_qspec
            )
            output_node.meta["quantization_annotation"] = _QuantizationAnnotation(
                output_qspec=output_act_qspec,
                _annotated=True,
            )
        _mark_nodes_as_annotated(partition)
        annotated_partitions.append(partition)
    return annotated_partitions

    Annotates all nodes in ``model``, which match the pattern specified by ``pattern_gm``
    using ``quantization_config``.

    ``pattern_gm`` captures patterns of the following type:

    input_1 ---
                               --> binary_op -> activation -> output
               /
    input_2 ---

    activation may or may not be applied in the pattern.

    Only annotates those patterns in which all nodes return True when ``filter_fn`` is applied
    to them.
    
coremltools/optimize/torch/quantization/_coreml_quantizer_utils.py