From 0988f868b4e75c47374fc7a1555a73d94c78fc03 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 8 Jun 2026 21:50:11 +0800 Subject: [PATCH 01/15] refactor: replace Python math with C-level math functions and refactor unary expressions Remove Python math module and use libc.math for C-level functions (fabs, exp, log, sqrt, sin, cos). Refactor unary expression evaluation by introducing specific subclasses (AbsExpr, ExpExpr, LogExpr, SqrtExpr, SinExpr, CosExpr) with dedicated evaluate methods using C functions, replacing the generic UnaryExpr implementation. --- src/pyscipopt/expr.pxi | 73 +++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 62f0c880d..2ad9d1492 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -42,7 +42,6 @@ # which should, in princple, modify the expr. However, since we do not implement __isub__, __sub__ # gets called (I guess) and so a copy is returned. # Modifying the expression directly would be a bug, given that the expression might be re-used by the user. -import math from typing import TYPE_CHECKING, Literal, Union import numpy as np @@ -54,6 +53,12 @@ from cpython.number cimport PyNumber_Check from cpython.object cimport Py_LE, Py_EQ, Py_GE, Py_TYPE from cpython.ref cimport PyObject from cpython.tuple cimport PyTuple_GET_ITEM +from libc.math cimport cos as c_cos +from libc.math cimport exp as c_exp +from libc.math cimport fabs as c_fabs +from libc.math cimport log as c_log +from libc.math cimport sqrt as c_sqrt +from libc.math cimport sin as c_sin cimport numpy as cnp from pyscipopt.scip cimport Variable, Solution @@ -279,22 +284,22 @@ cdef class ExprLike: return self * -1.0 def __abs__(self) -> GenExpr: - return UnaryExpr(Operator.fabs, buildGenExprObj(self)) + return AbsExpr(Operator.fabs, buildGenExprObj(self)) def exp(self) -> GenExpr: - return UnaryExpr(Operator.exp, buildGenExprObj(self)) + return ExpExpr(Operator.exp, buildGenExprObj(self)) def log(self) -> GenExpr: - return UnaryExpr(Operator.log, buildGenExprObj(self)) + return LogExpr(Operator.log, buildGenExprObj(self)) def sqrt(self) -> GenExpr: - return UnaryExpr(Operator.sqrt, buildGenExprObj(self)) + return SqrtExpr(Operator.sqrt, buildGenExprObj(self)) def sin(self) -> GenExpr: - return UnaryExpr(Operator.sin, buildGenExprObj(self)) + return SinExpr(Operator.sin, buildGenExprObj(self)) def cos(self) -> GenExpr: - return UnaryExpr(Operator.cos, buildGenExprObj(self)) + return CosExpr(Operator.cos, buildGenExprObj(self)) ##@details Polynomial expressions of variables with operator overloading. \n @@ -799,24 +804,62 @@ cdef class PowExpr(GenExpr): return (self.children[0])._evaluate(sol) ** self.expo -# Exp, Log, Sqrt, Sin, Cos Expressions cdef class UnaryExpr(GenExpr): + def __init__(self, op, expr): self.children = [] self.children.append(expr) self._op = op - def __abs__(self) -> UnaryExpr: - if self._op == "abs": - return self.copy() - return UnaryExpr(Operator.fabs, self) + def __abs__(self) -> AbsExpr: + return AbsExpr(Operator.fabs, self) - def __repr__(self): + def __repr__(self) -> str: return self._op + "(" + self.children[0].__repr__() + ")" + +cdef class AbsExpr(UnaryExpr): + + def __abs__(self) -> AbsExpr: + return self.copy() + + cpdef double _evaluate(self, Solution sol) except *: + return c_fabs((self.children[0])._evaluate(sol)) + + + +cdef class ExpExpr(UnaryExpr): + + cpdef double _evaluate(self, Solution sol) except *: + return c_exp((self.children[0])._evaluate(sol)) + + + +cdef class LogExpr(UnaryExpr): + + cpdef double _evaluate(self, Solution sol) except *: + return c_log((self.children[0])._evaluate(sol)) + + + +cdef class SqrtExpr(UnaryExpr): + + cpdef double _evaluate(self, Solution sol) except *: + return c_sqrt((self.children[0])._evaluate(sol)) + + + +cdef class SinExpr(UnaryExpr): + + cpdef double _evaluate(self, Solution sol) except *: + return c_sin((self.children[0])._evaluate(sol)) + + + +cdef class CosExpr(UnaryExpr): + cpdef double _evaluate(self, Solution sol) except *: - cdef double res = (self.children[0])._evaluate(sol) - return math.fabs(res) if self._op == "abs" else getattr(math, self._op)(res) + return c_cos((self.children[0])._evaluate(sol)) # class for constant expressions From 3c7a98169ee481977a5ce212157b32f61c057f3c Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 8 Jun 2026 21:50:38 +0800 Subject: [PATCH 02/15] feat(type stubs): add specific unary expression types Update UnaryExpr type stubs to include concrete expression subclasses for more precise type annotations. This change refines the return type of __abs__ and introduces new expression classes. - Change __abs__ return type from GenExpr to AbsExpr - Add AbsExpr, ExpExpr, LogExpr, SqrtExpr, SinExpr, and CosExpr classes - All new classes inherit from UnaryExpr --- src/pyscipopt/scip.pyi | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 86196cfc1..11f43139a 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -2262,7 +2262,25 @@ class Term: class UnaryExpr(GenExpr): def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... - def __abs__(self) -> GenExpr: ... + def __abs__(self) -> AbsExpr: ... + +class AbsExpr(UnaryExpr): + ... + +class ExpExpr(UnaryExpr): + ... + +class LogExpr(UnaryExpr): + ... + +class SqrtExpr(UnaryExpr): + ... + +class SinExpr(UnaryExpr): + ... + +class CosExpr(UnaryExpr): + ... @disjoint_base class VarExpr(GenExpr): From 166dc2117ed6e3957c93ade1e3a871c9cde882e6 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 8 Jun 2026 21:52:09 +0800 Subject: [PATCH 03/15] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80bea8d07..67e86568c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Changed - Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class (#1204) - Speed up `Expr.__add__` and `Expr.__iadd__` via the C-level API +- Replace Python math with C-level math functions and refactor unary expressions. ### Removed ## 6.2.1 - 2026.05.16 From 647f8ac84a8715318dd74482c265cf446feac405 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 8 Jun 2026 22:13:23 +0800 Subject: [PATCH 04/15] style(scip): condense UnaryExpr subclass definitions to single lines --- src/pyscipopt/scip.pyi | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 11f43139a..8dda7cdd6 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -2264,23 +2264,12 @@ class UnaryExpr(GenExpr): def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... def __abs__(self) -> AbsExpr: ... -class AbsExpr(UnaryExpr): - ... - -class ExpExpr(UnaryExpr): - ... - -class LogExpr(UnaryExpr): - ... - -class SqrtExpr(UnaryExpr): - ... - -class SinExpr(UnaryExpr): - ... - -class CosExpr(UnaryExpr): - ... +class AbsExpr(UnaryExpr): ... +class ExpExpr(UnaryExpr): ... +class LogExpr(UnaryExpr): ... +class SqrtExpr(UnaryExpr): ... +class SinExpr(UnaryExpr): ... +class CosExpr(UnaryExpr): ... @disjoint_base class VarExpr(GenExpr): From fadf4e1dc795c8571b428c180ca1ad17321558d8 Mon Sep 17 00:00:00 2001 From: 40% Date: Tue, 9 Jun 2026 02:11:36 +0000 Subject: [PATCH 05/15] Remove extra blank line --- src/pyscipopt/expr.pxi | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 2ad9d1492..5a2a5740c 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -827,7 +827,6 @@ cdef class AbsExpr(UnaryExpr): return c_fabs((self.children[0])._evaluate(sol)) - cdef class ExpExpr(UnaryExpr): cpdef double _evaluate(self, Solution sol) except *: From c27fe01233d96b48acd31c435f58906b630e6800 Mon Sep 17 00:00:00 2001 From: 40% Date: Tue, 9 Jun 2026 20:45:53 +0800 Subject: [PATCH 06/15] Remove extra blank lines --- src/pyscipopt/expr.pxi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 5a2a5740c..eb0317da8 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -833,28 +833,24 @@ cdef class ExpExpr(UnaryExpr): return c_exp((self.children[0])._evaluate(sol)) - cdef class LogExpr(UnaryExpr): cpdef double _evaluate(self, Solution sol) except *: return c_log((self.children[0])._evaluate(sol)) - cdef class SqrtExpr(UnaryExpr): cpdef double _evaluate(self, Solution sol) except *: return c_sqrt((self.children[0])._evaluate(sol)) - cdef class SinExpr(UnaryExpr): cpdef double _evaluate(self, Solution sol) except *: return c_sin((self.children[0])._evaluate(sol)) - cdef class CosExpr(UnaryExpr): cpdef double _evaluate(self, Solution sol) except *: From e5bdabcbe5d5e3b6da04b773d3381546a91dc1cc Mon Sep 17 00:00:00 2001 From: 40% Date: Thu, 11 Jun 2026 22:41:06 +0800 Subject: [PATCH 07/15] Simplify via `ExprLike.__abs__` --- src/pyscipopt/expr.pxi | 15 ++++++--------- src/pyscipopt/scip.pyi | 13 ++++++------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index eb0317da8..4e83099b6 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -283,22 +283,22 @@ cdef class ExprLike: def __neg__(self, /) -> Union[Expr, GenExpr]: return self * -1.0 - def __abs__(self) -> GenExpr: + def __abs__(self, /) -> AbsExpr: return AbsExpr(Operator.fabs, buildGenExprObj(self)) - def exp(self) -> GenExpr: + def exp(self, /) -> ExpExpr: return ExpExpr(Operator.exp, buildGenExprObj(self)) - def log(self) -> GenExpr: + def log(self, /) -> LogExpr: return LogExpr(Operator.log, buildGenExprObj(self)) - def sqrt(self) -> GenExpr: + def sqrt(self, /) -> SqrtExpr: return SqrtExpr(Operator.sqrt, buildGenExprObj(self)) - def sin(self) -> GenExpr: + def sin(self, /) -> SinExpr: return SinExpr(Operator.sin, buildGenExprObj(self)) - def cos(self) -> GenExpr: + def cos(self, /) -> CosExpr: return CosExpr(Operator.cos, buildGenExprObj(self)) @@ -811,9 +811,6 @@ cdef class UnaryExpr(GenExpr): self.children.append(expr) self._op = op - def __abs__(self) -> AbsExpr: - return AbsExpr(Operator.fabs, self) - def __repr__(self) -> str: return self._op + "(" + self.children[0].__repr__() + ")" diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 8dda7cdd6..16b41bc2a 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -338,12 +338,12 @@ class ExprLike: def __rmul__(self, other: object, /) -> Incomplete: ... def __rtruediv__(self, other: object, /) -> GenExpr: ... def __neg__(self, /) -> Union[Expr, GenExpr]: ... - def __abs__(self) -> GenExpr: ... - def exp(self) -> GenExpr: ... - def log(self) -> GenExpr: ... - def sqrt(self) -> GenExpr: ... - def sin(self) -> GenExpr: ... - def cos(self) -> GenExpr: ... + def __abs__(self, /) -> AbsExpr: ... + def exp(self, /) -> ExpExpr: ... + def log(self, /) -> LogExpr: ... + def sqrt(self, /) -> SqrtExpr: ... + def sin(self, /) -> SinExpr: ... + def cos(self, /) -> CosExpr: ... @disjoint_base class Expr(ExprLike): @@ -2262,7 +2262,6 @@ class Term: class UnaryExpr(GenExpr): def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... - def __abs__(self) -> AbsExpr: ... class AbsExpr(UnaryExpr): ... class ExpExpr(UnaryExpr): ... From 8294d8a22cbbe241c9fde3bb14741987e1b018d9 Mon Sep 17 00:00:00 2001 From: 40% Date: Thu, 11 Jun 2026 22:50:50 +0800 Subject: [PATCH 08/15] test C-level math functions --- tests/test_expr.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_expr.py b/tests/test_expr.py index f35096f73..5a3db9cdf 100644 --- a/tests/test_expr.py +++ b/tests/test_expr.py @@ -212,8 +212,16 @@ def test_getVal_with_GenExpr(): assert m.getVal(y / x) == 2 # test "**(prod(1.0,**(sum(0.0,prod(1.0,x)),-1)),2)" assert m.getVal((1 / x) ** 2) == 1 - # test "sin(sum(0.0,prod(1.0,x)))" + + # test C-level math functions + assert m.getVal(abs(x)) == 1 + assert m.getVal(abs(-x)) == 1 + assert m.getVal(abs(abs(-x))) == 1 + assert round(m.getVal(exp(x)), 6) == round(math.exp(1), 6) + assert round(m.getVal(log(x)), 6) == round(math.log(1), 6) + assert round(m.getVal(sqrt(x)), 6) == round(math.sqrt(1), 6) assert round(m.getVal(sin(x)), 6) == round(math.sin(1), 6) + assert round(m.getVal(cos(x)), 6) == round(math.cos(1), 6) with pytest.raises(TypeError): m.getVal(1) From 1262cee1cde4b383a6eeca2af731d7a10a8b7648 Mon Sep 17 00:00:00 2001 From: 40% Date: Sun, 21 Jun 2026 09:47:31 +0800 Subject: [PATCH 09/15] Deprecated inner enumerate class `Operator` --- CHANGELOG.md | 2 + src/pyscipopt/expr.pxi | 152 +++++++++++++++++++---------------------- src/pyscipopt/scip.pxi | 56 +++++++-------- src/pyscipopt/scip.pyi | 2 - 4 files changed, 102 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd04edf8..c8789aa0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - Speed up `Expr.__add__` and `Expr.__iadd__` via the C-level API - Replace Python math with C-level math functions and refactor unary expressions. ### Removed +- Deprecated `getOp` method in `GenExpr` +- Deprecated inner enumerate class `Operator` ## 6.2.1 - 2026.05.16 ### Fixed diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index a869d235d..4d6f50776 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -287,22 +287,22 @@ cdef class ExprLike: return self.copy() def __abs__(self, /) -> AbsExpr: - return AbsExpr(Operator.fabs, buildGenExprObj(self)) + return AbsExpr(buildGenExprObj(self)) def exp(self, /) -> ExpExpr: - return ExpExpr(Operator.exp, buildGenExprObj(self)) + return ExpExpr(buildGenExprObj(self)) def log(self, /) -> LogExpr: - return LogExpr(Operator.log, buildGenExprObj(self)) + return LogExpr(buildGenExprObj(self)) def sqrt(self, /) -> SqrtExpr: - return SqrtExpr(Operator.sqrt, buildGenExprObj(self)) + return SqrtExpr(buildGenExprObj(self)) def sin(self, /) -> SinExpr: - return SinExpr(Operator.sin, buildGenExprObj(self)) + return SinExpr(buildGenExprObj(self)) def cos(self, /) -> CosExpr: - return CosExpr(Operator.cos, buildGenExprObj(self)) + return CosExpr(buildGenExprObj(self)) cdef ExprLike copy(self, bint copy=True): raise NotImplementedError( @@ -539,17 +539,6 @@ def quickprod(termlist): return result -class Op: - const = 'const' - varidx = 'var' - exp, log, sqrt, sin, cos = 'exp', 'log', 'sqrt', 'sin', 'cos' - plus, minus, mul, div, power = '+', '-', '*', '/', '**' - add = 'sum' - prod = 'prod' - fabs = 'abs' - -Operator = Op() - ##@details
 General expressions of variables with operator overloading.
 #
 #@note
@@ -560,7 +549,6 @@ Operator = Op()
 #See also the @ref ExprDetails "description" in the expr.pxi. 
 cdef class GenExpr(ExprLike):
 
-    cdef public _op
     cdef public children
 
     def __init__(self): # do we need it
@@ -575,22 +563,22 @@ cdef class GenExpr(ExprLike):
         ans = SumExpr()
 
         # add left term
-        if left.getOp() == Operator.add:
+        if type(left) is SumExpr:
             ans.coefs.extend(left.coefs)
             ans.children.extend(left.children)
             ans.constant += left.constant
-        elif left.getOp() == Operator.const:
+        elif type(left) is Constant:
             ans.constant += left.number
         else:
             ans.coefs.append(1.0)
             ans.children.append(left)
 
         # add right term
-        if right.getOp() == Operator.add:
+        if type(right) is SumExpr:
             ans.coefs.extend(right.coefs)
             ans.children.extend(right.children)
             ans.constant += right.constant
-        elif right.getOp() == Operator.const:
+        elif type(right) is Constant:
             ans.constant += right.number
         else:
             ans.coefs.append(1.0)
@@ -604,20 +592,20 @@ cdef class GenExpr(ExprLike):
     #    right = buildGenExprObj(other)
     #
     #    # transform self into sum
-    #    if self.getOp() != Operator.add:
+    #    if type(self) is not SumExpr:
     #        newsum = SumExpr()
-    #        if self.getOp() == Operator.const:
+    #        if type(self) is Constant:
     #            newsum.constant += self.number
     #        else:
     #            newsum.coefs.append(1.0)
     #            newsum.children.append(self.copy()) # TODO: what is copy?
     #        self = newsum
     #    # add right term
-    #    if right.getOp() == Operator.add:
+    #    if type(right) is SumExpr:
     #        self.coefs.extend(right.coefs)
     #        self.children.extend(right.children)
     #        self.constant += right.constant
-    #    elif right.getOp() == Operator.const:
+    #    elif type(right) is Constant:
     #        self.constant += right.number
     #    else:
     #        self.coefs.append(1.0)
@@ -633,19 +621,19 @@ cdef class GenExpr(ExprLike):
         ans = ProdExpr()
 
         # multiply left factor
-        if left.getOp() == Operator.prod:
+        if type(left) is ProdExpr:
             ans.children.extend(left.children)
             ans.constant *= left.constant
-        elif left.getOp() == Operator.const:
+        elif type(left) is Constant:
             ans.constant *= left.number
         else:
             ans.children.append(left)
 
         # multiply right factor
-        if right.getOp() == Operator.prod:
+        if type(right) is ProdExpr:
             ans.children.extend(right.children)
             ans.constant *= right.constant
-        elif right.getOp() == Operator.const:
+        elif type(right) is Constant:
             ans.constant *= right.number
         else:
             ans.children.append(right)
@@ -657,18 +645,18 @@ cdef class GenExpr(ExprLike):
     #    assert isinstance(self, Expr)
     #    right = buildGenExprObj(other)
     #    # transform self into prod
-    #    if self.getOp() != Operator.prod:
+    #    if type(self) is not ProdExpr:
     #        newprod = ProdExpr()
-    #        if self.getOp() == Operator.const:
+    #        if type(self) is Constant:
     #            newprod.constant *= self.number
     #        else:
     #            newprod.children.append(self.copy()) # TODO: what is copy?
     #        self = newprod
     #    # multiply right factor
-    #    if right.getOp() == Operator.prod:
+    #    if type(right) is ProdExpr:
     #        self.children.extend(right.children)
     #        self.constant *= right.constant
-    #    elif right.getOp() == Operator.const:
+    #    elif type(right) is Constant:
     #        self.constant *= right.number
     #    else:
     #        self.children.append(right)
@@ -676,9 +664,9 @@ cdef class GenExpr(ExprLike):
 
     def __pow__(self, other, modulo):
         expo = buildGenExprObj(other)
-        if expo.getOp() != Operator.const:
+        if type(expo) is not Constant:
             raise NotImplementedError("exponents must be numbers")
-        if self.getOp() == Operator.const:
+        if type(self) is Constant:
             return Constant(self.number**expo.number)
         ans = PowExpr()
         ans.children.append(self)
@@ -705,7 +693,7 @@ cdef class GenExpr(ExprLike):
 
         divisor = buildGenExprObj(other)
         # we can't divide by 0
-        if isinstance(divisor, GenExpr) and divisor.getOp() == Operator.const and divisor.number == 0.0:
+        if type(divisor) is Constant and divisor.number == 0.0:
             raise ZeroDivisionError("cannot divide by 0")
         return self * divisor**(-1)
 
@@ -718,14 +706,9 @@ cdef class GenExpr(ExprLike):
         '''Note: none of these expressions should be polynomial'''
         return float('inf') 
 
-    def getOp(self):
-        '''returns operator of GenExpr'''
-        return self._op
-
     cdef GenExpr copy(self, bint copy=True):
         cdef object cls = Py_TYPE(self)
         cdef GenExpr res = cls.__new__(cls)
-        res._op = self._op
         res.children = self.children.copy() if copy else self.children
         return res
 
@@ -740,9 +723,9 @@ cdef class SumExpr(GenExpr):
         self.constant = 0.0
         self.coefs = []
         self.children = []
-        self._op = Operator.add
+
     def __repr__(self):
-        return self._op + "(" + str(self.constant) + "," + ",".join(map(lambda child : child.__repr__(), self.children)) + ")"
+        return f"sum({self.constant},{','.join(map(str, self.children))})"
 
     cpdef double _evaluate(self, Solution sol) except *:
         cdef double res = self.constant
@@ -755,7 +738,6 @@ cdef class SumExpr(GenExpr):
 
     cdef SumExpr copy(self, bint copy=True):
         cdef SumExpr res = SumExpr.__new__(SumExpr)
-        res._op = self._op
         res.children = self.children.copy() if copy else self.children
         res.constant = self.constant
         res.coefs = self.coefs.copy() if copy else self.coefs
@@ -770,10 +752,9 @@ cdef class ProdExpr(GenExpr):
     def __init__(self):
         self.constant = 1.0
         self.children = []
-        self._op = Operator.prod
 
     def __repr__(self):
-        return self._op + "(" + str(self.constant) + "," + ",".join(map(lambda child : child.__repr__(), self.children)) + ")"
+        return f"prod({self.constant},{','.join(map(str, self.children))})"
 
     cpdef double _evaluate(self, Solution sol) except *:
         cdef double res = self.constant
@@ -787,7 +768,6 @@ cdef class ProdExpr(GenExpr):
 
     cdef ProdExpr copy(self, bint copy=True):
         cdef ProdExpr res = ProdExpr.__new__(ProdExpr)
-        res._op = self._op
         res.children = self.children.copy() if copy else self.children
         res.constant = self.constant
         return res
@@ -800,10 +780,9 @@ cdef class VarExpr(GenExpr):
 
     def __init__(self, var):
         self.children = [var]
-        self._op = Operator.varidx
 
     def __repr__(self):
-        return self.children[0].__repr__()
+        return self.children[0]
 
     cpdef double _evaluate(self, Solution sol) except *:
         return (self.children[0])._evaluate(sol)
@@ -817,17 +796,15 @@ cdef class PowExpr(GenExpr):
     def __init__(self):
         self.expo = 1.0
         self.children = []
-        self._op = Operator.power
 
     def __repr__(self):
-        return self._op + "(" + self.children[0].__repr__() + "," + str(self.expo) + ")"
+        return f"**({self.children[0]},{self.expo})"
 
     cpdef double _evaluate(self, Solution sol) except *:
         return (self.children[0])._evaluate(sol) ** self.expo
 
     cdef PowExpr copy(self, bint copy=True):
         cdef PowExpr res = PowExpr.__new__(PowExpr)
-        res._op = self._op
         res.children = self.children.copy() if copy else self.children
         res.expo = self.expo
         return res
@@ -835,17 +812,15 @@ cdef class PowExpr(GenExpr):
 
 cdef class UnaryExpr(GenExpr):
 
-    def __init__(self, op, expr):
-        self.children = []
-        self.children.append(expr)
-        self._op = op
-
-    def __repr__(self) -> str:
-        return self._op + "(" + self.children[0].__repr__() + ")"
+    def __init__(self, expr: Union[Expr, GenExpr]):
+        self.children = [expr]
 
 
 cdef class AbsExpr(UnaryExpr):
 
+    def __repr__(self):
+        return f"abs({self.children[0]})"
+
     def __abs__(self) -> AbsExpr:
         return self.copy()
 
@@ -855,40 +830,56 @@ cdef class AbsExpr(UnaryExpr):
 
 cdef class ExpExpr(UnaryExpr):
 
+    def __repr__(self):
+        return f"exp({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_exp((self.children[0])._evaluate(sol))
 
 
 cdef class LogExpr(UnaryExpr):
 
+    def __repr__(self):
+        return f"log({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_log((self.children[0])._evaluate(sol))
 
 
 cdef class SqrtExpr(UnaryExpr):
 
+    def __repr__(self):
+        return f"sqrt({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_sqrt((self.children[0])._evaluate(sol))
 
 
 cdef class SinExpr(UnaryExpr):
 
+    def __repr__(self):
+        return f"sin({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_sin((self.children[0])._evaluate(sol))
 
 
 cdef class CosExpr(UnaryExpr):
 
+    def __repr__(self):
+        return f"cos({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_cos((self.children[0])._evaluate(sol))
 
 
 # class for constant expressions
 cdef class Constant(GenExpr):
+
     cdef public number
-    def __init__(self,number):
+
+    def __init__(self, number: Union[int, float]):
         self.number = number
-        self._op = Operator.const
 
     def __repr__(self):
         return str(self.number)
@@ -899,7 +890,6 @@ cdef class Constant(GenExpr):
     cdef Constant copy(self, bint copy=True):
         # The copy parameter doesn't work; this is for compatibility.
         cdef Constant res = Constant.__new__(Constant)
-        res._op = self._op
         res.number = self.number
         return res
 
@@ -1095,7 +1085,7 @@ def expr_to_nodes(expr):
 
 def value_to_array(val, nodes):
     """adds a given value to an array"""
-    nodes.append(tuple(['const', [val]]))
+    nodes.append((Constant, [val]))
     return len(nodes) - 1
 
 # there many hacky things here: value_to_array is trying to mimick
@@ -1105,24 +1095,26 @@ def value_to_array(val, nodes):
 # haven't even consider substractions, but I guess we would interpret them as a - b = a + (-1) * b
 def expr_to_array(expr, nodes):
     """adds expression to array"""
-    op = expr._op
-    if op == Operator.const: # FIXME: constant expr should also have children!
-        nodes.append(tuple([op, [expr.number]]))
-    elif op != Operator.varidx:
+    t = type(expr)
+    if t is Constant:  # FIXME: constant expr should also have children!
+        nodes.append((t, [expr.number]))
+
+    elif t is not VarExpr:
         indices = []
-        nchildren = len(expr.children)
         for child in expr.children:
-            pos = expr_to_array(child, nodes) # position of child in the final array of nodes, 'nodes'
-            indices.append(pos)
-        if op == Operator.power:
-            pos = value_to_array(expr.expo, nodes)
-            indices.append(pos)
-        elif (op == Operator.add and expr.constant != 0.0) or (op == Operator.prod and expr.constant != 1.0):
-            pos = value_to_array(expr.constant, nodes)
-            indices.append(pos)
-        nodes.append( tuple( [op, indices] ) )
-    else: # var
-        nodes.append( tuple( [op, expr.children] ) )
+            # position of child in the final array of nodes, 'nodes'
+            indices.append(expr_to_array(child, nodes))
+        if t is PowExpr:
+            indices.append(value_to_array(expr.expo, nodes))
+        elif (t is SumExpr and expr.constant != 0) or (
+            t is ProdExpr and expr.constant != 1
+        ):
+            indices.append(value_to_array(expr.constant, nodes))
+
+        nodes.append((t, indices))
+
+    else:  # var
+        nodes.append((t, expr.children))
     return len(nodes) - 1
 
 
diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi
index d047e584b..8aa1c122d 100644
--- a/src/pyscipopt/scip.pxi
+++ b/src/pyscipopt/scip.pxi
@@ -6081,29 +6081,29 @@ cdef class Model:
         # that are the children of this operator. This is sorted,
         # so we are going to do is:
         # loop over the nodes and create the expression of each
-        # Note1: when the operator is Operator.const, [indices] stores the value
+        # Note1: when the operator is const, [indices] stores the value
         # Note2: we need to compute the number of variable operators to find out
         # how many variables are there.
         nvars = 0
         for node in nodes:
-            if node[0] == Operator.varidx:
+            if node[0] is VarExpr:
                 nvars += 1
 
         scipexprs =  malloc(len(nodes) * sizeof(SCIP_EXPR*))
         for i,node in enumerate(nodes):
-            opidx = node[0]
-            if opidx == Operator.varidx:
+            t = node[0]
+            if t is VarExpr:
                 assert len(node[1]) == 1
                 pyvar = node[1][0] # for vars we store the actual var!
                 wrapper = _VarArray(pyvar)
                 PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &scipexprs[i], wrapper.ptr[0], NULL, NULL) )
-                continue
-            if opidx == Operator.const:
+
+            elif t is Constant:
                 assert len(node[1]) == 1
                 value = node[1][0]
                 PY_SCIP_CALL( SCIPcreateExprValue(self._scip, &scipexprs[i], value, NULL, NULL) )
-                continue
-            if opidx == Operator.add:
+
+            elif t is SumExpr:
                 nchildren = len(node[1])
                 childrenexpr =  malloc(nchildren * sizeof(SCIP_EXPR*))
                 coefs =  malloc(nchildren * sizeof(SCIP_Real))
@@ -6113,48 +6113,48 @@ cdef class Model:
                 PY_SCIP_CALL( SCIPcreateExprSum(self._scip, &scipexprs[i], nchildren, childrenexpr, coefs, 0, NULL, NULL))
                 free(coefs)
                 free(childrenexpr)
-                continue
-            if opidx == Operator.prod:
+
+            elif t is ProdExpr:
                 nchildren = len(node[1])
                 childrenexpr =  malloc(nchildren * sizeof(SCIP_EXPR*))
                 for c, pos in enumerate(node[1]):
                     childrenexpr[c] = scipexprs[pos]
                 PY_SCIP_CALL( SCIPcreateExprProduct(self._scip, &scipexprs[i], nchildren, childrenexpr, 1, NULL, NULL) )
                 free(childrenexpr)
-                continue
-            if opidx == Operator.power:
+
+            elif t is PowExpr:
                 # the second child is the exponent which is a const
                 valuenode = nodes[node[1][1]]
-                assert valuenode[0] == Operator.const
+                assert valuenode[0] is Constant
                 exponent = valuenode[1][0]
                 PY_SCIP_CALL( SCIPcreateExprPow(self._scip, &scipexprs[i], scipexprs[node[1][0]], exponent, NULL, NULL ))
-                continue
-            if opidx == Operator.exp:
+
+            elif t is ExpExpr:
                 assert len(node[1]) == 1
                 PY_SCIP_CALL( SCIPcreateExprExp(self._scip, &scipexprs[i], scipexprs[node[1][0]], NULL, NULL ))
-                continue
-            if opidx == Operator.log:
+
+            elif t is LogExpr:
                 assert len(node[1]) == 1
                 PY_SCIP_CALL( SCIPcreateExprLog(self._scip, &scipexprs[i], scipexprs[node[1][0]], NULL, NULL ))
-                continue
-            if opidx == Operator.sqrt:
+
+            elif t is SqrtExpr:
                 assert len(node[1]) == 1
                 PY_SCIP_CALL( SCIPcreateExprPow(self._scip, &scipexprs[i], scipexprs[node[1][0]], 0.5, NULL, NULL) )
-                continue
-            if opidx == Operator.sin:
+
+            elif t is SinExpr:
                 assert len(node[1]) == 1
                 PY_SCIP_CALL( SCIPcreateExprSin(self._scip, &scipexprs[i], scipexprs[node[1][0]], NULL, NULL) )
-                continue
-            if opidx == Operator.cos:
+
+            elif t is CosExpr:
                 assert len(node[1]) == 1
                 PY_SCIP_CALL( SCIPcreateExprCos(self._scip, &scipexprs[i], scipexprs[node[1][0]], NULL, NULL) )
-                continue
-            if opidx == Operator.fabs:
+
+            elif t is AbsExpr:
                 assert len(node[1]) == 1
                 PY_SCIP_CALL( SCIPcreateExprAbs(self._scip, &scipexprs[i], scipexprs[node[1][0]], NULL, NULL ))
-                continue
-            # default:
-            raise NotImplementedError
+
+            else:
+                raise NotImplementedError
 
         # create nonlinear constraint for the expression root
         PY_SCIP_CALL( SCIPcreateConsNonlinear(
diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi
index 0881f8bba..935eebb1b 100644
--- a/src/pyscipopt/scip.pyi
+++ b/src/pyscipopt/scip.pyi
@@ -389,11 +389,9 @@ class ExprCons:
 
 @disjoint_base
 class GenExpr(ExprLike):
-    _op: Incomplete
     children: Incomplete
     def __init__(self) -> None: ...
     def degree(self) -> Incomplete: ...
-    def getOp(self) -> Incomplete: ...
     def __add__(self, other: Incomplete, /) -> Incomplete: ...
     def __eq__(self, other: object, /) -> bool: ...
     def __ge__(self, other: object, /) -> bool: ...

From 485ec2d9130fcbce121d4cf02217c3cf7d31a533 Mon Sep 17 00:00:00 2001
From: 40% 
Date: Sat, 19 Sep 2026 10:26:17 +0800
Subject: [PATCH 10/15] add back the missing method `_evaluate`

---
 src/pyscipopt/expr.pxi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi
index b60559a24..cebdc6cec 100644
--- a/src/pyscipopt/expr.pxi
+++ b/src/pyscipopt/expr.pxi
@@ -305,6 +305,11 @@ cdef class ExprLike:
     def cos(self, /) -> CosExpr:
         return CosExpr(buildGenExprObj(self))
 
+    cpdef double _evaluate(self, Solution sol) except *:
+        raise NotImplementedError(
+            f"{self.__class__.__name__!s} need to implement _evaluate() method"
+        )
+
     cdef ExprLike copy(self, bint copy=True):
         raise NotImplementedError(
             f"{self.__class__.__name__!s} need to implement copy() method"

From f2e91b50e8902b642f445c2c36d079a9fcb5e0d3 Mon Sep 17 00:00:00 2001
From: 40% 
Date: Sat, 19 Sep 2026 10:32:42 +0800
Subject: [PATCH 11/15] add pr number to changelog

---
 CHANGELOG.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 949239657..d6a43f345 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,8 +19,8 @@
 - Speed up `ProdExpr.__neg__` and `Constant.__neg__` via C-level API (#1250)
 - Extended `structured_optimization_trace` recipe to support context-managed JSONL tracing with final `run_end` records, alongside the existing attach-style in-memory tracing.
 ### Removed
-- Deprecated `getOp` method in `GenExpr`
-- Deprecated inner enumerate class `Operator`
+- Deprecated `getOp` method in `GenExpr` (#1259)
+- Deprecated inner enumerate class `Operator` (#1259)
 
 ## 6.2.1 - 2026.05.16
 ### Fixed

From 30af678e1653f977644c765c6b9ce8197e710fdd Mon Sep 17 00:00:00 2001
From: 40% 
Date: Sat, 19 Sep 2026 10:37:38 +0800
Subject: [PATCH 12/15] remove `getOp`, `Op`, and `Operator` from pyi file

---
 src/pyscipopt/scip.pyi | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi
index a99678dc5..9822803bf 100644
--- a/src/pyscipopt/scip.pyi
+++ b/src/pyscipopt/scip.pyi
@@ -10,7 +10,6 @@ CONST: Term
 EventNames: dict
 MAJOR: int
 MINOR: int
-Operator: Op
 PATCH: int
 PY_SCIP_CALL: Incomplete
 StageNames: dict
@@ -444,7 +443,6 @@ class GenExpr(ExprLike):
     children: Incomplete
     def __init__(self) -> None: ...
     def degree(self) -> Incomplete: ...
-    def getOp(self) -> Incomplete: ...
     def __neg__(self, /) -> ProdExpr | Constant: ...
     @overload
     def __add__(self, other: float | ExprLike, /) -> SumExpr: ...
@@ -1843,23 +1841,6 @@ class Nodesel:
     def nodeinitsol(self) -> Incomplete: ...
     def nodeselect(self) -> Incomplete: ...
 
-class Op:
-    add: ClassVar[str] = ...
-    const: ClassVar[str] = ...
-    cos: ClassVar[str] = ...
-    div: ClassVar[str] = ...
-    exp: ClassVar[str] = ...
-    fabs: ClassVar[str] = ...
-    log: ClassVar[str] = ...
-    minus: ClassVar[str] = ...
-    mul: ClassVar[str] = ...
-    plus: ClassVar[str] = ...
-    power: ClassVar[str] = ...
-    prod: ClassVar[str] = ...
-    sin: ClassVar[str] = ...
-    sqrt: ClassVar[str] = ...
-    varidx: ClassVar[str] = ...
-
 class PY_SCIP_BENDERSENFOTYPE:
     CHECK: ClassVar[int] = ...
     LP: ClassVar[int] = ...

From 5dc43dbfccd687bc7b78696cdca8967f39cf35c2 Mon Sep 17 00:00:00 2001
From: 40% 
Date: Sat, 19 Sep 2026 10:47:56 +0800
Subject: [PATCH 13/15] remove op argument form `UnaryExpr`

---
 src/pyscipopt/expr.pxi | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi
index cebdc6cec..51bdf7ef0 100644
--- a/src/pyscipopt/expr.pxi
+++ b/src/pyscipopt/expr.pxi
@@ -823,13 +823,8 @@ cdef class PowExpr(GenExpr):
 
 cdef class UnaryExpr(GenExpr):
 
-    def __init__(self, op, expr):
-        self.children = []
-        self.children.append(expr)
-        self._op = op
-
-    def __repr__(self) -> str:
-        return self._op + "(" + self.children[0].__repr__() + ")"
+    def __init__(self, expr: Union[Expr, GenExpr]):
+        self.children = [expr]
 
 
 cdef class AbsExpr(UnaryExpr):
@@ -837,18 +832,27 @@ cdef class AbsExpr(UnaryExpr):
     def __abs__(self) -> AbsExpr:
         return self.copy()
 
+    def __repr__(self) -> str:
+        return f"abs({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_fabs((self.children[0])._evaluate(sol))
 
 
 cdef class ExpExpr(UnaryExpr):
 
+    def __repr__(self) -> str:
+        return f"exp({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         return c_exp((self.children[0])._evaluate(sol))
 
 
 cdef class LogExpr(UnaryExpr):
 
+    def __repr__(self) -> str:
+        return f"log({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         cdef double val = (self.children[0])._evaluate(sol)
         if val <= 0.0:
@@ -858,6 +862,9 @@ cdef class LogExpr(UnaryExpr):
 
 cdef class SqrtExpr(UnaryExpr):
 
+    def __repr__(self) -> str:
+        return f"sqrt({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         cdef double val = (self.children[0])._evaluate(sol)
         if val < 0.0:
@@ -867,6 +874,9 @@ cdef class SqrtExpr(UnaryExpr):
 
 cdef class SinExpr(UnaryExpr):
 
+    def __repr__(self) -> str:
+        return f"sin({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         cdef double val = (self.children[0])._evaluate(sol)
         if c_fabs(val) == INFINITY:
@@ -876,6 +886,9 @@ cdef class SinExpr(UnaryExpr):
 
 cdef class CosExpr(UnaryExpr):
 
+    def __repr__(self) -> str:
+        return f"cos({self.children[0]})"
+
     cpdef double _evaluate(self, Solution sol) except *:
         cdef double val = (self.children[0])._evaluate(sol)
         if c_fabs(val) == INFINITY:
@@ -883,7 +896,6 @@ cdef class CosExpr(UnaryExpr):
         return c_cos(val)
 
 
-# class for constant expressions
 cdef class Constant(GenExpr):
 
     cdef public number

From c8ed7ef21765dc1ac03d9278c528875bc408f6c7 Mon Sep 17 00:00:00 2001
From: 40% 
Date: Sat, 19 Sep 2026 11:46:22 +0800
Subject: [PATCH 14/15] @ Return a string from VarExpr.__repr__

VarExpr.__repr__ returned the wrapped Variable object instead of its name.
Since Variable defines __repr__ but no __str__, str() on it fell back to
__repr__, handed the non-string object back, and str.__str__ raised
TypeError: __str__ returned non-string (type pyscipopt.scip.Variable).

SumExpr and ProdExpr render their children with map(str, ...), so any
VarExpr reached by buildGenExprObj or GenExpr.__add__/__mul__ blew up --
e.g. str(abs(x)), str(sqrt(x) * -1), and repr(2**x) vs repr(exp(x * log(2))).
PowExpr and the unary __repr__ methods relied on the same implicit str(),
so the leaf node is now responsible for returning a real string.

Also annotate the __repr__ return type as str.
@
---
 src/pyscipopt/expr.pxi | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi
index 51bdf7ef0..834b6c1e4 100644
--- a/src/pyscipopt/expr.pxi
+++ b/src/pyscipopt/expr.pxi
@@ -143,7 +143,7 @@ cdef class Term:
         res.hashval = hash(tuple(v.ptr() for v in res.vartuple))
         return res
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return 'Term(%s)' % ', '.join([str(v) for v in self.vartuple])
 
     cpdef double _evaluate(self, Solution sol) except *:
@@ -430,7 +430,7 @@ cdef class Expr(ExprLike):
         '''remove terms with coefficient of 0'''
         self.terms =  {t:c for (t,c) in self.terms.items() if c != 0.0}
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return 'Expr(%s)' % repr(self.terms)
 
     def degree(self):
@@ -511,7 +511,7 @@ cdef class ExprCons:
         else:
             raise NotImplementedError("Ranged ExprCons can only support with '<=' or '>='.")
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return 'ExprCons(%s, %s, %s)' % (self.expr, self._lhs, self._rhs)
 
     def __bool__(self):
@@ -730,7 +730,7 @@ cdef class SumExpr(GenExpr):
         self.coefs = []
         self.children = []
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f"sum({self.constant},{','.join(map(str, self.children))})"
 
     cpdef double _evaluate(self, Solution sol) except *:
@@ -764,7 +764,7 @@ cdef class ProdExpr(GenExpr):
         res.constant = -res.constant
         return res
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f"prod({self.constant},{','.join(map(str, self.children))})"
 
     cpdef double _evaluate(self, Solution sol) except *:
@@ -792,8 +792,8 @@ cdef class VarExpr(GenExpr):
     def __init__(self, var):
         self.children = [var]
 
-    def __repr__(self):
-        return self.children[0]
+    def __repr__(self) -> str:
+        return str(self.children[0])
 
     cpdef double _evaluate(self, Solution sol) except *:
         return (self.children[0])._evaluate(sol)
@@ -808,7 +808,7 @@ cdef class PowExpr(GenExpr):
         self.expo = 1.0
         self.children = []
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f"**({self.children[0]},{self.expo})"
 
     cpdef double _evaluate(self, Solution sol) except *:
@@ -906,7 +906,7 @@ cdef class Constant(GenExpr):
     def __neg__(self, /) -> Constant:
         return Constant(-self.number)
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return str(self.number)
 
     cpdef double _evaluate(self, Solution sol) except *:

From a6169ce5251e587ea009618b6a33f90b5732b9f8 Mon Sep 17 00:00:00 2001
From: 40% 
Date: Sun, 20 Sep 2026 10:34:53 +0800
Subject: [PATCH 15/15] Change type checks to use Py_TYPE for expressions

---
 src/pyscipopt/expr.pxi | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi
index 834b6c1e4..aad037420 100644
--- a/src/pyscipopt/expr.pxi
+++ b/src/pyscipopt/expr.pxi
@@ -569,22 +569,22 @@ cdef class GenExpr(ExprLike):
         ans = SumExpr()
 
         # add left term
-        if type(left) is SumExpr:
+        if Py_TYPE(left) is SumExpr:
             ans.coefs.extend(left.coefs)
             ans.children.extend(left.children)
             ans.constant += left.constant
-        elif type(left) is Constant:
+        elif Py_TYPE(left) is Constant:
             ans.constant += left.number
         else:
             ans.coefs.append(1.0)
             ans.children.append(left)
 
         # add right term
-        if type(right) is SumExpr:
+        if Py_TYPE(right) is SumExpr:
             ans.coefs.extend(right.coefs)
             ans.children.extend(right.children)
             ans.constant += right.constant
-        elif type(right) is Constant:
+        elif Py_TYPE(right) is Constant:
             ans.constant += right.number
         else:
             ans.coefs.append(1.0)
@@ -598,20 +598,20 @@ cdef class GenExpr(ExprLike):
     #    right = buildGenExprObj(other)
     #
     #    # transform self into sum
-    #    if type(self) is not SumExpr:
+    #    if Py_TYPE(self) is not SumExpr:
     #        newsum = SumExpr()
-    #        if type(self) is Constant:
+    #        if Py_TYPE(self) is Constant:
     #            newsum.constant += self.number
     #        else:
     #            newsum.coefs.append(1.0)
     #            newsum.children.append(self.copy()) # TODO: what is copy?
     #        self = newsum
     #    # add right term
-    #    if type(right) is SumExpr:
+    #    if Py_TYPE(right) is SumExpr:
     #        self.coefs.extend(right.coefs)
     #        self.children.extend(right.children)
     #        self.constant += right.constant
-    #    elif type(right) is Constant:
+    #    elif Py_TYPE(right) is Constant:
     #        self.constant += right.number
     #    else:
     #        self.coefs.append(1.0)
@@ -627,19 +627,19 @@ cdef class GenExpr(ExprLike):
         ans = ProdExpr()
 
         # multiply left factor
-        if type(left) is ProdExpr:
+        if Py_TYPE(left) is ProdExpr:
             ans.children.extend(left.children)
             ans.constant *= left.constant
-        elif type(left) is Constant:
+        elif Py_TYPE(left) is Constant:
             ans.constant *= left.number
         else:
             ans.children.append(left)
 
         # multiply right factor
-        if type(right) is ProdExpr:
+        if Py_TYPE(right) is ProdExpr:
             ans.children.extend(right.children)
             ans.constant *= right.constant
-        elif type(right) is Constant:
+        elif Py_TYPE(right) is Constant:
             ans.constant *= right.number
         else:
             ans.children.append(right)
@@ -651,18 +651,18 @@ cdef class GenExpr(ExprLike):
     #    assert isinstance(self, Expr)
     #    right = buildGenExprObj(other)
     #    # transform self into prod
-    #    if type(self) is not ProdExpr:
+    #    if Py_TYPE(self) is not ProdExpr:
     #        newprod = ProdExpr()
-    #        if type(self) is Constant:
+    #        if Py_TYPE(self) is Constant:
     #            newprod.constant *= self.number
     #        else:
     #            newprod.children.append(self.copy()) # TODO: what is copy?
     #        self = newprod
     #    # multiply right factor
-    #    if type(right) is ProdExpr:
+    #    if Py_TYPE(right) is ProdExpr:
     #        self.children.extend(right.children)
     #        self.constant *= right.constant
-    #    elif type(right) is Constant:
+    #    elif Py_TYPE(right) is Constant:
     #        self.constant *= right.number
     #    else:
     #        self.children.append(right)
@@ -670,9 +670,9 @@ cdef class GenExpr(ExprLike):
 
     def __pow__(self, other, modulo):
         expo = buildGenExprObj(other)
-        if type(expo) is not Constant:
+        if Py_TYPE(expo) is not Constant:
             raise NotImplementedError("exponents must be numbers")
-        if type(self) is Constant:
+        if Py_TYPE(self) is Constant:
             return Constant(self.number**expo.number)
         ans = PowExpr()
         ans.children.append(self)
@@ -699,7 +699,7 @@ cdef class GenExpr(ExprLike):
 
         divisor = buildGenExprObj(other)
         # we can't divide by 0
-        if type(divisor) is Constant and divisor.number == 0.0:
+        if Py_TYPE(divisor) is Constant and divisor.number == 0.0:
             raise ZeroDivisionError("cannot divide by 0")
         return self * divisor**(-1)
 
@@ -1120,7 +1120,7 @@ def value_to_array(val, nodes):
 # haven't even consider substractions, but I guess we would interpret them as a - b = a + (-1) * b
 def expr_to_array(expr, nodes):
     """adds expression to array"""
-    t = type(expr)
+    t = Py_TYPE(expr)
     if t is Constant:  # FIXME: constant expr should also have children!
         nodes.append((t, [expr.number]))