Files
paddlepaddle--paddle/paddle/cinn/common/integer_set.cc
T
2026-07-13 12:40:42 +08:00

812 lines
28 KiB
C++

// Copyright (c) 2023 CINN Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/cinn/common/integer_set.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/utils/ir_copy.h"
#include "paddle/cinn/optim/replace_var_with_expr.h"
#include "paddle/cinn/optim/simplify_util.h"
#include "paddle/common/enforce.h"
namespace cinn {
namespace common {
CasInterval::CasInterval(ir::Expr expr_l, ir::Expr expr_r) {
VLOG(6) << "CasInterval is : [" << expr_l << ", " << expr_r << "].";
expr_r = ReplaceMinToConstant(expr_r);
expr_l = ReplaceMaxToConstant(expr_l);
expr_l = optim::ArithSimplify(expr_l);
expr_r = optim::ArithSimplify(expr_r);
VLOG(6) << "After simplify, CasInterval is : [" << expr_l << ", " << expr_r
<< "].";
if (expr_l.is_constant() && expr_r.is_constant()) {
PADDLE_ENFORCE_EQ(
expr_l->type().is_integer(),
true,
::common::errors::InvalidArgument("Expected expr_l to be an integer."));
PADDLE_ENFORCE_EQ(
expr_r->type().is_integer(),
true,
::common::errors::InvalidArgument("Expected expr_r to be an integer."));
l = expr_l.as_int64();
r = expr_r.as_int64();
return;
}
e_l = expr_l;
e_r = expr_r;
}
/**
* @brief Given an expr, visit it. If there is an ir::Min and its operands are 1
* constant value and 1 inconstant value, return the constant min value. For
* example, if a < min(5, b), then we get a < 5 and a < b. Using a < 5 to
* simplify the condition ensures correctness, though not sufficient.
*/
ir::Expr CasInterval::ReplaceMinToConstant(ir::Expr expr) {
ir::Expr copied = ir::ir_utils::IRCopy(expr);
struct Mutator : public ir::IRMutator<ir::Expr*> {
void operator()(ir::Expr* expr) { Visit(expr); }
void Visit(ir::Expr* expr) { ir::IRMutator<>::Visit(expr, expr); }
private:
void Visit(const ir::Min* op, ir::Expr* expr) override {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
auto min_a = op->a();
auto min_b = op->b();
if (min_a.is_constant() && !min_b.is_constant()) {
PADDLE_ENFORCE_EQ(
min_a->type().is_integer(),
true,
::common::errors::InvalidArgument("Min a should be an integer."));
*expr = ir::ir_utils::IRCopy(min_a);
} else if (min_b.is_constant() && !min_a.is_constant()) {
PADDLE_ENFORCE_EQ(
min_b->type().is_integer(),
true,
::common::errors::InvalidArgument("Min b should be an integer."));
*expr = ir::ir_utils::IRCopy(min_b);
}
}
};
Mutator()(&copied);
return copied;
}
/**
* @brief Given an expr, visit it. If there is an ir::Max and its operands are 1
* constant value and 1 inconstant value, return the constant max value.
*/
ir::Expr CasInterval::ReplaceMaxToConstant(ir::Expr expr) {
ir::Expr copied = ir::ir_utils::IRCopy(expr);
struct Mutator : public ir::IRMutator<ir::Expr*> {
void operator()(ir::Expr* expr) { Visit(expr); }
void Visit(ir::Expr* expr) { ir::IRMutator<>::Visit(expr, expr); }
private:
void Visit(const ir::Max* op, ir::Expr* expr) override {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
auto max_a = op->a();
auto max_b = op->b();
if (max_a.is_constant() && !max_b.is_constant()) {
PADDLE_ENFORCE_EQ(
max_a->type().is_integer(),
true,
::common::errors::InvalidArgument("Max a should be an integer."));
*expr = ir::ir_utils::IRCopy(max_a);
} else if (max_b.is_constant() && !max_a.is_constant()) {
PADDLE_ENFORCE_EQ(
max_b->type().is_integer(),
true,
::common::errors::InvalidArgument("Max b should be an integer."));
*expr = ir::ir_utils::IRCopy(max_b);
}
}
};
Mutator()(&copied);
return copied;
}
std::ostream& operator<<(std::ostream& os, const CasInterval& i) {
if (i.e_l.defined() && i.e_r.defined()) {
os << "Expr e_l Interval[" << i.e_l << ", " << i.e_r << "]";
} else {
os << "Int l Interval[" << i.l << ", " << i.r << "]";
}
return os;
}
ir::Expr SymbolicExprLimit::positive_inf =
ir::Expr(ir::Var("positive_infinity"));
ir::Expr SymbolicExprLimit::negative_inf =
ir::Expr(ir::Var("negative_infinity"));
cas_intervals_t CollectVarIntervalsOfExprs(const std::vector<ir::Expr>& exprs,
bool is_lower_bound_zero) {
cas_intervals_t var_intervals;
for (ir::Expr expr : exprs) {
ir::ir_utils::CollectIRNodes(expr, [&](const ir::Expr* x) {
if (const ir::_Var_* var = x->as_var()) {
ir::Expr lower_bound = is_lower_bound_zero
? ir::Expr(static_cast<int64_t>(1))
: SymbolicExprLimit::negative_inf;
ir::Expr upper_bound = SymbolicExprLimit::positive_inf;
if (var->lower_bound.defined()) {
lower_bound = var->lower_bound;
}
if (var->upper_bound.defined()) {
upper_bound = var->upper_bound;
}
if (var->is_symbolic_constant) {
lower_bound = ir::Expr(1);
}
var_intervals.insert(
{var->name,
CasInterval(lower_bound, NormalizeUpperBound(upper_bound))});
}
return false;
});
}
return var_intervals;
}
std::optional<bool> SymbolicExprAnalyzer::Prove(
const ir::Expr& condition) const {
try {
if (condition.As<ir::EQ>()) {
return ProveEQ(condition.As<ir::EQ>()->a(), condition.As<ir::EQ>()->b());
}
if (condition.As<ir::NE>()) {
return ProveNE(condition.As<ir::NE>()->a(), condition.As<ir::NE>()->b());
}
if (condition.As<ir::GE>()) {
return ProveGE(condition.As<ir::GE>()->a(), condition.As<ir::GE>()->b());
}
if (condition.As<ir::LE>()) {
return ProveLE(condition.As<ir::LE>()->a(), condition.As<ir::LE>()->b());
}
if (condition.As<ir::GT>()) {
return ProveGT(condition.As<ir::GT>()->a(), condition.As<ir::GT>()->b());
}
if (condition.As<ir::LT>()) {
return ProveLT(condition.As<ir::LT>()->a(), condition.As<ir::LT>()->b());
}
return std::nullopt;
} catch (const ::common::enforce::EnforceNotMet& e) {
LOG(WARNING) << "Error occurred during integer calculation: " << e.what()
<< ", so SymbolicExprAnalyzer cannot prove anything.";
return std::nullopt;
}
}
std::optional<bool> SymbolicExprAnalyzer::ProveEQ(const ir::Expr& lhs,
const ir::Expr& rhs) const {
try {
if (lhs == rhs) {
return true;
}
ir::Expr diff = optim::ArithSimplify(ir::Sub::Make(lhs, rhs));
if (diff.is_constant()) {
return diff.get_constant() == 0;
}
ir::Expr diff_lower_bound = LowerBound(diff);
VLOG(6) << "lower bound of " << diff << " = " << diff_lower_bound;
ir::Expr diff_upper_bound = UpperBound(diff);
VLOG(6) << "upper bound of " << diff << " = " << diff_upper_bound;
if (diff_lower_bound.is_constant() && diff_upper_bound.is_constant() &&
diff_lower_bound.get_constant() == diff_upper_bound.get_constant()) {
return diff_lower_bound.get_constant() == 0;
}
std::optional<bool> prove_gt = ProveGT(lhs, rhs);
if (prove_gt.has_value() && prove_gt.value()) {
return false;
}
std::optional<bool> prove_lt = ProveLT(lhs, rhs);
if (prove_lt.has_value() && prove_lt.value()) {
return false;
}
return std::nullopt;
} catch (const ::common::enforce::EnforceNotMet& e) {
LOG(WARNING) << "Error occurred during integer calculation: " << e.what()
<< ", so SymbolicExprAnalyzer cannot prove anything.";
return std::nullopt;
}
}
std::optional<bool> SymbolicExprAnalyzer::ProveNE(const ir::Expr& lhs,
const ir::Expr& rhs) const {
try {
std::optional<bool> prove_eq = ProveEQ(lhs, rhs);
if (!prove_eq.has_value()) {
return std::nullopt;
}
return !prove_eq.value();
} catch (const ::common::enforce::EnforceNotMet& e) {
LOG(WARNING) << "Error occurred during integer calculation: " << e.what()
<< ", so SymbolicExprAnalyzer cannot prove anything.";
return std::nullopt;
}
}
std::optional<bool> SymbolicExprAnalyzer::ProveGE(const ir::Expr& lhs,
const ir::Expr& rhs) const {
try {
if (lhs == rhs) {
return true;
}
if (rhs == SymbolicExprLimit::positive_inf ||
lhs == SymbolicExprLimit::negative_inf) {
return false;
}
if (lhs == SymbolicExprLimit::positive_inf ||
rhs == SymbolicExprLimit::negative_inf) {
return true;
}
ir::Expr diff = optim::ArithSimplify(ir::Sub::Make(lhs, rhs));
VLOG(6) << "diff of " << ir::Sub::Make(lhs, rhs) << " = " << diff;
if (diff.is_constant() && diff.get_constant() < 0) {
return false;
}
if (diff.is_constant() && diff.get_constant() >= 0) {
return true;
}
ir::Expr diff_upper_bound = UpperBound(diff);
VLOG(6) << "upper bound of " << diff << " = " << diff_upper_bound;
if (diff_upper_bound.is_constant() && diff_upper_bound.get_constant() < 0) {
return false;
}
ir::Expr diff_lower_bound = LowerBound(diff);
VLOG(6) << "lower bound of " << diff << " = " << diff_lower_bound;
if (diff_lower_bound.is_constant() &&
diff_lower_bound.get_constant() >= 0) {
return true;
}
return std::nullopt;
} catch (const ::common::enforce::EnforceNotMet& e) {
LOG(WARNING) << "Error occurred during integer calculation: " << e.what()
<< ", so SymbolicExprAnalyzer cannot prove anything.";
return std::nullopt;
}
}
std::optional<bool> SymbolicExprAnalyzer::ProveLE(const ir::Expr& lhs,
const ir::Expr& rhs) const {
return ProveGE(rhs, lhs);
}
std::optional<bool> SymbolicExprAnalyzer::ProveGT(const ir::Expr& lhs,
const ir::Expr& rhs) const {
try {
if (lhs == rhs) {
return false;
}
if (rhs == SymbolicExprLimit::positive_inf ||
lhs == SymbolicExprLimit::negative_inf) {
return false;
}
if (lhs == SymbolicExprLimit::positive_inf ||
rhs == SymbolicExprLimit::negative_inf) {
return true;
}
ir::Expr diff = optim::ArithSimplify(ir::Sub::Make(lhs, rhs));
VLOG(6) << "diff of " << ir::Sub::Make(lhs, rhs) << " = " << diff;
if (diff.is_constant() && diff.get_constant() <= 0) {
return false;
}
if (diff.is_constant() && diff.get_constant() > 0) {
return true;
}
ir::Expr diff_upper_bound = UpperBound(diff);
VLOG(6) << "upper bound of " << diff << " = " << diff_upper_bound;
if (diff_upper_bound.is_constant() &&
diff_upper_bound.get_constant() <= 0) {
return false;
}
ir::Expr diff_lower_bound = LowerBound(diff);
VLOG(6) << "lower bound of " << diff << " = " << diff_lower_bound;
if (diff_lower_bound.is_constant() && diff_lower_bound.get_constant() > 0) {
return true;
}
return std::nullopt;
} catch (const ::common::enforce::EnforceNotMet& e) {
LOG(WARNING) << "Error occurred during integer calculation: " << e.what()
<< ", so SymbolicExprAnalyzer cannot prove anything.";
return std::nullopt;
}
}
std::optional<bool> SymbolicExprAnalyzer::ProveLT(const ir::Expr& lhs,
const ir::Expr& rhs) const {
return ProveGT(rhs, lhs);
}
// Tell whether lhs can be divisible by rhs, lhs must be a pure math expression
// and rhs must be a var
std::optional<bool> SymbolicExprAnalyzer::ProveDivisible(
const ir::Expr& lhs, const ir::Expr& rhs) const {
PADDLE_ENFORCE_EQ(rhs.is_var(),
true,
::common::errors::InvalidArgument(
"Rhs in ProveDivisible must be a var temporarily!\n"));
PADDLE_ENFORCE_EQ(lhs.defined(),
true,
::common::errors::InvalidArgument(
"Lhs in ProveDivisible must be defined."));
PADDLE_ENFORCE_EQ(rhs.defined(),
true,
::common::errors::InvalidArgument(
"Rhs in ProveDivisible must be defined."));
PADDLE_ENFORCE_EQ(
cinn::optim::IsPureMath(lhs),
true,
::common::errors::InvalidArgument(
"Lhs in ProveDivisible must be a pure math expression."));
try {
ir::Expr lhs_copy = ir::ir_utils::IRCopy(lhs);
if (cinn::common::is_zero(lhs_copy)) return true;
auto OptionalAnd =
[](const std::optional<bool>& lhs,
const std::optional<bool>& rhs) -> std::optional<bool> {
if (lhs.has_value() && rhs.has_value()) {
return lhs.value() && rhs.value();
} else {
return std::nullopt;
}
};
auto OptionalOr =
[](const std::optional<bool>& lhs,
const std::optional<bool>& rhs) -> std::optional<bool> {
if (lhs.has_value() && rhs.has_value()) {
return lhs.value() || rhs.value();
} else if ((!lhs.has_value()) && (!rhs.has_value())) {
return std::nullopt;
} else if (lhs.has_value() && (!rhs.has_value())) {
return lhs.value() ? std::optional<bool>(lhs.value())
: std::optional<bool>(std::nullopt);
} else {
return rhs.value() ? std::optional<bool>(rhs.value())
: std::optional<bool>(std::nullopt);
}
};
std::vector<ir::Expr> ops{};
std::optional<bool> res = std::nullopt;
ir::Expr zero(0);
ir::Expr tmp_expr;
auto is_ge = ProveGE(lhs, rhs);
switch (lhs.node_type()) {
case cinn::ir::IrNodeTy::_Var_:
return ProveEQ(lhs, rhs);
case cinn::ir::IrNodeTy::IntImm:
return false;
case cinn::ir::IrNodeTy::Sum:
res = true;
ops = lhs.As<ir::Sum>()->operands();
PADDLE_ENFORCE_NE(ops.empty(),
true,
::common::errors::InvalidArgument(
"Operands in Sum node should not be empty."));
std::for_each(ops.begin(), ops.end(), [&](const ir::Expr& expr) {
res = OptionalAnd(res, this->ProveDivisible(expr, rhs));
});
res = OptionalAnd(res, is_ge);
return res;
case cinn::ir::IrNodeTy::Product:
res = false;
ops = lhs.As<ir::Product>()->operands();
PADDLE_ENFORCE_NE(ops.empty(),
true,
::common::errors::InvalidArgument(
"Operands in Sum node should not be empty."));
std::for_each(ops.begin(), ops.end(), [&](const ir::Expr& expr) {
res = OptionalOr(res, this->ProveDivisible(expr, rhs));
if (res.has_value() && res.value()) return;
});
res = OptionalAnd(res, is_ge);
return res;
case cinn::ir::IrNodeTy::FloatImm:
return false;
case cinn::ir::IrNodeTy::Add:
return OptionalAnd(
OptionalAnd(ProveDivisible(lhs.As<ir::Add>()->a(), rhs),
ProveDivisible(lhs.As<ir::Add>()->b(), rhs)),
is_ge);
case cinn::ir::IrNodeTy::Sub:
return OptionalAnd(
OptionalAnd(ProveDivisible(lhs.As<ir::Sub>()->a(), rhs),
ProveDivisible(lhs.As<ir::Sub>()->b(), rhs)),
is_ge);
case cinn::ir::IrNodeTy::Div:
tmp_expr = optim::ArithSimplify(lhs);
if (tmp_expr.node_type() == cinn::ir::IrNodeTy::Div)
return std::nullopt;
return OptionalAnd(ProveDivisible(tmp_expr, rhs), is_ge);
case cinn::ir::IrNodeTy::Mul:
return OptionalAnd(
OptionalOr(ProveDivisible(lhs.As<ir::Mul>()->a(), rhs),
ProveDivisible(lhs.As<ir::Mul>()->b(), rhs)),
is_ge);
case cinn::ir::IrNodeTy::Mod:
return false;
case cinn::ir::IrNodeTy::Minus:
return ProveDivisible(lhs.As<ir::Minus>()->v(), rhs);
default:
PADDLE_THROW(::common::errors::InvalidArgument("Not supported yet!"));
break;
}
} catch (const ::common::enforce::EnforceNotMet& e) {
LOG(WARNING) << "Error occurred during integer calculation: " << e.what()
<< ", so SymbolicExprAnalyzer cannot prove anything.";
return std::nullopt;
}
}
class BoundReplacer : public ir::IRMutator<> {
public:
explicit BoundReplacer(const cas_intervals_t& var_intervals,
bool is_lower_bound)
: var_intervals_(var_intervals),
sign_(is_lower_bound),
var_visited_({}) {}
void operator()(ir::Expr* expr) { IRMutator::Visit(expr, expr); }
private:
void Visit(const ir::_Var_* var, ir::Expr* op) override {
// if the variable is S0/S1..., do not replace it.
if (var->is_symbolic_constant) return;
ir::Expr lower_bound = SymbolicExprLimit::negative_inf;
ir::Expr upper_bound = SymbolicExprLimit::positive_inf;
if (var_intervals_.count(var->name) != 0) {
const CasInterval& interval = var_intervals_.at(var->name);
lower_bound =
interval.e_l.defined() ? interval.e_l : ir::Expr(interval.l);
upper_bound =
interval.e_r.defined() ? interval.e_r : ir::Expr(interval.r);
}
if (!var_visited_.count(var->name)) {
if (sign_) {
*op = ir::ir_utils::IRCopy(lower_bound);
var_visited_.insert({var->name, lower_bound});
} else {
*op = ir::ir_utils::IRCopy(upper_bound);
var_visited_.insert({var->name, upper_bound});
}
} else {
*op = ir::ir_utils::IRCopy(var_visited_.at(var->name));
}
}
void Visit(const ir::Add* expr, ir::Expr* op) override {
ir::Add* node = op->As<ir::Add>();
IRMutator::Visit(&node->a(), &node->a());
IRMutator::Visit(&node->b(), &node->b());
}
void Visit(const ir::Mul* expr, ir::Expr* op) override {
ir::Mul* node = op->As<ir::Mul>();
if (node->b().is_constant() && node->b().get_constant() < 0) {
sign_ = !sign_;
}
IRMutator::Visit(&node->a(), &node->a());
if (node->b().is_constant() && node->b().get_constant() < 0) {
sign_ = !sign_;
}
if (node->a().is_constant() && node->a().get_constant() < 0) {
sign_ = !sign_;
}
IRMutator::Visit(&node->b(), &node->b());
if (node->a().is_constant() && node->a().get_constant() < 0) {
sign_ = !sign_;
}
}
void Visit(const ir::Sub* expr, ir::Expr* op) override {
ir::Sub* node = op->As<ir::Sub>();
IRMutator::Visit(&node->a(), &node->a());
sign_ = !sign_;
IRMutator::Visit(&node->b(), &node->b());
sign_ = !sign_;
}
void Visit(const ir::Div* expr, ir::Expr* op) override {
ir::Div* node = op->As<ir::Div>();
if (node->b().is_constant() && node->b().get_constant() < 0) {
sign_ = !sign_;
}
IRMutator::Visit(&node->a(), &node->a());
if (node->b().is_constant() && node->b().get_constant() < 0) {
sign_ = !sign_;
}
sign_ = !sign_;
IRMutator::Visit(&node->b(), &node->b());
sign_ = !sign_;
}
void Visit(const ir::Mod* expr, ir::Expr* op) override {
ir::Mod* node = op->As<ir::Mod>();
if (sign_) {
*op = ir::Expr(0);
} else {
IRMutator::Visit(&node->b(), &node->b());
*op = node->b() - ir::Expr(1);
}
}
private:
const cas_intervals_t& var_intervals_;
std::unordered_map<std::string, ir::Expr> var_visited_;
// Determine replacing with upper or lower bound,
// True means lower bound and False means upper bound.
bool sign_;
};
ir::Expr SymbolicExprAnalyzer::LowerBound(const ir::Expr& expr) const {
BoundReplacer bound_replacer(var_intervals_, true);
ir::Expr bound = ir::ir_utils::IRCopy(expr);
if (bound.is_index()) {
bound = bound.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3);
}
bound_replacer(&bound);
return optim::ArithSimplify(bound);
}
ir::Expr SymbolicExprAnalyzer::UpperBound(const ir::Expr& expr) const {
BoundReplacer bound_replacer(var_intervals_, false);
ir::Expr bound = ir::ir_utils::IRCopy(expr);
if (bound.is_index()) {
bound = bound.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3);
}
bound_replacer(&bound);
return optim::ArithSimplify(bound);
}
std::optional<bool> ProveEQ(const SingleIntervalIntSet& lhs,
const SingleIntervalIntSet& rhs) {
cas_intervals_t merged_var_intervals = MergeVarIntervals(lhs, rhs);
SymbolicExprAnalyzer analyzer(merged_var_intervals);
std::optional<bool> prove_min_eq = analyzer.ProveEQ(lhs.min_, rhs.min_);
std::optional<bool> prove_max_eq = analyzer.ProveEQ(lhs.max_, rhs.max_);
if (!prove_min_eq.has_value() || !prove_max_eq.has_value()) {
return std::nullopt;
} else if (prove_min_eq.value() == true && prove_max_eq.value() == true) {
return true;
} else if (prove_min_eq.value() == false || prove_max_eq.value() == false) {
return false;
}
return std::nullopt;
}
std::optional<SingleIntervalIntSet> ProvedUnion(const SingleIntervalIntSet& a,
const SingleIntervalIntSet& b) {
bool is_a_empty = a.ProveEmpty().value_or(false);
bool is_a_all = a.ProveAll().value_or(false);
bool is_b_empty = b.ProveEmpty().value_or(false);
bool is_b_all = b.ProveAll().value_or(false);
if (is_a_empty || is_b_all) {
return b;
}
if (is_b_empty || is_a_all) {
return a;
}
// May be relaxed when (a.max < b.min - 1) or (b.max < a.min - 1)
cas_intervals_t merged_var_intervals = MergeVarIntervals(a, b);
SymbolicExprAnalyzer analyzer(merged_var_intervals);
ir::Expr min = SymbolicExprLimit::positive_inf;
ir::Expr max = SymbolicExprLimit::negative_inf;
std::optional<bool> prove_a_min_le_b_min = analyzer.ProveLE(a.Min(), b.Min());
if (!prove_a_min_le_b_min.has_value()) {
return std::nullopt;
} else if (prove_a_min_le_b_min.value() == true) {
min = a.Min();
} else if (prove_a_min_le_b_min.value() == false) {
min = b.Min();
}
std::optional<bool> prove_a_max_ge_b_max = analyzer.ProveGE(a.Max(), b.Max());
if (!prove_a_max_ge_b_max.has_value()) {
return std::nullopt;
} else if (prove_a_max_ge_b_max.value() == true) {
max = a.Max();
} else if (prove_a_max_ge_b_max.value() == false) {
max = b.Max();
}
return SingleIntervalIntSet(min, max, std::move(merged_var_intervals));
}
std::optional<SingleIntervalIntSet> ProvedIntersect(
const SingleIntervalIntSet& a, const SingleIntervalIntSet& b) {
bool is_a_empty = a.ProveEmpty().value_or(false);
bool is_a_all = a.ProveAll().value_or(false);
bool is_b_empty = b.ProveEmpty().value_or(false);
bool is_b_all = b.ProveAll().value_or(false);
if (is_a_all || is_b_empty) {
return b;
}
if (is_b_all || is_a_empty) {
return a;
}
cas_intervals_t merged_var_intervals = MergeVarIntervals(a, b);
SymbolicExprAnalyzer analyzer(merged_var_intervals);
ir::Expr min = SymbolicExprLimit::positive_inf;
ir::Expr max = SymbolicExprLimit::negative_inf;
std::optional<bool> prove_a_max_lt_b_min_sub1 =
analyzer.ProveLT(a.Max(), b.Min() - ir::Expr(1));
std::optional<bool> prove_b_max_lt_a_min_sub1 =
analyzer.ProveLT(b.Max(), a.Min() - ir::Expr(1));
if (prove_a_max_lt_b_min_sub1.has_value() &&
prove_a_max_lt_b_min_sub1.value() ||
prove_b_max_lt_a_min_sub1.has_value() &&
prove_b_max_lt_a_min_sub1.value()) {
return SingleIntervalIntSet(min, max, std::move(merged_var_intervals));
}
std::optional<bool> prove_a_min_ge_b_min = analyzer.ProveGE(a.Min(), b.Min());
if (!prove_a_min_ge_b_min.has_value()) {
return std::nullopt;
} else if (prove_a_min_ge_b_min.value() == true) {
min = a.Min();
} else if (prove_a_min_ge_b_min.value() == false) {
min = b.Min();
}
std::optional<bool> prove_a_max_le_b_max = analyzer.ProveLE(a.Max(), b.Max());
if (!prove_a_max_le_b_max.has_value()) {
return std::nullopt;
} else if (prove_a_max_le_b_max.value() == true) {
max = a.Max();
} else if (prove_a_max_le_b_max.value() == false) {
max = b.Max();
}
return SingleIntervalIntSet(min, max, std::move(merged_var_intervals));
}
cas_intervals_t MergeVarIntervals(const SingleIntervalIntSet& a,
const SingleIntervalIntSet& b) {
cas_intervals_t merged = a.var_intervals_;
merged.insert(b.var_intervals_.begin(), b.var_intervals_.end());
return merged;
}
SingleIntervalIntSet::SingleIntervalIntSet(const ir::Expr& min,
const ir::Expr& max,
cas_intervals_t var_intervals)
: min_(min), max_(max), var_intervals_(var_intervals) {
if (var_intervals_.empty()) {
auto insert_interval_func = [&](const ir::Expr* x) {
if (x->as_var()) {
ir::Expr lower_bound = x->as_var()->lower_bound.defined()
? x->as_var()->lower_bound
: SymbolicExprLimit::negative_inf;
ir::Expr upper_bound = x->as_var()->upper_bound.defined()
? x->as_var()->upper_bound
: SymbolicExprLimit::positive_inf;
var_intervals_.insert(
{x->as_var()->name,
CasInterval(lower_bound, NormalizeUpperBound(upper_bound))});
}
return false;
};
ir::ir_utils::CollectIRNodes(min_, insert_interval_func);
ir::ir_utils::CollectIRNodes(max_, insert_interval_func);
}
}
std::optional<bool> SingleIntervalIntSet::ProveEmpty() const {
if (min_ == SymbolicExprLimit::positive_inf ||
max_ == SymbolicExprLimit::negative_inf) {
return true;
}
SymbolicExprAnalyzer analyzer(var_intervals_);
return analyzer.ProveGT(min_, max_);
}
std::optional<bool> SingleIntervalIntSet::ProveAll() const {
return min_ == SymbolicExprLimit::negative_inf &&
max_ == SymbolicExprLimit::positive_inf;
}
std::optional<bool> SingleIntervalIntSet::ProvePoint() const {
SymbolicExprAnalyzer analyzer(var_intervals_);
return analyzer.ProveEQ(min_, max_);
}
std::optional<bool> SingleIntervalIntSet::ProveSubSet(
const SingleIntervalIntSet& other) const {
cas_intervals_t merged_var_intervals = MergeVarIntervals(*this, other);
SymbolicExprAnalyzer analyzer(merged_var_intervals);
std::optional<bool> prove_min_ge = analyzer.ProveGE(min_, other.Min());
std::optional<bool> prove_max_le = analyzer.ProveLE(max_, other.Max());
if (!prove_min_ge.has_value() || !prove_max_le.has_value()) {
return std::nullopt;
} else if (prove_min_ge.value() && prove_max_le.value()) {
return true;
} else {
return false;
}
return std::nullopt;
}
std::optional<bool> SingleIntervalIntSet::ProveSuperSet(
const SingleIntervalIntSet& other) const {
cas_intervals_t merged_var_intervals = MergeVarIntervals(*this, other);
SymbolicExprAnalyzer analyzer(merged_var_intervals);
std::optional<bool> prove_min_le = analyzer.ProveLE(min_, other.Min());
std::optional<bool> prove_max_ge = analyzer.ProveGE(max_, other.Max());
if (!prove_min_le.has_value() || !prove_max_ge.has_value()) {
return std::nullopt;
} else if (prove_min_le.value() && prove_max_ge.value()) {
return true;
} else {
return false;
}
return std::nullopt;
}
ir::Expr EnhancedSimplifyModExpr(
ir::Expr e,
const paddle::flat_hash_map<std::string, CasInterval>& var_intervals) {
struct Mutator : public ir::IRMutator<ir::Expr*> {
explicit Mutator(
const paddle::flat_hash_map<std::string, CasInterval>& var_intervals)
: var_intervals_(var_intervals), analyzer_(var_intervals_) {}
void operator()(ir::Expr* expr) { Visit(expr); }
void Visit(ir::Expr* expr) { ir::IRMutator<>::Visit(expr, expr); }
private:
void Visit(const ir::Mod* op, ir::Expr* expr) override {
std::optional<bool> prove_lt = analyzer_.ProveLT(op->a(), op->b());
if (prove_lt.has_value() && prove_lt.value()) {
*expr = op->a();
}
}
private:
const paddle::flat_hash_map<std::string, CasInterval>& var_intervals_;
SymbolicExprAnalyzer analyzer_;
};
Mutator mutator(var_intervals);
ir::Expr copied = ir::ir_utils::IRCopy(e);
mutator(&copied);
return copied;
}
} // namespace common
} // namespace cinn