|
53 | 53 | from ._utils import ( |
54 | 54 | get_operands as _get_operands, |
55 | 55 | ) |
| 56 | +from ._utils import ( |
| 57 | + replace_pad_with_mode as _replace_pad_with_mode, |
| 58 | +) |
56 | 59 |
|
57 | 60 | INT32_MAX: int = 2147483647 |
58 | 61 |
|
@@ -644,20 +647,28 @@ def replace_arange_start_step( |
644 | 647 | else coreai.constant(1, dtype=start.type.element_type) |
645 | 648 | ) |
646 | 649 |
|
647 | | - # coreai.range_ requires scalar (rank-0) operands that share an element |
648 | | - # type. aten.arange promotes mixed-type scalars internally; we replicate |
649 | | - # that here by squeezing each operand to rank-0 and casting to the FX |
650 | | - # node's output dtype before the op. |
| 650 | + # When ALL operands are integer-typed, keep them as si32 so coreai.range_ |
| 651 | + # can infer a static output shape, then cast the result to the requested |
| 652 | + # dtype. If any operand is float (e.g. arange(0, 5, 0.5)), fall back to |
| 653 | + # target_type — truncating a float step to si32 would corrupt the values. |
651 | 654 | target_type = get_output_element_type_from_node(node) |
| 655 | + si32 = IntegerType.get_signed(32) |
| 656 | + all_integer = all( |
| 657 | + isinstance(v.type.element_type, IntegerType) for v in (start, end, step) |
| 658 | + ) |
| 659 | + range_type = si32 if all_integer else target_type |
652 | 660 |
|
653 | 661 | def to_scalar(v: Value) -> Value: |
654 | 662 | if v.type.rank > 0: |
655 | 663 | v = coreai.shrink_dims(v, list(range(v.type.rank))) |
656 | | - if v.type.element_type != target_type: |
657 | | - v = coreai.cast(v, target_type) |
| 664 | + if v.type.element_type != range_type: |
| 665 | + v = coreai.cast(v, range_type) |
658 | 666 | return v |
659 | 667 |
|
660 | | - return coreai.range_(to_scalar(start), to_scalar(end), to_scalar(step)) |
| 668 | + result = coreai.range_(to_scalar(start), to_scalar(end), to_scalar(step)) |
| 669 | + if result.type.element_type != target_type: |
| 670 | + result = coreai.cast(result, target_type) |
| 671 | + return result |
661 | 672 |
|
662 | 673 |
|
663 | 674 | def replace_batch_norm( |
@@ -1109,6 +1120,20 @@ def replace_constant_pad_nd( |
1109 | 1120 | return result |
1110 | 1121 |
|
1111 | 1122 |
|
| 1123 | +def replace_reflection_pad( |
| 1124 | + values_map: dict[str, Value], node: fx.Node, loc: Location |
| 1125 | +) -> Value: |
| 1126 | + """aten.reflection_pad{1,2,3}d.default -> coreai.pad<reflect>.""" |
| 1127 | + return _replace_pad_with_mode(values_map, node, loc, "reflect") |
| 1128 | + |
| 1129 | + |
| 1130 | +def replace_replication_pad( |
| 1131 | + values_map: dict[str, Value], node: fx.Node, loc: Location |
| 1132 | +) -> Value: |
| 1133 | + """aten.replication_pad{1,2,3}d.default -> coreai.pad<replicate>.""" |
| 1134 | + return _replace_pad_with_mode(values_map, node, loc, "replicate") |
| 1135 | + |
| 1136 | + |
1112 | 1137 | def _conv_transpose( |
1113 | 1138 | x: Value, |
1114 | 1139 | weight: Value, |
@@ -1522,6 +1547,111 @@ def replace_argmax(values_map: dict[str, Value], node: fx.Node, loc: Location) - |
1522 | 1547 | return result if keepdim else coreai.shrink_dims(result, [dim]) |
1523 | 1548 |
|
1524 | 1549 |
|
| 1550 | +def replace_atan2(values_map: dict[str, Value], node: fx.Node, loc: Location) -> Value: |
| 1551 | + """Lower atan2(y, x) using atan(y/x) with quadrant correction. |
| 1552 | +
|
| 1553 | + CoreAI has no native atan2, so it is decomposed as: |
| 1554 | + - x != 0, finite: atan(y/x) adjusted by ±π for the correct quadrant. |
| 1555 | + - x == +0: ±π/2 for non-zero y, 0 for y = 0. |
| 1556 | + - x == -0: ±π for all y (including ±0 → ±π per IEEE-754). |
| 1557 | + - both infinite: ±π/4 or ±3π/4 per IEEE-754. |
| 1558 | +
|
| 1559 | + Signed-zero handling: IEEE-754 treats -0.0 as distinct from +0.0 for atan2 |
| 1560 | + (e.g. atan2(-0, -1) = -π, not +π). The 1/v trick — 1/-0.0 = -inf — is used |
| 1561 | + to detect the sign bit of zero inputs so that y_neg and x_neg are correct |
| 1562 | + for -0.0 inputs without misclassifying ±inf (which use the strict > path). |
| 1563 | +
|
| 1564 | + When x=0, x is replaced with 1 before the divide solely to avoid NaN/inf; that |
| 1565 | + intermediate result is discarded by the final where-select. |
| 1566 | + atan2(0, 0) = 0 by convention. |
| 1567 | + """ |
| 1568 | + y, x = _get_operands(values_map, node, [0, 1]) |
| 1569 | + ele_type = x.type.element_type |
| 1570 | + |
| 1571 | + zero = coreai.constant(0.0, dtype=ele_type) |
| 1572 | + one = coreai.constant(1.0, dtype=ele_type) |
| 1573 | + pi = coreai.constant(np.pi, dtype=ele_type) |
| 1574 | + neg_pi = coreai.constant(-np.pi, dtype=ele_type) |
| 1575 | + half_pi = coreai.constant(np.pi / 2.0, dtype=ele_type) |
| 1576 | + neg_half_pi = coreai.constant(-np.pi / 2.0, dtype=ele_type) |
| 1577 | + quarter_pi = coreai.constant(np.pi / 4.0, dtype=ele_type) |
| 1578 | + neg_quarter_pi = coreai.constant(-np.pi / 4.0, dtype=ele_type) |
| 1579 | + three_quarter_pi = coreai.constant(3.0 * np.pi / 4.0, dtype=ele_type) |
| 1580 | + neg_three_quarter_pi = coreai.constant(-3.0 * np.pi / 4.0, dtype=ele_type) |
| 1581 | + |
| 1582 | + # ── signed-zero-aware sign predicates ───────────────────────────────────── |
| 1583 | + # 1 / -0.0 = -inf (IEEE-754), so (0 > 1/v) is True iff v = -0.0. Combine with |
| 1584 | + # the strict > predicate (handles ±inf and non-zero finites) via OR. |
| 1585 | + y_is_zero = coreai.broadcasting_equal(y, zero) |
| 1586 | + x_is_zero = coreai.broadcasting_equal(x, zero) |
| 1587 | + y_neg = coreai.broadcasting_or( |
| 1588 | + coreai.broadcasting_greater(zero, y), |
| 1589 | + coreai.broadcasting_and( |
| 1590 | + y_is_zero, |
| 1591 | + coreai.broadcasting_greater(zero, coreai.broadcasting_divide(one, y)), |
| 1592 | + ), |
| 1593 | + ) |
| 1594 | + x_neg = coreai.broadcasting_or( |
| 1595 | + coreai.broadcasting_greater(zero, x), |
| 1596 | + coreai.broadcasting_and( |
| 1597 | + x_is_zero, |
| 1598 | + coreai.broadcasting_greater(zero, coreai.broadcasting_divide(one, x)), |
| 1599 | + ), |
| 1600 | + ) |
| 1601 | + x_is_neg_zero = coreai.broadcasting_and( |
| 1602 | + x_is_zero, |
| 1603 | + coreai.broadcasting_greater(zero, coreai.broadcasting_divide(one, x)), |
| 1604 | + ) |
| 1605 | + |
| 1606 | + # ── both-infinite branch ────────────────────────────────────────────────── |
| 1607 | + # atan(inf/inf) = atan(NaN) = NaN; handle before the divide. |
| 1608 | + pos_inf = coreai.constant(float("inf"), dtype=ele_type) |
| 1609 | + neg_inf = coreai.constant(float("-inf"), dtype=ele_type) |
| 1610 | + x_is_inf = coreai.broadcasting_or( |
| 1611 | + coreai.broadcasting_equal(x, pos_inf), coreai.broadcasting_equal(x, neg_inf) |
| 1612 | + ) |
| 1613 | + y_is_inf = coreai.broadcasting_or( |
| 1614 | + coreai.broadcasting_equal(y, pos_inf), coreai.broadcasting_equal(y, neg_inf) |
| 1615 | + ) |
| 1616 | + both_inf = coreai.broadcasting_and(x_is_inf, y_is_inf) |
| 1617 | + inf_result = coreai.broadcasting_where( |
| 1618 | + y_neg, |
| 1619 | + coreai.broadcasting_where(x_neg, neg_three_quarter_pi, neg_quarter_pi), |
| 1620 | + coreai.broadcasting_where(x_neg, three_quarter_pi, quarter_pi), |
| 1621 | + ) |
| 1622 | + |
| 1623 | + # ── x = 0 branch ────────────────────────────────────────────────────────── |
| 1624 | + # x = +0: ±π/2 for strictly ±y, 0 when y = 0. |
| 1625 | + # x = -0: ±π for all y (y_neg covers y = -0.0 via the 1/y trick above). |
| 1626 | + y_pos_strict = coreai.broadcasting_greater(y, zero) |
| 1627 | + y_neg_strict = coreai.broadcasting_greater(zero, y) |
| 1628 | + pos_x_zero_result = coreai.broadcasting_where( |
| 1629 | + y_pos_strict, |
| 1630 | + half_pi, |
| 1631 | + coreai.broadcasting_where(y_neg_strict, neg_half_pi, zero), |
| 1632 | + ) |
| 1633 | + neg_x_zero_result = coreai.broadcasting_where(y_neg, neg_pi, pi) |
| 1634 | + zero_result = coreai.broadcasting_where( |
| 1635 | + x_is_neg_zero, neg_x_zero_result, pos_x_zero_result |
| 1636 | + ) |
| 1637 | + |
| 1638 | + # ── finite nonzero x branch ──────────────────────────────────────────────── |
| 1639 | + # Avoid division by zero: substitute x = 1 when x = 0; result discarded by |
| 1640 | + # the outer where-select. |
| 1641 | + x_safe = coreai.broadcasting_where(x_is_zero, one, x) |
| 1642 | + base = coreai.atan(coreai.broadcasting_divide(y, x_safe)) |
| 1643 | + correction = coreai.broadcasting_where( |
| 1644 | + y_neg, |
| 1645 | + coreai.broadcasting_sub(base, pi), |
| 1646 | + coreai.broadcasting_add(base, pi), |
| 1647 | + ) |
| 1648 | + nonzero_result = coreai.broadcasting_where(x_neg, correction, base) |
| 1649 | + |
| 1650 | + # ── combine ──────────────────────────────────────────────────────────────── |
| 1651 | + result = coreai.broadcasting_where(x_is_zero, zero_result, nonzero_result) |
| 1652 | + return coreai.broadcasting_where(both_inf, inf_result, result) |
| 1653 | + |
| 1654 | + |
1525 | 1655 | def replace_gather(values_map: dict[str, Value], node: fx.Node, loc: Location) -> Value: |
1526 | 1656 | """Converts aten.gather to coreai.gather_along_axis.""" |
1527 | 1657 | x, index = _get_operands(values_map, node, [0, 2]) |
@@ -3440,6 +3570,7 @@ def sdpa_maskless(q: Value, k: Value, v: Value) -> Value: |
3440 | 3570 | "asin.default": replace_unary_ops, |
3441 | 3571 | "asinh.default": replace_unary_ops, |
3442 | 3572 | "atan.default": replace_unary_ops, |
| 3573 | + "atan2.default": replace_atan2, |
3443 | 3574 | "atanh.default": replace_unary_ops, |
3444 | 3575 | "_adaptive_avg_pool2d.default": replace_adaptive_avg_pool2d, |
3445 | 3576 | "_unsafe_view.default": replace_view, |
@@ -3551,8 +3682,14 @@ def sdpa_maskless(q: Value, k: Value, v: Value) -> Value: |
3551 | 3682 | "prod.default": replace_prod_default, |
3552 | 3683 | "prod.dim_int": replace_prod_dim_int, |
3553 | 3684 | "reciprocal.default": replace_reciprocal, |
| 3685 | + "reflection_pad1d.default": replace_reflection_pad, |
| 3686 | + "reflection_pad2d.default": replace_reflection_pad, |
| 3687 | + "reflection_pad3d.default": replace_reflection_pad, |
3554 | 3688 | "relu.default": replace_unary_ops, |
3555 | 3689 | "remainder.Tensor": replace_remainder, |
| 3690 | + "replication_pad1d.default": replace_replication_pad, |
| 3691 | + "replication_pad2d.default": replace_replication_pad, |
| 3692 | + "replication_pad3d.default": replace_replication_pad, |
3556 | 3693 | "round.default": replace_unary_ops, |
3557 | 3694 | "round.decimals": replace_round_decimals, |
3558 | 3695 | "round": replace_unary_ops, |
|
0 commit comments