Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
### Added
### Fixed
### Changed
- `pow(x, y, modulo)` now raises `TypeError` instead of silently ignoring the modulo argument
### Removed

## 6.3.0 - 2026.10.11
Expand Down
4 changes: 2 additions & 2 deletions src/pyscipopt/expr.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ cdef class Expr(ExprLike):
return NotImplemented
return super().__rtruediv__(other)

def __pow__(self, other, modulo):
def __pow__(self, other):
if float(other).is_integer() and other >= 0:
exp = int(other)
else: # need to transform to GenExpr
Expand Down Expand Up @@ -679,7 +679,7 @@ cdef class GenExpr(ExprLike):
# self.children.append(right)
# return self

def __pow__(self, other, modulo):
def __pow__(self, other):
expo = buildGenExprObj(other)
if <type>Py_TYPE(expo) is not Constant:
raise NotImplementedError("exponents must be numbers")
Expand Down
10 changes: 4 additions & 6 deletions src/pyscipopt/scip.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,7 @@ class Constant(GenExpr):
number: Incomplete
def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ...
def __neg__(self, /) -> Constant: ...
def __pow__( # type: ignore[override]
self, other: float | Constant, mod: Incomplete = ..., /
) -> Constant: ...
def __pow__(self, other: float | Constant, /) -> Constant: ... # type: ignore[override]

@disjoint_base
class Constraint:
Expand Down Expand Up @@ -419,9 +417,9 @@ class Expr(ExprLike):
# PowExpr can be wrong. We aim to make the stubs useful in common cases
# even though the type system cannot fully express the real types.
@overload
def __pow__(self, other: int, mod: Incomplete = ..., /) -> Expr: ...
def __pow__(self, other: int, /) -> Expr: ...
@overload
def __pow__(self, other: float, mod: Incomplete = ..., /) -> PowExpr: ...
def __pow__(self, other: float, /) -> PowExpr: ...
def __rpow__(self, other: float, /) -> ExpExpr: ...
def __getitem__(self, index: Incomplete, /) -> Incomplete: ...
def __iter__(self) -> Incomplete: ...
Expand Down Expand Up @@ -467,7 +465,7 @@ class GenExpr(ExprLike):
@overload
def __truediv__(self, other: np.ndarray | MatrixExpr, /) -> MatrixExpr: ...
def __rtruediv__(self, other: float, /) -> ProdExpr: ...
def __pow__(self, other: float | Constant, mod: Incomplete = ..., /) -> PowExpr: ...
def __pow__(self, other: float | Constant, /) -> PowExpr: ...
def __rpow__(self, other: float, mod: Incomplete = ..., /) -> ExpExpr: ...

@disjoint_base
Expand Down
17 changes: 17 additions & 0 deletions tests/test_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,23 @@ def test_rpow_constant_base(model):
c = (-2)**x


def test_pow_three_arg_raises(model):
m, x, y, z = model

# Expr path (integer exponent; falls through to GenExpr only for non-int)
with pytest.raises(TypeError):
pow(x, 2, 5)

# GenExpr path
with pytest.raises(TypeError):
pow(x**1.5, 2, 5)

# Two-argument pow still works on both paths
assert repr(x**2) == repr(pow(x, 2))
assert isinstance(x**1.5, GenExpr)
assert isinstance(pow(x, 1.5), GenExpr)


def test_getVal_with_GenExpr():
m = Model()
x = m.addVar(lb=1, ub=1, name="x")
Expand Down
Loading