Example #1
0
def test_small_graph():
    subgraph = TESubgraph([], None)
    part_a = InlinePart(
        subgraph,
        [
            Propagator(
                [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
                [0, 0],
            ),
            Propagator(
                [[0, 1, 0], [1, 0, 0], [0, 0, 1]],
                [-1, -1],
            ),
        ],
    )
    part_b = InlinePart(
        subgraph,
        [
            Propagator(
                [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
                [0, 0],
            ),
        ],
    )
    tensor_1 = Tensor([10, 10], "uint8")
    tensor_2 = Tensor([9, 9], "uint8")
    tensor_3 = Tensor([10, 10], "uint8")
    tensor_4 = Tensor([10, 10], "uint8")

    part_a.set_input(0, tensor_1)
    part_a.set_input(1, tensor_2)
    part_a.set_output(tensor_3)
    tensor_1.add_consumer(part_a)
    tensor_2.add_consumer(part_a)
    tensor_3.add_producer(part_a)
    part_b.set_input(0, tensor_3)
    part_b.set_output(tensor_4)
    tensor_3.add_consumer(part_b)
    tensor_4.add_producer(part_b)

    assert part_a.input_tensors == [tensor_1, tensor_2]
    assert part_a.output_tensor == tensor_3
    assert part_b.input_tensors == [tensor_3]
    assert part_b.output_tensor == tensor_4

    assert tensor_1.producers == []
    assert tensor_1.consumers == [part_a]
    assert tensor_2.producers == []
    assert tensor_2.consumers == [part_a]
    assert tensor_3.producers == [part_a]
    assert tensor_3.consumers == [part_b]
    assert tensor_4.producers == [part_b]
    assert tensor_4.consumers == []

    graph = CascaderGraph([tensor_1, tensor_2], [tensor_4])
    assert graph.input_tensors == [tensor_1, tensor_2]
    assert graph.output_tensors == [tensor_4]
    assert graph.part_order == [part_b, part_a]
    for i, part in enumerate(graph.part_order):
        assert graph.get_part_id(part) == i
Example #2
0
def test_inline_part():
    subgraph = TESubgraph([], None)
    part = InlinePart(
        subgraph,
        [
            Propagator(
                [[0, 1, 0], [1, 0, 0], [0, 0, 1]],
                [0, 0],
            ),
        ],
    )
    output_stripe_config = StripeConfig([2, 4], [8, 8], [2, 4], [1, 2], [4, 2],
                                        [0, 0])
    input_stripe_config = StripeConfig([4, 2], [8, 8], [4, 2], [2, 1], [2, 4],
                                       [0, 0])

    assert part.input_tensors == [None]
    assert part.output_tensor == None
    assert len(part.propagators) == 1
    assert part.in_line == True
    assert part.get_stripe_align_hint() == [1, 1]
    performance_info = part.get_performance_info(output_stripe_config,
                                                 is_rolling=False)
    assert performance_info.compute_cycles == 0
    assert performance_info.read_bytes == [0]
    assert performance_info.write_bytes == 0
    input_stripe_configs = part.calculate_input_stripe_configs(
        output_stripe_config)
    assert len(input_stripe_configs) == 1
    assert input_stripe_configs[0] == input_stripe_config
Example #3
0
def match_ethosu_conv2d(output_tensor):
    """Match a Tensor Expression corresponding to an NPU Conv2D.

    If the Tensor Expression matches, an EthosuPart will be created that models the
    matched Tensor Expression. Otherwise, None will be returned.

    Parameters
    ----------
    output_tensor : tvm.te.Tensor
        The tensor to attempt to match with.

    Returns
    -------
    Union[None, EthosuPart]
        The created EthosuPart if there was a match, otherwise None.

    """
    write = output_tensor
    if write.op.name != "ethosu_write":
        return None
    convert_to_nhcwb16 = write.op.input_tensors[0]
    if convert_to_nhcwb16.op.name != "ethosu_convert_to_nhcwb16":
        return None
    conv2d = convert_to_nhcwb16.op.input_tensors[0]
    if conv2d.op.name != "ethosu_conv2d":
        return None
    pad = conv2d.op.input_tensors[0]
    if pad.op.name != "ethosu_pad":
        return None
    convert_to_nhwc = pad.op.input_tensors[0]
    if convert_to_nhwc.op.name != "ethosu_convert_to_nhwc":
        return None
    read = convert_to_nhwc.op.input_tensors[0]
    if read.op.name != "ethosu_read":
        return None

    input_tensors = [
        read.op.input_tensors[0],
        conv2d.op.input_tensors[1],
        conv2d.op.input_tensors[2],
    ]
    subgraph = TESubgraph(input_tensors, output_tensor)
    propagators = [
        write.op.attrs["ifm_propagator"],
        write.op.attrs["weights_propagator"],
        write.op.attrs["bias_propagator"],
    ]
    # TODO(@jacobbohlin) Both the output_quantum and quantum_cycles here are placeholders,
    # needs true implementation.
    if convert_to_nhcwb16.op.attrs["layout"] == "NHWC":
        output_quantum = [1, 2, 2, 1]
    else:
        output_quantum = [1, 2, 1, 2, 1]
    quantum_cycles = 1000
    return EthosuPart(subgraph, propagators, output_quantum, quantum_cycles)
Example #4
0
File: inline.py Project: wenxcs/tvm
def match_ethosu_inline(output_tensor, device_config):
    """Match a Tensor Expression corresponding to an operator that will be inlined.

    If the Tensor Expression matches, an InlinePart will be created that models the
    matched Tensor Expression. Otherwise, None will be returned. This matcher is
    naive and assumes nothing about the compute of the Tensor Expression. Therefore,
    the resulting InlinePart will have full-tensor dependencies (i.e. each output
    element depends on every input element).

    Parameters
    ----------
    output_tensor : tvm.te.Tensor
        The tensor to attempt to match with.
    device_config : EthosuDeviceConfig
        Target device configuration

    Returns
    -------
    Union[None, InlinePart]
        The created InlinePart if there was a match, otherwise None.

    """
    if output_tensor.op.name not in INLINE_OPS:
        return None

    input_tensors = output_tensor.op.input_tensors
    propagators = []
    output_dims = len(output_tensor.shape)
    for input_tensor in input_tensors:
        input_dims = len(input_tensor.shape)
        transform_matrix = np.zeros((input_dims + 1, output_dims + 1))
        for i, axis in enumerate(input_tensor.shape):
            transform_matrix[i, output_dims] = int(axis)
        transform_matrix[input_dims, output_dims] = 1
        offset_vector = np.zeros(input_dims, dtype="int64")
        propagators.append(
            Propagator(
                transform_matrix.tolist(),
                offset_vector.tolist(),
            ))

    subgraph = TESubgraph(input_tensors, output_tensor)
    return InlinePart(
        subgraph,
        propagators,
    )
Example #5
0
def match_ethosu_unary_elementwise(output_tensor, device_config):
    """Match a Tensor Expression corresponding to an NPU Unary Elementwise.

    If the Tensor Expression matches, an EthosuPart will be created that models the
    matched Tensor Expression. Otherwise, None will be returned.

    Parameters
    ----------
    output_tensor : tvm.te.Tensor
        The tensor to attempt to match with.
    device_config : EthosuDeviceConfig
        Target device configuration

    Returns
    -------
    Union[None, EthosuPart]
        The created EthosuPart if there was a match, otherwise None.

    """
    write = output_tensor
    if write.op.name != "ethosu_write":
        return None
    convert_to_nhcwb16 = write.op.input_tensors[0]
    if convert_to_nhcwb16.op.name != "ethosu_convert_to_nhcwb16":
        return None
    unary_elementwise = convert_to_nhcwb16.op.input_tensors[0]
    if unary_elementwise.op.name != "ethosu_unary_elementwise":
        return None
    pad = unary_elementwise.op.input_tensors[0]
    if pad.op.name != "ethosu_pad":
        return None
    convert_to_nhwc = pad.op.input_tensors[0]
    if convert_to_nhwc.op.name != "ethosu_convert_to_nhwc":
        return None
    read = convert_to_nhwc.op.input_tensors[0]
    if read.op.name != "ethosu_read":
        return None

    input_tensors = [
        read.op.input_tensors[0],
    ]
    subgraph = TESubgraph(input_tensors, output_tensor)
    propagators = [
        write.op.attrs["ifm_propagator"],
    ]
    ifm_dtype = input_tensors[0].dtype
    ofm_dtype = output_tensor.dtype

    output_layout = convert_to_nhcwb16.op.attrs["layout"]
    input_layout = convert_to_nhwc.op.attrs["layout"]
    output_quantum = device_config.get_output_quantum(output_layout)

    block_config = device_config.get_elementwise_block_config(
        propagators[0],
        None,
        unary_elementwise.op.attrs,
        output_tensor.shape,
        output_layout,
        input_layout,
        None,
        ifm_dtype,
        ofm_dtype,
    )

    return EthosuPart(
        subgraph,
        propagators,
        output_quantum,
        1,
        block_config,
    )
Example #6
0
def match_ethosu_conv2d(output_tensor, device_config):
    """Match a Tensor Expression corresponding to an NPU Conv2D.

    If the Tensor Expression matches, an EthosuPart will be created that models the
    matched Tensor Expression. Otherwise, None will be returned.

    Parameters
    ----------
    output_tensor : tvm.te.Tensor
        The tensor to attempt to match with.
    device_config : EthosuDeviceConfig
        Target device configuration

    Returns
    -------
    Union[None, EthosuPart]
        The created EthosuPart if there was a match, otherwise None.

    """
    write = output_tensor
    if write.op.name != "ethosu_write":
        return None
    convert_to_nhcwb16 = write.op.input_tensors[0]
    if convert_to_nhcwb16.op.name != "ethosu_convert_to_nhcwb16":
        return None
    conv2d = convert_to_nhcwb16.op.input_tensors[0]
    if conv2d.op.name != "ethosu_conv2d":
        return None
    pad = conv2d.op.input_tensors[0]
    if pad.op.name != "ethosu_pad":
        return None
    upscale = pad.op.input_tensors[0]
    if upscale.op.name != "ethosu_upscale":
        return None
    convert_to_nhwc = upscale.op.input_tensors[0]
    if convert_to_nhwc.op.name != "ethosu_convert_to_nhwc":
        return None
    read = convert_to_nhwc.op.input_tensors[0]
    if read.op.name != "ethosu_read":
        return None

    input_tensors = [
        read.op.input_tensors[0],
        conv2d.op.input_tensors[1],
        conv2d.op.input_tensors[2],
    ]

    subgraph = TESubgraph(input_tensors, output_tensor)
    propagators = [
        write.op.attrs["ifm_propagator"],
        write.op.attrs["weights_propagator"],
        write.op.attrs["bias_propagator"],
    ]
    ifm_dtype = input_tensors[0].dtype
    ofm_dtype = output_tensor.dtype

    ifm_channels = int(input_tensors[0].shape[3])
    ofm_channels, kernel_height, kernel_width = (
        int(axis) for axis in input_tensors[1].shape[0:3])
    kernel_elements = kernel_height * kernel_width

    is_part_kernel = device_config.is_partkernel(conv2d.op.name, ifm_channels,
                                                 ifm_dtype, kernel_elements)
    subkernels = len(
        device_config.get_kernel_steps(conv2d.op.name, kernel_height,
                                       kernel_width, ifm_dtype,
                                       is_part_kernel))

    output_layout = convert_to_nhcwb16.op.attrs["layout"]
    input_layout = convert_to_nhwc.op.attrs["layout"]
    output_quantum = device_config.get_output_quantum(output_layout)

    valid_block_configs = device_config.get_valid_block_configs(
        propagators[0],
        conv2d.op.attrs,
        output_tensor.shape,
        ofm_channels,
        ifm_channels,
        output_layout,
        input_layout,
        ifm_dtype,
        ofm_dtype,
        kernel_height,
        kernel_width,
    )

    return EthosuPart(
        subgraph,
        propagators,
        output_quantum,
        subkernels,
        valid_block_configs,
        1,
    )
Example #7
0
def match_ethosu_pooling(output_tensor, device_config):
    """Match a Tensor Expression corresponding to an NPU Pooling.

    If the Tensor Expression matches, an EthosuPart will be created that models the
    matched Tensor Expression. Otherwise, None will be returned.

    Parameters
    ----------
    output_tensor : tvm.te.Tensor
        The tensor to attempt to match with.
    device_config : EthosuDeviceConfig
        Target device configuration

    Returns
    -------
    Union[None, EthosuPart]
        The created EthosuPart if there was a match, otherwise None.

    """
    write = output_tensor
    if write.op.name != "ethosu_write":
        return None
    convert_to_nhcwb16 = write.op.input_tensors[0]
    if convert_to_nhcwb16.op.name != "ethosu_convert_to_nhcwb16":
        return None
    pool2d = convert_to_nhcwb16.op.input_tensors[0]
    if pool2d.op.name != "ethosu_pooling":
        return None
    pad = pool2d.op.input_tensors[0]
    if pad.op.name != "ethosu_pad":
        return None
    convert_to_nhwc = pad.op.input_tensors[0]
    if convert_to_nhwc.op.name != "ethosu_convert_to_nhwc":
        return None
    read = convert_to_nhwc.op.input_tensors[0]
    if read.op.name != "ethosu_read":
        return None

    input_tensors = [
        read.op.input_tensors[0],
    ]
    subgraph = TESubgraph(input_tensors, output_tensor)
    propagators = [
        write.op.attrs["ifm_propagator"],
    ]
    ifm_dtype = input_tensors[0].dtype
    ofm_dtype = output_tensor.dtype

    ifm_channels = int(input_tensors[0].shape[3])
    ofm_channels = ifm_channels
    pool_shape_h = int(pool2d.op.attrs["pool_shape_h"])
    pool_shape_w = int(pool2d.op.attrs["pool_shape_w"])

    subkernels = len(
        device_config.get_kernel_steps(pool2d.op.name, pool_shape_h, pool_shape_w, ifm_dtype)
    )

    output_layout = convert_to_nhcwb16.op.attrs["layout"]
    input_layout = convert_to_nhwc.op.attrs["layout"]
    output_quantum = device_config.get_output_quantum(output_layout)

    valid_block_configs = device_config.get_valid_block_configs(
        propagators[0],
        pool2d.op.attrs,
        output_tensor.shape,
        ofm_channels,
        ifm_channels,
        output_layout,
        input_layout,
        ifm_dtype,
        ofm_dtype,
        pool_shape_h,
        pool_shape_w,
    )

    return EthosuPart(
        subgraph,
        propagators,
        output_quantum,
        subkernels,
        valid_block_configs,
    )
Example #8
0
def match_ethosu_identity(output_tensor, device_config):
    """Match a Tensor Expression corresponding to an NPU identity.

    If the Tensor Expression matches, an EthosuPart will be created that models the
    matched Tensor Expression. Otherwise, None will be returned.

    Parameters
    ----------
    output_tensor : tvm.te.Tensor
        The tensor to attempt to match with.
    device_config : EthosuDeviceConfig
        Target device configuration

    Returns
    -------
    Union[None, EthosuPart]
        The created EthosuPart if there was a match, otherwise None.
    """
    write = output_tensor
    if write.op.name != "ethosu_write":
        return None
    identity = write.op.input_tensors[0]
    if identity.op.name != "ethosu_identity":
        return None
    read = identity.op.input_tensors[0]
    if read.op.name != "ethosu_read":
        return None

    input_tensors = [
        read.op.input_tensors[0],
    ]
    subgraph = TESubgraph(input_tensors, output_tensor)
    propagators = [
        write.op.attrs["ifm_propagator"],
    ]
    ifm_dtype = input_tensors[0].dtype
    ofm_dtype = output_tensor.dtype

    input_tensors_shape = input_tensors[0].shape
    length = len(input_tensors_shape)
    assert length <= 4, "Input tensor shape must be <= 4 for the identity operator"
    channels = int(input_tensors_shape[length - 1]) if length >= 3 else 1

    subkernels = len(
        device_config.get_kernel_steps(identity.op.name, 1, 1, ifm_dtype))

    input_layout = output_layout = "NHWC"
    output_quantum = device_config.get_output_quantum(output_layout)

    valid_block_configs = device_config.get_valid_block_configs(
        propagators[0],
        identity.op.attrs,
        output_tensor.shape,
        channels,
        channels,
        output_layout,
        input_layout,
        ifm_dtype,
        ofm_dtype,
        1,
        1,
    )

    return EthosuPart(
        subgraph,
        propagators,
        output_quantum,
        subkernels,
        valid_block_configs,
    )