def arg_to_proto(value: ARG_LIKE, *, out: v2.program_pb2.Arg | None = None) -> v2.program_pb2.Arg:
    """Writes an argument value into an Arg proto.

    Args:
        value: The value to encode.
        out: The proto to write the result into. Defaults to a new instance.

    Returns:
        The proto that was written into.

    Raises:
        ValueError: if the object holds unsupported values.
    """
    msg = v2.program_pb2.Arg() if out is None else out

    if isinstance(value, (bool, np.bool_)):
        msg.arg_value.bool_value = bool(value)
    elif isinstance(value, FLOAT_TYPES):
        msg.arg_value.float_value = float(value)
    elif isinstance(value, complex):
        msg.arg_value.complex_value.real_value = value.real
        msg.arg_value.complex_value.imag_value = value.imag
    elif isinstance(value, bytes):
        msg.arg_value.bytes_value = value
    elif isinstance(value, str):
        msg.arg_value.string_value = value
    elif isinstance(value, (list, tuple, np.ndarray, set, frozenset)):
        if isinstance(value, np.ndarray):
            _ndarray_to_proto(value, out=msg)
        elif len(value) == 0:
            # Convert empty list or tuple
            _tuple_to_proto(value, out=msg.arg_value.tuple_value)
        elif isinstance(value, list) and isinstance(value[0], str):
            # Note that we should not convert a tuple to a list here
            # in order to preserve types
            if not all(isinstance(x, str) for x in value):
                # Not a uniform list, convert to tuple
                _tuple_to_proto(value, out=msg.arg_value.tuple_value)
                return msg
            msg.arg_value.string_values.values.extend(str(x) for x in value)
        else:
            # This is a numerical field.
            numerical_fields = [
                [msg.arg_value.bool_values.values, (bool, np.bool_)],
                [msg.arg_value.int64_values.values, (int, np.integer, bool)],
                [msg.arg_value.double_values.values, (float, np.floating, int, bool)],
            ]
            cur_index = 0
            non_numerical = None
            for v in value:
                while cur_index < len(numerical_fields) and not isinstance(
                    v, numerical_fields[cur_index][1]
                ):
                    cur_index += 1
                if cur_index == len(numerical_fields):
                    non_numerical = v
                    break

            if non_numerical is not None:
                # Not a uniform list, convert to tuple
                _tuple_to_proto(value, out=msg.arg_value.tuple_value)
                return msg
            field, types_tuple = numerical_fields[cur_index]
            field.extend(types_tuple[0](x) for x in value)
    elif isinstance(value, tunits.Value):
        msg.arg_value.value_with_unit.MergeFrom(value.to_proto())
    elif isinstance(value, MeasurementKey):
        msg.measurement_key.string_key = value.name
        msg.measurement_key.path.extend(value.path)
    else:
        _arg_func_to_proto(value, msg)

    return msg
Writes an argument value into an Arg proto.

    Args:
        value: The value to encode.
        out: The proto to write the result into. Defaults to a new instance.

    Returns:
        The proto that was written into.

    Raises:
        ValueError: if the object holds unsupported values.
    
cirq-google/cirq_google/serialization/arg_func_langs.py