@Override
    protected OrderedTensorType lazyGetType() {
        if (inputs.size() < 1 || inputs.get(0).type().isEmpty()) {
            return null;
        }
        OrderedTensorType dataType = inputs.get(0).type().get();

        // required as we use tensor create
        inputs.get(0).exportAsRankingFunction = true;

        // Todo: only supports opsets 1-9, for >= get these from inputs
        int[] startsInput = attributeListAsArray("starts", 0);
        int[] endsInput = attributeListAsArray("ends", 0);
        int[] stepsInput = new int[dataType.rank()]; Arrays.fill(stepsInput, 1);  // Todo: get from input when opset >= 10

        int[] axes;
        if (attributes.getList("axes").isPresent()) {
            axes = attributeListAsArray("axes", 0);
        } else {
            // infer axes: default is [0, 1, ..., len('starts')-1]
            axes = new int[startsInput.length];
            for (int i = 0; i < startsInput.length; ++i) {
                axes[i] = i;
            }
        }

        if (startsInput.length != endsInput.length) {
            throw new IllegalArgumentException("Slice in " + name + ": 'starts' and 'ends' indexes are not of the same size.");
        }
        if (startsInput.length != axes.length) {
            throw new IllegalArgumentException("Slice in " + name + ": 'axes' and 'starts' are not of same size.");
        }

        int[] dimensionSizes = new int [dataType.rank()];
        for (int i = 0; i < dataType.rank(); ++i) {
            dimensionSizes[i] = dataType.dimensions().get(i).size().get().intValue();
        }

        starts = new int[dataType.rank()]; Arrays.fill(starts, 0);
        ends = new int[dataType.rank()];
        steps = new int[dataType.rank()]; Arrays.fill(steps, 1);

        for (int i = 0; i < axes.length; ++i) {
            int axis = axes[i];
            int start = startsInput[i];
            int end = endsInput[i];
            int step = stepsInput[i];

            axis = Math.min(axis, dataType.rank() - 1);
            axis = axis < 0 ? axis + dataType.rank() : axis;

            start = Math.min(start, dimensionSizes[axis]);
            start = start < 0 ? start + dimensionSizes[axis] : start;

            end = Math.min(end, dimensionSizes[axis]);
            end = end < 0 ? end + dimensionSizes[axis] : end;

            // Todo: check negative values for step size

            starts[axis] = start;
            steps[axis] = step;

            if (step == 0) {
                throw new IllegalArgumentException("Slice in " + name + ": illegal step size of 0.");
            }
            if ((end - start) < 1) {
                throw new IllegalArgumentException("Slice in " + name + ": illegal start (" + start + ") and end (" + end + ") index.");
            }
            dimensionSizes[axis] = (end - start) / step;
        }

        OrderedTensorType.Builder typeBuilder = new OrderedTensorType.Builder(resultValueType());
        for (int i = 0; i < dataType.rank(); ++i) {
            addDimension(i, dimensionSizes[i], typeBuilder);
        }
        return typeBuilder.build();
    }
Onnx slice operation.

Opset 1 to 9 accepts starts, ends, and axes tensors as attributes

Opset 10 and up accepts starts, ends, axes, and steps tensors as inputs. Here we assume these are
constants, otherwise we can't import this model because that would mean we
would not know the resulting tensor type until run-time, and that is currently
not supported in Vespa.
model-integration/src/main/java/ai/vespa/rankingexpression/importer/operations/Slice.java