Skip to content

[mlir][sparse] Enable explicit and implicit value in sparse encoding #88975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion mlir/include/mlir-c/Dialect/SparseTensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ mlirAttributeIsASparseTensorEncodingAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirSparseTensorEncodingAttrGet(
MlirContext ctx, intptr_t lvlRank,
MlirSparseTensorLevelType const *lvlTypes, MlirAffineMap dimToLvl,
MlirAffineMap lvlTodim, int posWidth, int crdWidth);
MlirAffineMap lvlTodim, int posWidth, int crdWidth,
MlirAttribute explicitVal, MlirAttribute implicitVal);

/// Returns the level-rank of the `sparse_tensor.encoding` attribute.
MLIR_CAPI_EXPORTED intptr_t
Expand Down Expand Up @@ -85,6 +86,14 @@ mlirSparseTensorEncodingAttrGetPosWidth(MlirAttribute attr);
MLIR_CAPI_EXPORTED int
mlirSparseTensorEncodingAttrGetCrdWidth(MlirAttribute attr);

/// Returns the explicit value of the `sparse_tensor.encoding` attribute.
MLIR_CAPI_EXPORTED MlirAttribute
mlirSparseTensorEncodingAttrGetExplicitVal(MlirAttribute attr);

/// Returns the implicit value of the `sparse_tensor.encoding` attribute.
MLIR_CAPI_EXPORTED MlirAttribute
mlirSparseTensorEncodingAttrGetImplicitVal(MlirAttribute attr);

MLIR_CAPI_EXPORTED unsigned
mlirSparseTensorEncodingAttrGetStructuredN(MlirSparseTensorLevelType lvlType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
- **soa** : only applicable to singleton levels, fuses the singleton
level in SoA (structure of arrays) scheme.

In addition to the map, the following two fields are optional:
In addition to the map, the following fields are optional:

- The required bitwidth for position storage (integral offsets
into the sparse storage scheme). A narrow width reduces the memory
Expand All @@ -183,6 +183,23 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
coordinate over all levels). The choices are `8`, `16`, `32`,
`64`, or, the default, `0` to indicate a native bitwidth.

- The explicit value for the sparse tensor. If explicitVal is set,
then all the non-zero values in the tensor have the same explicit value.
The default value Attribute() indicates that it is not set. This
is useful for binary-valued sparse tensors whose values can either
be an implicit value (0 by default) or an explicit value (such as 1).
In this approach, we don't store explicit/implicit values, and metadata
(such as position and coordinate arrays) alone fully defines the original tensor.
This yields additional savings for the storage requirements,
as well as for the computational time, since we skip operating on
implicit values and can constant fold the explicit values where they are used.

- The implicit value for the sparse tensor. If implicitVal is set,
then the "zero" value in the tensor is equal to the implicit value.
For now, we only support `0` as the implicit value but it could be
extended in the future. The default value Attribute() indicates that
the implicit value is `0` (same type as the tensor element type).

Examples:

```mlir
Expand Down Expand Up @@ -226,6 +243,15 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
}>
... tensor<8x8xf64, #DCSC> ...

// Doubly compressed sparse column storage with specific
// explicit and implicit values.
#DCSC = #sparse_tensor.encoding<{
map = (i, j) -> (j : compressed, i : compressed),
explicitVal = 1 : i64,
implicitVal = 0 : i64
}>
... tensor<8x8xi64, #DCSC> ...

// Block sparse row storage (2x3 blocks).
#BSR = #sparse_tensor.encoding<{
map = ( i, j ) ->
Expand Down Expand Up @@ -307,6 +333,12 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
// The required bitwidth for coordinate storage.
"unsigned":$crdWidth,

// The required explicit value.
"::mlir::Attribute":$explicitVal,

// The required implicit value.
"::mlir::Attribute":$implicitVal,

// A slice attribute for each dimension of the tensor type.
ArrayRefParameter<
"::mlir::sparse_tensor::SparseTensorDimSliceAttr",
Expand All @@ -319,14 +351,17 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
CArg<"AffineMap", "{}">:$dimToLvl,
CArg<"AffineMap", "{}">:$lvlToDim,
CArg<"unsigned", "0">:$posWidth,
CArg<"unsigned", "0">:$crdWidth), [{
CArg<"unsigned", "0">:$crdWidth,
CArg<"::mlir::Attribute", "{}">:$explicitVal,
CArg<"::mlir::Attribute", "{}">:$implicitVal), [{
if (!dimToLvl) {
dimToLvl = ::mlir::AffineMap::getMultiDimIdentityMap(lvlTypes.size(), $_ctxt);
}
if (!lvlToDim) {
lvlToDim = ::mlir::sparse_tensor::inferLvlToDim(dimToLvl, $_ctxt);
}
return $_get($_ctxt, lvlTypes, dimToLvl, lvlToDim, posWidth, crdWidth,
explicitVal, implicitVal,
ArrayRef<::mlir::sparse_tensor::SparseTensorDimSliceAttr>{});
}]>
];
Expand All @@ -353,6 +388,22 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
/// reset to the default, and all other fields inherited from `this`.
SparseTensorEncodingAttr withoutBitWidths() const;

/// Constructs a new encoding with the given explicit value
/// and all other fields inherited from `this`.
SparseTensorEncodingAttr withExplicitVal(Attribute explicitVal) const;

/// Constructs a new encoding with the explicit value
/// reset to the default, and all other fields inherited from `this`.
SparseTensorEncodingAttr withoutExplicitVal() const;

/// Constructs a new encoding with the given implicit value
/// and all other fields inherited from `this`.
SparseTensorEncodingAttr withImplicitVal(Attribute implicitVal) const;

/// Constructs a new encoding with the implicit value
/// reset to the default, and all other fields inherited from `this`.
SparseTensorEncodingAttr withoutImplicitVal() const;

/// Constructs a new encoding with the given dimSlices, and all
/// other fields inherited from `this`.
SparseTensorEncodingAttr withDimSlices(ArrayRef<::mlir::sparse_tensor::SparseTensorDimSliceAttr> dimSlices) const;
Expand Down
22 changes: 22 additions & 0 deletions mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ class SparseTensorType {
return withEncoding(enc.withoutBitWidths());
}

SparseTensorType withExplicitVal(Attribute explicitVal) const {
return withEncoding(enc.withExplicitVal(explicitVal));
}

SparseTensorType withoutExplicitVal() const {
return withEncoding(enc.withoutExplicitVal());
}

SparseTensorType withImplicitVal(Attribute implicitVal) const {
return withEncoding(enc.withImplicitVal(implicitVal));
}

SparseTensorType withoutImplicitVal() const {
return withEncoding(enc.withoutImplicitVal());
}

SparseTensorType
withDimSlices(ArrayRef<SparseTensorDimSliceAttr> dimSlices) const {
return withEncoding(enc.withDimSlices(dimSlices));
Expand Down Expand Up @@ -327,6 +343,12 @@ class SparseTensorType {
/// Returns the position-overhead bitwidth, defaulting to zero.
unsigned getPosWidth() const { return enc ? enc.getPosWidth() : 0; }

/// Returns the explicit value, defaulting to null Attribute for unset.
Attribute getExplicitVal() const { return enc.getExplicitVal(); }

/// Returns the implicit value, defaulting to null Attribute for 0.
Attribute getImplicitVal() const { return enc.getImplicitVal(); }

/// Returns the coordinate-overhead MLIR type, defaulting to `IndexType`.
Type getCrdType() const { return enc.getCrdElemType(); }

Expand Down
27 changes: 24 additions & 3 deletions mlir/lib/Bindings/Python/DialectSparseTensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,19 @@ static void populateDialectSparseTensorSubmodule(const py::module &m) {
[](py::object cls, std::vector<MlirSparseTensorLevelType> lvlTypes,
std::optional<MlirAffineMap> dimToLvl,
std::optional<MlirAffineMap> lvlToDim, int posWidth, int crdWidth,
MlirContext context) {
std::optional<MlirAttribute> explicitVal,
std::optional<MlirAttribute> implicitVal, MlirContext context) {
return cls(mlirSparseTensorEncodingAttrGet(
context, lvlTypes.size(), lvlTypes.data(),
dimToLvl ? *dimToLvl : MlirAffineMap{nullptr},
lvlToDim ? *lvlToDim : MlirAffineMap{nullptr}, posWidth,
crdWidth));
crdWidth, explicitVal ? *explicitVal : MlirAttribute{nullptr},
implicitVal ? *implicitVal : MlirAttribute{nullptr}));
},
py::arg("cls"), py::arg("lvl_types"), py::arg("dim_to_lvl"),
py::arg("lvl_to_dim"), py::arg("pos_width"), py::arg("crd_width"),
py::arg("context") = py::none(),
py::arg("explicit_val") = py::none(),
py::arg("implicit_val") = py::none(), py::arg("context") = py::none(),
"Gets a sparse_tensor.encoding from parameters.")
.def_classmethod(
"build_level_type",
Expand Down Expand Up @@ -97,6 +100,24 @@ static void populateDialectSparseTensorSubmodule(const py::module &m) {
mlirSparseTensorEncodingAttrGetPosWidth)
.def_property_readonly("crd_width",
mlirSparseTensorEncodingAttrGetCrdWidth)
.def_property_readonly(
"explicit_val",
[](MlirAttribute self) -> std::optional<MlirAttribute> {
MlirAttribute ret =
mlirSparseTensorEncodingAttrGetExplicitVal(self);
if (mlirAttributeIsNull(ret))
return {};
return ret;
})
.def_property_readonly(
"implicit_val",
[](MlirAttribute self) -> std::optional<MlirAttribute> {
MlirAttribute ret =
mlirSparseTensorEncodingAttrGetImplicitVal(self);
if (mlirAttributeIsNull(ret))
return {};
return ret;
})
.def_property_readonly(
"structured_n",
[](MlirAttribute self) -> unsigned {
Expand Down
26 changes: 18 additions & 8 deletions mlir/lib/CAPI/Dialect/SparseTensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,20 @@ bool mlirAttributeIsASparseTensorEncodingAttr(MlirAttribute attr) {
return isa<SparseTensorEncodingAttr>(unwrap(attr));
}

MlirAttribute
mlirSparseTensorEncodingAttrGet(MlirContext ctx, intptr_t lvlRank,
MlirSparseTensorLevelType const *lvlTypes,
MlirAffineMap dimToLvl, MlirAffineMap lvlToDim,
int posWidth, int crdWidth) {
MlirAttribute mlirSparseTensorEncodingAttrGet(
MlirContext ctx, intptr_t lvlRank,
MlirSparseTensorLevelType const *lvlTypes, MlirAffineMap dimToLvl,
MlirAffineMap lvlToDim, int posWidth, int crdWidth,
MlirAttribute explicitVal, MlirAttribute implicitVal) {
SmallVector<LevelType> cppLvlTypes;

cppLvlTypes.reserve(lvlRank);
for (intptr_t l = 0; l < lvlRank; ++l)
cppLvlTypes.push_back(static_cast<LevelType>(lvlTypes[l]));
return wrap(SparseTensorEncodingAttr::get(unwrap(ctx), cppLvlTypes,
unwrap(dimToLvl), unwrap(lvlToDim),
posWidth, crdWidth));

return wrap(SparseTensorEncodingAttr::get(
unwrap(ctx), cppLvlTypes, unwrap(dimToLvl), unwrap(lvlToDim), posWidth,
crdWidth, unwrap(explicitVal), unwrap(implicitVal)));
}

MlirAffineMap mlirSparseTensorEncodingAttrGetDimToLvl(MlirAttribute attr) {
Expand Down Expand Up @@ -91,6 +93,14 @@ int mlirSparseTensorEncodingAttrGetCrdWidth(MlirAttribute attr) {
return cast<SparseTensorEncodingAttr>(unwrap(attr)).getCrdWidth();
}

MlirAttribute mlirSparseTensorEncodingAttrGetExplicitVal(MlirAttribute attr) {
return wrap(cast<SparseTensorEncodingAttr>(unwrap(attr)).getExplicitVal());
}

MlirAttribute mlirSparseTensorEncodingAttrGetImplicitVal(MlirAttribute attr) {
return wrap(cast<SparseTensorEncodingAttr>(unwrap(attr)).getImplicitVal());
}

MlirSparseTensorLevelType mlirSparseTensorEncodingAttrBuildLvlType(
enum MlirSparseTensorLevelFormat lvlFmt,
const enum MlirSparseTensorLevelPropertyNondefault *properties,
Expand Down
Loading