Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [Arith] Merge surjective/non-surjective iter mapping detections by wrongtest-intellif · Pull Request #11287 · apache/tvm · GitHub
Skip to content

[Arith] Merge surjective/non-surjective iter mapping detections - #11287

Merged
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply
May 31, 2022
Merged

[Arith] Merge surjective/non-surjective iter mapping detections#11287
vinx13 merged 6 commits into
apache:mainfrom
wrongtest-intellif:simplify_floormod_after_multiply

Conversation

@wrongtest-intellif

@wrongtest-intellifwrongtest-intellif commented May 12, 2022

Copy link
Copy Markdown
Contributor

Update a simplify rule when c2 is nonzero, original rule is covered with constant folding.
floormod(x * c1, c2) =>
floormod(x * (floordiv(c1, c2) * c2 + floormod(c1, c2)), c2) =>
floormod(x * floormod(c1, c2)), c2)

This is useful for certain non-perfect tiling case, where there are dynamic loop ranges which is actually constant wrt outer loop domain.

For example, floordiv(floormod(x * 360, 16) + 359, 16) with x in [0, 2) can finally reduce to constant 22, since the rule could eliminate the multiply factor 360 to 360 % 16, activating more available rules.

Unfortunately the working example on tiling encounter a region_cover related problem again.

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

where (or is it neccesary) to write testcase on analyzer.simplify()'s behavior ?

@tqchen

Copy link
Copy Markdown
Member

@wrongtest yes we should cover simplifier's behavior, but the rewrite_simplifier testcase should be sufficient for now

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from bf9c28d to d4d439dCompareMay 13, 2022 07:27
@tqchen
tqchen requested a review from vinx13May 13, 2022 15:17
@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

The failed compute_at's region cover check possibly could get fixed by #11235 improvement on iteration analysis.

@vinx13

Copy link
Copy Markdown
Member

LGTM, let's have #11235 merged first

@wrongtest-intellif

Copy link
Copy Markdown
ContributorAuthor

To enable region cover proof on such cases, we need to lift DetectIterMapPadded to standard implementation for DetectIterMap.

@Hzfengsy

Copy link
Copy Markdown
Member

A gentle ping for @vinx13

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from d4d439d to 6795cb0CompareMay 23, 2022 13:03
@wrongtest-intellifwrongtest-intellif changed the title [Arith][Simplify] Extend simplify rule for floormod(x * c1 + y, c2) [Arith] Merge surjective/non-surjective iter mapping detectionsMay 23, 2022
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 6795cb0 to 700b702CompareMay 23, 2022 18:21
@vinx13

Copy link
Copy Markdown
Member

@wrongtest Can you elaborate the usage of DetectIterMapPadded in our analysis? Do we need the padding information?

also cc @Lunderberg for DetectIterMap changes

@wrongtest-intellif

wrongtest-intellif commented May 23, 2022

Copy link
Copy Markdown
ContributorAuthor

usage of DetectIterMapPadded in our analysis

Try merge DetectIterMapPadded and DetectIterMap into the same interface, and replace option require_bijective with a new enum IterMapLevel with three alternatives:

  • Bijective
    for original behavior on require_bijective=true
  • Surjective
    for original behavior on require_bijective=false
  • Injective
    for behavior of DetectIterMapPadded

The #11235 brings great way to analyze iteration form like (x + 7) // 16 with padding. The surjective checking of DetectIterMap is used many where (like region cover check after schedule step), however, it can not leverage this analysis now, it is checked to take no padding predicate.

I think actually, as an example, though (x + 7) // 16 is rewritten into a "padded" iteration form, we could still prove the mapping is surjective, since the left and right padding is no more than the largest divisor by how we pad it. If we extent CheckMapping rules carefully, we may be able to distinguish that

  • (x + 7) // 16 -> surjective
    • this is the access index form in my original failed case
  • (x + 7) % 16 -> surjective [0, 16) if x's extent is larger than 16
  • ((x + 7) // 16, (x + 7) % 16) -> non-surjective

So from my perspective it would be great if we have a uniform interface and share same padding based analysis. Ideally padding_predicate is not affected for IndexMap functionalities, and it should not introduce false positives in bijective/surjective checking. I'm still working to check more unittest cases and adapt padding analysis if surjective mapping is required.

Do we need the padding information

No, original usages of DetectIterMap do not require padding_predicate as before. But we need prove surjective-ness if padding is added for new iteration form supported by original DetectIterMapPadded .

@LunderbergLunderberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the DetectIterMap changes, and especially like the merging and de-duplication. Mostly just some nitpicks here and there.

Comment threadinclude/tvm/arith/iter_affine_map.h Outdated
Comment threadinclude/tvm/arith/iter_affine_map.h

// Step0.1: Check each index to determine required padding
bool allow_padding = !require_bijective;
bool allow_padding = check_level != IterMapLevel::Bijective;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would enable padding for IterMapLevel::Surjective, which I don't think is correct. Since padding is any output value for which no input value exists, any introduction of padding wouldn't be surjective.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the claim~ I try to change padding to iter mark itself.

For example,(x + 7)x in [0, 8) => IterMark(IterSplit(IterSum({x}, 7), lower_factor=1, extent=16, scale=1), extent=16 with left_pad=7, right_pad=1

Then (x + 7) // 8 is mapped to range [0, extent//2) == [0, 2), though we have padding into iter mark, the IterSplit's range can be achieved when we only iterate x in it's original domain: (0 + 7) // 8 = 0, (7 + 7) // 8 = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and that does maintain surjectivity for a single index. I'm not entirely sure for the case of two indices, though. For the same x ∈ [0,8), the indices [(x+7)//8, (x+7)%8] would have the same padding left_pad=7 and right_pad=1. Even though each individual index can take any value in the output ((x+7)//8 ∈[0,2) and (x+7)%8 ∈ [0,8)), there are some coordinate pairs that cannot be generated for any value of x (e.g. [0,0] and [1,7]).

@wrongtest-intellifwrongtest-intellifMay 25, 2022

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! This is where we should be careful. In CheckMapping with surjective mode when padding exists, we check padded // LCM and padded % LCM(or it's sub-splits) must not both exists. The case below depict this check:

sum=80+ydom_map=var_dom([(y, 176)])
# (80 + y) // 32 itself could be surjectiveassert_iter_sum_pattern(
{fld(sum, 32): (6, 2, 1)},
dom_map,
)
# (80 + y) % 2, ((80 + y) // 2) % 16) could be surjective,# since they can be seen as sub-splits of (80 + y) % 32assert_iter_sum_pattern(
{flm(fld(sum, 2), 16): (16, 0, 1), flm(sum, 2): (2, 0, 1)},
dom_map,
)
# but (80 + y) // 32, (80 + y) % 32 are not surjectiveassert_iter_sum_failure({fld(sum, 32), flm(sum, 32)}, dom_map)

Other kinds of negatives like (80 + y) // 32, (80 + y) // 4 would be banned by existing checking rule.

Comment threadsrc/arith/iter_affine_map.cc Outdated
requires_padding_ = requires_padding_ || (left_padding_introduced || right_padding_introduced);
padding_predicate_ = padding_predicate_ || (left_padding_predicate || right_padding_predicate);
}
// ICHECK(CanProveDivisible(info.padded->extent, split->lower_factor));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these // ICHECK lines be either uncommented or removed?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to check the padding factor is divisible by split->lower_factor, then the commented check can be ensured from context. I found it may fail unfortunetely due to simplifier's ability limitation when the padded extent contain complex flm/fld expressions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I noticed that there were also some simplification steps that needed to increase the number of iterations performed. Is the failure to prove divisibility related, since CanProveDivisible only uses the default of 2 steps?

(I'm also wondering if the default for Analyzer::Simplify should be to iterate until it the simplification converges, rather than using a fixed number of steps.)

Comment threadpython/tvm/arith/iter_affine_map.py Outdated
@junrushao

Copy link
Copy Markdown
Member

Quick note: #11235 is merged

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from a1a2086 to 1c15f4dCompareMay 25, 2022 09:45
Comment threadsrc/arith/iter_affine_map.cc
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch 2 times, most recently from f4280f0 to 001ed50CompareMay 25, 2022 20:36
Comment threadsrc/arith/iter_affine_map.cc Outdated

return analyzer_->CanProveEqual(dividend, divisor) ||
analyzer_->CanProve(floormod(dividend, divisor) == 0);
analyzer_->CanProve(analyzer_->Simplify(floormod(dividend, divisor), 8) == 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be great to have some explanations here that it need more simplification steps

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that is something forget to revert. There is some cases the division could not be proved like
floormod(0 + -x * 8, x) == 0, floormod(8*c1*c2, c1) == 0, even we increate iteration num. They get work-around here and there, for example,

if (CanProveDivisible(right_edge, divisor)) {
right_pad = 0;
} else {
right_pad = analyzer_->Simplify(floormod(-right_edge, divisor));
}

@Lunderberg suggest Simplify could be optimized to iterate until reaching fix point. But now it is suffice to work on existing tests.

@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 001ed50 to f24db1dCompareMay 28, 2022 06:33
@vinx13

Copy link
Copy Markdown
Member

Could you also update this line https://github.com/apache/tvm/blob/main/src/tir/schedule/primitive/layout_transformation.cc#L395? There are some conflict that CI didn't catch because of concurrent merge

- determine case like x % 16, x in [0, 5) to be non-surjective, since usages may treat the region extent as 16 by mistake.
- skip second round of rewrite when there is no padding
- fix some typo in comments
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from f24db1d to 48a16f1CompareMay 31, 2022 05:28
@wrongtest-intellif
wrongtest-intellifforce-pushed the simplify_floormod_after_multiply branch from 48a16f1 to 4d1239aCompareMay 31, 2022 05:52
@vinx13
vinx13 merged commit c1b22ee into apache:mainMay 31, 2022
@junrushao

Copy link
Copy Markdown
Member

One bug from my side is magically fixed by this PR!!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@wrongtest-intellif@tqchen@vinx13@Hzfengsy@junrushao@Lunderberg