From cf898c478b77e0d06ad79fc57a0c2517aeed92eb Mon Sep 17 00:00:00 2001
From: Alexandru Fikl <alexfikl@gmail.com>
Date: Thu, 4 Feb 2021 20:34:25 -0600
Subject: [PATCH] run pyupgrade over everything

---
 doc/conf.py                              |  4 +-
 pymbolic/__init__.py                     |  3 -
 pymbolic/algorithm.py                    | 16 +++---
 pymbolic/compiler.py                     | 13 ++---
 pymbolic/cse.py                          | 12 ++--
 pymbolic/functions.py                    |  2 -
 pymbolic/geometric_algebra/__init__.py   | 73 ++++++++++++------------
 pymbolic/geometric_algebra/mapper.py     | 30 +++-------
 pymbolic/geometric_algebra/primitives.py |  4 +-
 pymbolic/imperative/statement.py         | 16 +++---
 pymbolic/imperative/transform.py         |  2 +-
 pymbolic/imperative/utils.py             | 10 ++--
 pymbolic/interop/ast.py                  | 10 ++--
 pymbolic/interop/common.py               |  4 +-
 pymbolic/interop/maxima.py               | 18 ++----
 pymbolic/interop/symengine.py            |  2 -
 pymbolic/interop/sympy.py                |  2 -
 pymbolic/mapper/__init__.py              | 28 +++++----
 pymbolic/mapper/c_code.py                |  8 +--
 pymbolic/mapper/coefficient.py           | 10 ++--
 pymbolic/mapper/collector.py             |  8 +--
 pymbolic/mapper/constant_converter.py    |  2 -
 pymbolic/mapper/constant_folder.py       |  4 +-
 pymbolic/mapper/cse_tagger.py            |  2 -
 pymbolic/mapper/dependency.py            | 22 ++++---
 pymbolic/mapper/differentiator.py        |  2 -
 pymbolic/mapper/distributor.py           |  2 -
 pymbolic/mapper/evaluator.py             |  8 +--
 pymbolic/mapper/flattener.py             |  2 -
 pymbolic/mapper/flop_counter.py          |  2 -
 pymbolic/mapper/graphviz.py              | 18 +++---
 pymbolic/mapper/persistent_hash.py       |  2 -
 pymbolic/mapper/stringifier.py           | 22 ++++---
 pymbolic/mapper/substitutor.py           |  2 -
 pymbolic/mapper/unifier.py               | 26 +++------
 pymbolic/parser.py                       |  6 +-
 pymbolic/polynomial.py                   |  7 +--
 pymbolic/primitives.py                   | 16 +++---
 pymbolic/rational.py                     |  2 -
 pymbolic/traits.py                       |  6 +-
 setup.py                                 |  1 -
 test/simple.py                           |  2 -
 test/test_maxima.py                      |  2 -
 test/test_pymbolic.py                    | 38 ++++++------
 test/test_sympy.py                       |  2 -
 45 files changed, 183 insertions(+), 290 deletions(-)

diff --git a/doc/conf.py b/doc/conf.py
index 2ae4ff4..20ea09a 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -21,8 +21,8 @@ source_suffix = ".rst"
 master_doc = "index"
 
 # General information about the project.
-project = u"pymbolic"
-copyright = u"2013, Andreas Kloeckner"
+project = "pymbolic"
+copyright = "2013, Andreas Kloeckner"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
diff --git a/pymbolic/__init__.py b/pymbolic/__init__.py
index d0e2ca0..82c4ba4 100644
--- a/pymbolic/__init__.py
+++ b/pymbolic/__init__.py
@@ -1,6 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/algorithm.py b/pymbolic/algorithm.py
index 39c44bb..e27532d 100644
--- a/pymbolic/algorithm.py
+++ b/pymbolic/algorithm.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -23,7 +21,7 @@ THE SOFTWARE.
 """
 
 import six
-from six.moves import range, zip, reduce
+from six.moves import reduce
 import cmath
 from pytools import memoize
 
@@ -312,8 +310,8 @@ def solve_affine_equations_for(unknowns, equations):
     from pymbolic import var
     unknowns = [var(u) for u in unknowns]
     unknowns_set = set(unknowns)
-    unknown_idx_lut = dict((tgt_name, idx)
-            for idx, tgt_name in enumerate(unknowns))
+    unknown_idx_lut = {tgt_name: idx
+            for idx, tgt_name in enumerate(unknowns)}
 
     # Find non-unknown variables, fix order for them
     # Last non-unknown is constant.
@@ -323,8 +321,8 @@ def solve_affine_equations_for(unknowns, equations):
         parameters.update(dep_map(rhs) - unknowns_set)
 
     parameters_list = list(parameters)
-    parameter_idx_lut = dict((var_name, idx)
-            for idx, var_name in enumerate(parameters_list))
+    parameter_idx_lut = {var_name: idx
+            for idx, var_name in enumerate(parameters_list)}
 
     from pymbolic.mapper.coefficient import CoefficientCollector
     coeff_coll = CoefficientCollector()
@@ -336,7 +334,7 @@ def solve_affine_equations_for(unknowns, equations):
 
     for i_eqn, (lhs, rhs) in enumerate(equations):
         for lhs_factor, coeffs in [(1, coeff_coll(lhs)), (-1, coeff_coll(rhs))]:
-            for key, coeff in six.iteritems(coeffs):
+            for key, coeff in coeffs.items():
                 if key in unknowns_set:
                     mat[i_eqn, unknown_idx_lut[key]] = lhs_factor*coeff
                 elif key in parameters:
@@ -375,7 +373,7 @@ def solve_affine_equations_for(unknowns, equations):
         for lhs, rhs in equations:
             print(lhs, "=", rhs)
         print("-------------------")
-        for lhs, rhs in six.iteritems(result):
+        for lhs, rhs in result.items():
             print(lhs, "=", rhs)
 
     return result
diff --git a/pymbolic/compiler.py b/pymbolic/compiler.py
index 2f749e2..f1be217 100644
--- a/pymbolic/compiler.py
+++ b/pymbolic/compiler.py
@@ -1,6 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -56,7 +53,7 @@ class CompileMapper(StringifyMapper):
             elif exp == 1:
                 return "*%s" % sbase
             else:
-                return "*%s**%s" % (sbase, exp)
+                return f"*{sbase}**{exp}"
 
         result = ""
         rev_data = expr.data[::-1]
@@ -65,7 +62,7 @@ class CompileMapper(StringifyMapper):
                 next_exp = rev_data[i+1][0]
             else:
                 next_exp = 0
-            result = "(%s+%s)%s" % (result, self(coeff, PREC_SUM),
+            result = "({}+{}){}".format(result, self(coeff, PREC_SUM),
                     stringify_exp(exp-next_exp))
 
         if enclosing_prec > PREC_SUM and len(expr.data) > 1:
@@ -89,7 +86,7 @@ class CompileMapper(StringifyMapper):
         return StringifyMapper.map_foreign(self, expr, enclosing_prec)
 
 
-class CompiledExpression(object):
+class CompiledExpression:
     """This class encapsulates an expression compiled into Python bytecode
     for faster evaluation.
 
@@ -122,13 +119,13 @@ class CompiledExpression(object):
         used_variables = DependencyMapper(
                 composite_leaves=False)(self._Expression)
         used_variables -= set(self._Variables)
-        used_variables -= set(pymbolic.var(key) for key in list(ctx.keys()))
+        used_variables -= {pymbolic.var(key) for key in list(ctx.keys())}
         used_variables = list(used_variables)
         used_variables.sort()
         all_variables = self._Variables + used_variables
 
         expr_s = CompileMapper()(self._Expression, PREC_NONE)
-        func_s = "lambda %s: %s" % (",".join(str(v) for v in all_variables),
+        func_s = "lambda {}: {}".format(",".join(str(v) for v in all_variables),
                 expr_s)
         self._code = eval(func_s, ctx)
 
diff --git a/pymbolic/cse.py b/pymbolic/cse.py
index d145946..3e0bce4 100644
--- a/pymbolic/cse.py
+++ b/pymbolic/cse.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import six
 
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
@@ -30,14 +28,14 @@ from pymbolic.mapper import IdentityMapper, WalkMapper
 COMMUTATIVE_CLASSES = (prim.Sum, prim.Product)
 
 
-class NormalizedKeyGetter(object):
+class NormalizedKeyGetter:
     def __call__(self, expr):
         if isinstance(expr, COMMUTATIVE_CLASSES):
             kid_count = {}
             for child in expr.children:
                 kid_count[child] = kid_count.get(child, 0) + 1
 
-            return type(expr), frozenset(six.iteritems(kid_count))
+            return type(expr), frozenset(kid_count.items())
         else:
             return expr
 
@@ -139,9 +137,9 @@ def tag_common_subexpressions(exprs):
     for expr in exprs:
         ucm(expr)
 
-    to_eliminate = set([subexpr_key
-        for subexpr_key, count in six.iteritems(ucm.subexpr_counts)
-        if count > 1])
+    to_eliminate = {subexpr_key
+        for subexpr_key, count in ucm.subexpr_counts.items()
+        if count > 1}
 
     cse_mapper = CSEMapper(to_eliminate, get_key)
     result = [cse_mapper(expr) for expr in exprs]
diff --git a/pymbolic/functions.py b/pymbolic/functions.py
index b1a98df..7f3955d 100644
--- a/pymbolic/functions.py
+++ b/pymbolic/functions.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/geometric_algebra/__init__.py b/pymbolic/geometric_algebra/__init__.py
index 1258b18..d6cfb3f 100644
--- a/pymbolic/geometric_algebra/__init__.py
+++ b/pymbolic/geometric_algebra/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -23,7 +21,6 @@ THE SOFTWARE.
 """
 
 import six
-from six.moves import range
 
 from pytools import memoize, memoize_method
 import numpy as np
@@ -179,7 +176,7 @@ def canonical_reordering_sign(a_bits, b_bits):
 
 # {{{ space
 
-class Space(object):
+class Space:
     """
     .. attribute :: basis_names
 
@@ -266,7 +263,7 @@ class Space(object):
         elif self.is_euclidean:
             return "Space(%r)" % self.basis_names
         else:
-            return "Space(%r, %r)" % (self.basis_names, self.metric_matrix)
+            return f"Space({self.basis_names!r}, {self.metric_matrix!r})"
 
 
 @memoize
@@ -295,7 +292,7 @@ def _shared_metric_coeff(shared_bits, space):
     return result
 
 
-class _GAProduct(object):
+class _GAProduct:
     pass
 
 
@@ -396,7 +393,7 @@ def _cast_or_ni(obj, space):
         return MultiVector(obj, space)
 
 
-class MultiVector(object):
+class MultiVector:
     r"""An immutable multivector type. Its implementation follows [DFM].
     It is pickleable, and not picky about what data is used as coefficients.
     It supports :class:`pymbolic.primitives.Expression` objects of course,
@@ -524,8 +521,8 @@ class MultiVector(object):
                 raise ValueError("only numpy vectors (not higher-rank objects) "
                         "are supported for 'data'")
             dimensions, = data.shape
-            data = dict(
-                    ((i,), xi) for i, xi in enumerate(data))
+            data = {
+                    (i,): xi for i, xi in enumerate(data)}
         elif isinstance(data, dict):
             pass
         else:
@@ -542,10 +539,10 @@ class MultiVector(object):
 
         from pytools import single_valued
         from pymbolic.primitives import is_zero
-        if data and single_valued(isinstance(k, tuple) for k in six.iterkeys(data)):
+        if data and single_valued(isinstance(k, tuple) for k in data.keys()):
             # data is in non-normalized non-bits tuple form
             new_data = {}
-            for basis_indices, coeff in six.iteritems(data):
+            for basis_indices, coeff in data.items():
                 bits, sign = space.bits_and_sign(basis_indices)
                 new_coeff = new_data.setdefault(bits, 0) + sign*coeff
 
@@ -560,7 +557,7 @@ class MultiVector(object):
 
         # assert that multivectors don't get nested
         assert not any(isinstance(coeff, MultiVector)
-                for coeff in six.itervalues(data))
+                for coeff in data.values())
 
         self.space = space
         self.data = data
@@ -578,7 +575,7 @@ class MultiVector(object):
         from pymbolic.mapper.stringifier import PREC_PRODUCT, PREC_SUM
 
         terms = []
-        for bits in sorted(six.iterkeys(self.data),
+        for bits in sorted(self.data.keys(),
                 key=lambda bits: (bit_count(bits), bits)):
             coeff = self.data[bits]
 
@@ -605,7 +602,7 @@ class MultiVector(object):
 
             blade_str = self.space.blade_bits_to_str(bits)
             if bits:
-                terms.append("%s * %s" % (blade_str, coeff_str))
+                terms.append(f"{blade_str} * {coeff_str}")
             else:
                 terms.append(coeff_str)
 
@@ -624,7 +621,7 @@ class MultiVector(object):
         return self.stringify(None, PREC_NONE)
 
     def __repr__(self):
-        return "MultiVector(%s, %r)" % (self.data, self.space)
+        return f"MultiVector({self.data}, {self.space!r})"
 
     # }}}
 
@@ -632,8 +629,8 @@ class MultiVector(object):
 
     def __neg__(self):
         return MultiVector(
-                dict((bits, -coeff)
-                    for bits, coeff in six.iteritems(self.data)),
+                {bits: -coeff
+                    for bits, coeff in self.data.items()},
                 self.space)
 
     def __add__(self, other):
@@ -644,7 +641,7 @@ class MultiVector(object):
         if self.space is not other.space:
             raise ValueError("can only add multivectors from identical spaces")
 
-        all_bits = set(six.iterkeys(self.data)) | set(six.iterkeys(other.data))
+        all_bits = set(self.data.keys()) | set(other.data.keys())
 
         from pymbolic.primitives import is_zero
         new_data = {}
@@ -685,8 +682,8 @@ class MultiVector(object):
 
         from pymbolic.primitives import is_zero
         new_data = {}
-        for sbits, scoeff in six.iteritems(self.data):
-            for obits, ocoeff in six.iteritems(other.data):
+        for sbits, scoeff in self.data.items():
+            for obits, ocoeff in other.data.items():
                 new_bits = sbits ^ obits
                 weight = bpw(sbits, obits, self.space)
 
@@ -821,14 +818,14 @@ class MultiVector(object):
             raise ZeroDivisionError
         if len(self.data) > 1:
             if self.get_pure_grade() in [0, 1, self.space.dimensions]:
-                return MultiVector(dict(
-                    (bits, coeff/nsqr) for bits, coeff in six.iteritems(self.data)),
+                return MultiVector({
+                    bits: coeff/nsqr for bits, coeff in self.data.items()},
                     self.space)
 
             else:
                 raise NotImplementedError("division by non-blades")
 
-        (bits, coeff), = six.iteritems(self.data)
+        (bits, coeff), = self.data.items()
 
         # (1.1.54) in [HS]
         grade = bit_count(bits)
@@ -846,7 +843,7 @@ class MultiVector(object):
         Often written :math:`A^\dagger`.
         """
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             grade = bit_count(bits)
             if grade*(grade-1)//2 % 2 == 0:
                 new_data[bits] = coeff
@@ -862,7 +859,7 @@ class MultiVector(object):
         Often written :math:`\widehat A`.
         """
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             grade = bit_count(bits)
             if grade % 2 == 0:
                 new_data[bits] = coeff
@@ -902,7 +899,7 @@ class MultiVector(object):
     @memoize_method
     def __hash__(self):
         result = hash(self.space)
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             result ^= hash(bits) ^ hash(coeff)
 
         return result
@@ -931,7 +928,7 @@ class MultiVector(object):
             tol = 1e-12
 
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             if abs(coeff) > tol:
                 new_data[bits] = coeff
 
@@ -950,10 +947,10 @@ class MultiVector(object):
         """
 
         if grade is None:
-            for bits, coeff in six.iteritems(self.data):
+            for bits, coeff in self.data.items():
                 yield MultiVector({bits: coeff}, self.space)
         else:
-            for bits, coeff in six.iteritems(self.data):
+            for bits, coeff in self.data.items():
                 if bit_count(bits) == grade:
                     yield MultiVector({bits: coeff}, self.space)
 
@@ -963,7 +960,7 @@ class MultiVector(object):
         Often written :math:`\langle A\rangle_r`.
         """
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             if bit_count(bits) == r:
                 new_data[bits] = coeff
 
@@ -984,7 +981,7 @@ class MultiVector(object):
     def all_grades(self):
         """Return a :class:`set` of grades occurring in *self*."""
 
-        return set(bit_count(bits) for bits, coeff in six.iteritems(self.data))
+        return {bit_count(bits) for bits, coeff in self.data.items()}
 
     def get_pure_grade(self):
         """If *self* only has components of a single grade, return
@@ -995,7 +992,7 @@ class MultiVector(object):
 
         result = None
 
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             grade = bit_count(bits)
             if result is None:
                 result = grade
@@ -1009,7 +1006,7 @@ class MultiVector(object):
     def odd(self):
         """Extract the odd-grade blades."""
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             if bit_count(bits) % 2:
                 new_data[bits] = coeff
 
@@ -1018,7 +1015,7 @@ class MultiVector(object):
     def even(self):
         """Extract the even-grade blades."""
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             if bit_count(bits) % 2 == 0:
                 new_data[bits] = coeff
 
@@ -1040,7 +1037,7 @@ class MultiVector(object):
 
     def as_scalar(self):
         result = 0
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             if bits != 0:
                 raise ValueError("multivector is not a scalar")
             result = coeff
@@ -1059,9 +1056,9 @@ class MultiVector(object):
         else:
             result = [0] * self.space.dimensions
 
-        log_table = dict((2**i, i) for i in range(self.space.dimensions))
+        log_table = {2**i: i for i in range(self.space.dimensions)}
         try:
-            for bits, coeff in six.iteritems(self.data):
+            for bits, coeff in self.data.items():
                 result[log_table[bits]] = coeff
         except KeyError:
             raise ValueError("multivector is not a purely grade-1")
@@ -1081,7 +1078,7 @@ class MultiVector(object):
         new coefficient.
         """
         new_data = {}
-        for bits, coeff in six.iteritems(self.data):
+        for bits, coeff in self.data.items():
             new_data[bits] = f(coeff)
 
         return MultiVector(new_data, self.space)
diff --git a/pymbolic/geometric_algebra/mapper.py b/pymbolic/geometric_algebra/mapper.py
index f35a2f5..3eb3f32 100644
--- a/pymbolic/geometric_algebra/mapper.py
+++ b/pymbolic/geometric_algebra/mapper.py
@@ -1,6 +1,3 @@
-# * encoding: utf-8 *
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2014 Andreas Kloeckner"
 
 __license__ = """
@@ -26,7 +23,6 @@ THE SOFTWARE.
 # This is experimental, undocumented, and could go away any second.
 # Consider yourself warned.
 
-from six.moves import range, zip
 
 from pymbolic.geometric_algebra import MultiVector
 import pymbolic.geometric_algebra.primitives as prim
@@ -104,30 +100,22 @@ class StringifyMapper(StringifyMapperBase):
 
     def map_nabla(self, expr, enclosing_prec):
         import sys
-        if sys.version_info >= (3,):
-            return u"∇[%s]" % expr.nabla_id
-        else:
-            return r"\/[%s]" % expr.nabla_id
+        return "∇[%s]" % expr.nabla_id
 
     def map_nabla_component(self, expr, enclosing_prec):
         import sys
-        if sys.version_info >= (3,):
-            return u"∇%s[%s]" % (
-                    self.AXES.get(expr.ambient_axis, expr.ambient_axis),
-                    expr.nabla_id)
-        else:
-            return r"\/%s[%s]" % (
-                    self.AXES.get(expr.ambient_axis, expr.ambient_axis),
-                    expr.nabla_id)
+        return "∇{}[{}]".format(
+                self.AXES.get(expr.ambient_axis, expr.ambient_axis),
+                expr.nabla_id)
 
     def map_derivative_source(self, expr, enclosing_prec):
-        return r"D[%s](%s)" % (expr.nabla_id, self.rec(expr.operand, PREC_NONE))
+        return r"D[{}]({})".format(expr.nabla_id, self.rec(expr.operand, PREC_NONE))
 
 
 class GraphvizMapper(GraphvizMapperBase):
     def map_derivative_source(self, expr):
         self.lines.append(
-                '%s [label="D[%s]\",shape=ellipse];' % (
+                '{} [label="D[{}]\",shape=ellipse];'.format(
                     self.get_id(expr), expr.nabla_id))
         if not self.visit(expr, node_printed=True):
             return
@@ -169,7 +157,7 @@ class Dimensionalizer(EvaluationMapper):
             return rec_op.map(
                     lambda coeff: DerivativeSource(coeff, expr.nabla_id))
         else:
-            return super(Dimensionalizer, self).map_derivative_source(expr)
+            return super().map_derivative_source(expr)
 
 # }}}
 
@@ -182,10 +170,10 @@ class DerivativeSourceAndNablaComponentCollector(CachingMapperMixin, Collector):
                 "Dimensionalizer--Nabla found, not allowed")
 
     def map_nabla_component(self, expr):
-        return set([expr])
+        return {expr}
 
     def map_derivative_source(self, expr):
-        return set([expr]) | self.rec(expr.operand)
+        return {expr} | self.rec(expr.operand)
 
 
 class NablaComponentToUnitVector(EvaluationMapper):
diff --git a/pymbolic/geometric_algebra/primitives.py b/pymbolic/geometric_algebra/primitives.py
index 8d7948d..7e4e73c 100644
--- a/pymbolic/geometric_algebra/primitives.py
+++ b/pymbolic/geometric_algebra/primitives.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2014 Andreas Kloeckner"
 
 __license__ = """
@@ -78,7 +76,7 @@ class DerivativeSource(_GeometricCalculusExpression):
     mapper_method = "map_derivative_source"
 
 
-class Derivative(object):
+class Derivative:
     """This mechanism cannot be used to take more than one derivative at a time.
 
     .. autoproperty:: nabla
diff --git a/pymbolic/imperative/statement.py b/pymbolic/imperative/statement.py
index 5b06e62..6ef4a6a 100644
--- a/pymbolic/imperative/statement.py
+++ b/pymbolic/imperative/statement.py
@@ -52,7 +52,7 @@ class Statement(RecordWithoutPickling):
             id = six.moves.intern(id)
 
         depends_on = frozenset(kwargs.pop("depends_on", []))
-        super(Statement, self).__init__(
+        super().__init__(
                                        id=id,
                                        depends_on=depends_on,
                                        **kwargs)
@@ -99,7 +99,7 @@ class ConditionalStatement(Statement):
 
     def __init__(self, **kwargs):
         condition = kwargs.pop("condition", True)
-        super(ConditionalStatement, self).__init__(
+        super().__init__(
                 condition=condition,
                 **kwargs)
 
@@ -109,13 +109,13 @@ class ConditionalStatement(Statement):
         return " if " + str(self.condition)
 
     def __str__(self):
-        return (super(ConditionalStatement, self).__str__()
+        return (super().__str__()
                 + self._condition_printing_suffix())
 
     def get_read_variables(self):
         dep_mapper = self.get_dependency_mapper()
         return (
-                super(ConditionalStatement, self).get_read_variables()
+                super().get_read_variables()
                 | frozenset(
                     dep.name for dep in dep_mapper(self.condition)))
 
@@ -131,7 +131,7 @@ class Assignment(Statement):
     """
 
     def __init__(self, lhs, rhs, **kwargs):
-        super(Assignment, self).__init__(
+        super().__init__(
                 lhs=lhs,
                 rhs=rhs,
                 **kwargs)
@@ -147,7 +147,7 @@ class Assignment(Statement):
             raise TypeError("unexpected type of LHS")
 
     def get_read_variables(self):
-        result = super(Assignment, self).get_read_variables()
+        result = super().get_read_variables()
         get_deps = self.get_dependency_mapper()
 
         def get_vars(expr):
@@ -158,7 +158,7 @@ class Assignment(Statement):
         return result
 
     def map_expressions(self, mapper, include_lhs=True):
-        return (super(Assignment, self)
+        return (super()
                 .map_expressions(mapper, include_lhs=include_lhs)
                 .copy(
                     lhs=mapper(self.lhs) if include_lhs else self.lhs,
@@ -178,7 +178,7 @@ class Assignment(Statement):
 
 class ConditionalAssignment(ConditionalStatement, Assignment):
     def map_expressions(self, mapper, include_lhs=True):
-        return (super(ConditionalAssignment, self)
+        return (super()
                 .map_expressions(mapper, include_lhs=include_lhs)
                 .copy(condition=mapper(self.condition)))
 
diff --git a/pymbolic/imperative/transform.py b/pymbolic/imperative/transform.py
index 6f4c08d..d21fe5b 100644
--- a/pymbolic/imperative/transform.py
+++ b/pymbolic/imperative/transform.py
@@ -29,7 +29,7 @@ def fuse_statement_streams_with_unique_ids(statements_a, statements_b):
     new_statements = list(statements_a)
     from pytools import UniqueNameGenerator
     stmt_id_gen = UniqueNameGenerator(
-            set([stmta.id for stmta in new_statements]))
+            {stmta.id for stmta in new_statements})
 
     b_unique_statements = []
     old_b_id_to_new_b_id = {}
diff --git a/pymbolic/imperative/utils.py b/pymbolic/imperative/utils.py
index 633f010..18cbfef 100644
--- a/pymbolic/imperative/utils.py
+++ b/pymbolic/imperative/utils.py
@@ -1,5 +1,3 @@
-from __future__ import division, with_statement
-
 __copyright__ = """
 Copyright (C) 2013 Andreas Kloeckner
 Copyright (C) 2014 Matt Wala
@@ -80,7 +78,7 @@ def get_dot_dependency_graph(
             stmt_label = statement_stringifier(stmt)
             tooltip = stmt.id
 
-        return 'label="%s",shape="box",tooltip="%s"' % (stmt_label, tooltip)
+        return f'label="{stmt_label}",shape="box",tooltip="{tooltip}"'
 
     lines = list(preamble_hook())
     lines.append("rankdir=BT;")
@@ -90,7 +88,7 @@ def get_dot_dependency_graph(
     annotation_dep_graph = {}
 
     for stmt in statements:
-        lines.append('"%s" [%s];' % (stmt.id, get_node_attrs(stmt)))
+        lines.append('"{}" [{}];'.format(stmt.id, get_node_attrs(stmt)))
         for dep in stmt.depends_on:
             dep_graph.setdefault(stmt.id, set()).add(dep)
 
@@ -126,9 +124,9 @@ def get_dot_dependency_graph(
 
     for stmt_1 in dep_graph:
         for stmt_2 in dep_graph.get(stmt_1, set()):
-            lines.append("%s -> %s" % (stmt_1, stmt_2))
+            lines.append(f"{stmt_1} -> {stmt_2}")
 
-    for (stmt_1, stmt_2), annot in six.iteritems(annotation_dep_graph):
+    for (stmt_1, stmt_2), annot in annotation_dep_graph.items():
         lines.append(
                 '%s -> %s  [label="%s",style="dashed"]'
                 % (stmt_2, stmt_1, annot))
diff --git a/pymbolic/interop/ast.py b/pymbolic/interop/ast.py
index 0eb0ceb..e8c4b45 100644
--- a/pymbolic/interop/ast.py
+++ b/pymbolic/interop/ast.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2015 Andreas Kloeckner"
 
 __license__ = """
@@ -61,7 +59,7 @@ An example::
 '''
 
 
-class ASTMapper(object):
+class ASTMapper:
     def __call__(self, expr, *args, **kwargs):
         return self.rec(expr, *args, **kwargs)
 
@@ -193,9 +191,9 @@ class ASTToPymbolic(ASTMapper):
         args = tuple(self.rec(arg) for arg in expr.args)
         if expr.keywords:
             return p.CallWithKwargs(func, args,
-                    dict(
-                        (kw.arg, self.rec(kw.value))
-                        for kw in expr.keywords))
+                    {
+                        kw.arg: self.rec(kw.value)
+                        for kw in expr.keywords})
         else:
             return p.Call(func, args)
 
diff --git a/pymbolic/interop/common.py b/pymbolic/interop/common.py
index 568a364..aa77c9f 100644
--- a/pymbolic/interop/common.py
+++ b/pymbolic/interop/common.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -27,7 +25,7 @@ from pymbolic.mapper.evaluator import EvaluationMapper
 from functools import partial
 
 
-class SympyLikeMapperBase(object):
+class SympyLikeMapperBase:
 
     def __call__(self, expr, *args, **kwargs):
         return self.rec(expr, *args, **kwargs)
diff --git a/pymbolic/interop/maxima.py b/pymbolic/interop/maxima.py
index 8d45e53..f5e892a 100644
--- a/pymbolic/interop/maxima.py
+++ b/pymbolic/interop/maxima.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -73,7 +71,7 @@ class MaximaStringifyMapper(StringifyMapper):
     def map_constant(self, expr, enclosing_prec):
         from pymbolic.mapper.stringifier import PREC_SUM
         if isinstance(expr, complex):
-            result = "%r + %r*%%i" % (expr.real, expr.imag)
+            result = f"{expr.real!r} + {expr.imag!r}*%i"
         else:
             result = repr(expr)
 
@@ -113,7 +111,7 @@ class MaximaParser(ParserBase):
             if isinstance(left_exp, tuple):
                 left_exp = FinalizedTuple(left_exp)
         else:
-            left_exp = super(MaximaParser, self).parse_prefix(pstate)
+            left_exp = super().parse_prefix(pstate)
 
         return left_exp
 
@@ -133,11 +131,7 @@ class MaximaParser(ParserBase):
             pstate.advance()
             return np.e
         elif next_tag is p._identifier:
-            if six.PY3:
-                return primitives.Variable(pstate.next_str_and_advance())
-            else:
-                # Py2 does not have Unicode identifiers
-                return primitives.Variable(str(pstate.next_str_and_advance()))
+            return primitives.Variable(pstate.next_str_and_advance())
         else:
             pstate.expected("terminal")
 
@@ -253,7 +247,7 @@ def _strify_assignments_and_expr(assignments, expr):
             return assignment
         if isinstance(assignment, tuple):
             name, value = assignment
-            return"%s: %s" % (name, strify(value))
+            return"{}: {}".format(name, strify(value))
         else:
             return strify(assignment)
 
@@ -295,7 +289,7 @@ class MaximaKernel:
         # {{{ check for maxima command
 
         self.child.sendline(
-            'hash \"{command}\"; echo $?'.format(command=self.executable))
+            f'hash \"{self.executable}\"; echo $?')
 
         hash_output = self.child.expect(["0\r\n", "1\r\n"])
         if hash_output != 0:
@@ -323,7 +317,7 @@ class MaximaKernel:
         if which == 0:
             if enforce_prompt_numbering:
                 assert int(self.child.match.group(1)) == self.current_prompt, (
-                        "found prompt: %s, expected: %s" % (
+                        "found prompt: {}, expected: {}".format(
                             self.child.match.group(1).decode(),
                             self.current_prompt))
             else:
diff --git a/pymbolic/interop/symengine.py b/pymbolic/interop/symengine.py
index a984400..11a819d 100644
--- a/pymbolic/interop/symengine.py
+++ b/pymbolic/interop/symengine.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = """
 Copyright (C) 2017 Matt Wala
 """
diff --git a/pymbolic/interop/sympy.py b/pymbolic/interop/sympy.py
index 393894e..189c9dd 100644
--- a/pymbolic/interop/sympy.py
+++ b/pymbolic/interop/sympy.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = """
 Copyright (C) 2017 Matt Wala
 Copyright (C) 2009-2013 Andreas Kloeckner
diff --git a/pymbolic/mapper/__init__.py b/pymbolic/mapper/__init__.py
index 1105a97..ba992e7 100644
--- a/pymbolic/mapper/__init__.py
+++ b/pymbolic/mapper/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import six
 from functools import reduce
 
@@ -94,7 +92,7 @@ class UnsupportedExpressionError(ValueError):
 
 # {{{ mapper base
 
-class Mapper(object):
+class Mapper:
     """A visitor for trees of :class:`pymbolic.primitives.Expression`
     subclasses. Each expression-derived object is dispatched to the
     method named by the :attr:`pymbolic.primitives.Expression.mapper_method`
@@ -108,7 +106,7 @@ class Mapper(object):
         """
 
         raise UnsupportedExpressionError(
-                "%s cannot handle expressions of type %s" % (
+                "{} cannot handle expressions of type {}".format(
                     type(self), type(expr)))
 
     def __call__(self, expr, *args, **kwargs):
@@ -184,7 +182,7 @@ class Mapper(object):
             return self.map_numpy_array(expr, *args, **kwargs)
         else:
             raise ValueError(
-                    "%s encountered invalid foreign object: %s" % (
+                    "{} encountered invalid foreign object: {}".format(
                         self.__class__, repr(expr)))
 
 # }}}
@@ -304,7 +302,7 @@ class CombineMapper(RecursiveMapper):
     def map_multivector(self, expr, *args, **kwargs):
         return self.combine(
                 self.rec(coeff)
-                for bits, coeff in six.iteritems(expr.data))
+                for bits, coeff in expr.data.items())
 
     def map_common_subexpression(self, expr, *args, **kwargs):
         return self.rec(expr.child, *args, **kwargs)
@@ -380,9 +378,9 @@ class IdentityMapper(Mapper):
                 self.rec(expr.function, *args, **kwargs),
                 tuple(self.rec(child, *args, **kwargs)
                     for child in expr.parameters),
-                dict(
-                    (key, self.rec(val, *args, **kwargs))
-                    for key, val in six.iteritems(expr.kw_parameters))
+                {
+                    key: self.rec(val, *args, **kwargs)
+                    for key, val in expr.kw_parameters.items()}
                     )
 
     def map_subscript(self, expr, *args, **kwargs):
@@ -657,7 +655,7 @@ class WalkMapper(RecursiveMapper):
         if not self.visit(expr, *args, **kwargs):
             return
 
-        for bits, coeff in six.iteritems(expr.data):
+        for bits, coeff in expr.data.items():
             self.rec(coeff)
 
         self.post_visit(expr, *args, **kwargs)
@@ -815,9 +813,9 @@ class CallbackMapper(RecursiveMapper):
 
 # {{{ caching mixins
 
-class CachingMapperMixin(object):
+class CachingMapperMixin:
     def __init__(self):
-        super(CachingMapperMixin, self).__init__()
+        super().__init__()
         self.result_cache = {}
 
     def rec(self, expr):
@@ -825,16 +823,16 @@ class CachingMapperMixin(object):
             return self.result_cache[expr]
         except TypeError:
             # not hashable, oh well
-            return super(CachingMapperMixin, self).rec(expr)
+            return super().rec(expr)
         except KeyError:
-            result = super(CachingMapperMixin, self).rec(expr)
+            result = super().rec(expr)
             self.result_cache[expr] = result
             return result
 
     __call__ = rec
 
 
-class CSECachingMapperMixin(object):
+class CSECachingMapperMixin:
     """A :term:`mix-in` that helps
     subclassed mappers implement caching for
     :class:`pymbolic.primitives.CommonSubexpression`
diff --git a/pymbolic/mapper/c_code.py b/pymbolic/mapper/c_code.py
index a45c34b..1bef35d 100644
--- a/pymbolic/mapper/c_code.py
+++ b/pymbolic/mapper/c_code.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -69,8 +67,8 @@ class CCodeMapper(SimplifyingSortingStringifyMapper):
         super().__init__(reverse)
         self.cse_prefix = cse_prefix
 
-        self.cse_to_name = dict((cse, name) for name, cse in cse_name_list)
-        self.cse_names = set(cse for name, cse in cse_name_list)
+        self.cse_to_name = {cse: name for name, cse in cse_name_list}
+        self.cse_names = {cse for name, cse in cse_name_list}
         self.cse_name_list = cse_name_list[:]
 
         self.complex_constant_base_type = complex_constant_base_type
@@ -98,7 +96,7 @@ class CCodeMapper(SimplifyingSortingStringifyMapper):
 
     def map_constant(self, x, enclosing_prec):
         if isinstance(x, complex):
-            return "std::complex<%s>(%s, %s)" % (
+            return "std::complex<{}>({}, {})".format(
                     self.complex_constant_base_type,
                     self.map_constant(x.real, PREC_NONE),
                     self.map_constant(x.imag, PREC_NONE))
diff --git a/pymbolic/mapper/coefficient.py b/pymbolic/mapper/coefficient.py
index 647a2f7..32c9f38 100644
--- a/pymbolic/mapper/coefficient.py
+++ b/pymbolic/mapper/coefficient.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import six
 
 __copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
@@ -37,7 +35,7 @@ class CoefficientCollector(Mapper):
 
         result = {}
         for stride_dict in stride_dicts:
-            for var, stride in six.iteritems(stride_dict):
+            for var, stride in stride_dict.items():
                 if var in result:
                     result[var] += stride
                 else:
@@ -69,10 +67,10 @@ class CoefficientCollector(Mapper):
         if idx_of_child_with_vars is None:
             return {1: other_coeffs}
         else:
-            return dict(
-                    (var, other_coeffs*coeff)
+            return {
+                    var: other_coeffs*coeff
                     for var, coeff in
-                    six.iteritems(children_coeffs[idx_of_child_with_vars]))
+                    children_coeffs[idx_of_child_with_vars].items()}
 
         return result
 
diff --git a/pymbolic/mapper/collector.py b/pymbolic/mapper/collector.py
index 81d4eb6..f73c537 100644
--- a/pymbolic/mapper/collector.py
+++ b/pymbolic/mapper/collector.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import six
 
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
@@ -87,7 +85,7 @@ class TermCollector(IdentityMapper):
 
         coefficients = []
         cleaned_base2exp = {}
-        for base, exp in six.iteritems(base2exp):
+        for base, exp in base2exp.items():
             term = base**exp
             if self.get_dependencies(term) <= self.parameters:
                 coefficients.append(term)
@@ -95,7 +93,7 @@ class TermCollector(IdentityMapper):
                 cleaned_base2exp[base] = exp
 
         term = frozenset(
-                (base, exp) for base, exp in six.iteritems(cleaned_base2exp))
+                (base, exp) for base, exp in cleaned_base2exp.items())
         return term, self.rec(pymbolic.flattened_product(coefficients))
 
     def map_sum(self, mysum):
@@ -108,5 +106,5 @@ class TermCollector(IdentityMapper):
             return pymbolic.flattened_product(base**exp for base, exp in rep)
 
         result = pymbolic.flattened_sum(coeff*rep2term(termrep)
-                for termrep, coeff in six.iteritems(term2coeff))
+                for termrep, coeff in term2coeff.items())
         return result
diff --git a/pymbolic/mapper/constant_converter.py b/pymbolic/mapper/constant_converter.py
index 5a8f645..2d25a76 100644
--- a/pymbolic/mapper/constant_converter.py
+++ b/pymbolic/mapper/constant_converter.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2017 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/constant_folder.py b/pymbolic/mapper/constant_folder.py
index f40e4ad..5799efa 100644
--- a/pymbolic/mapper/constant_folder.py
+++ b/pymbolic/mapper/constant_folder.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -28,7 +26,7 @@ from pymbolic.mapper import \
         CSECachingMapperMixin
 
 
-class ConstantFoldingMapperBase(object):
+class ConstantFoldingMapperBase:
     def is_constant(self, expr):
         from pymbolic.mapper.dependency import DependencyMapper
         return not bool(DependencyMapper()(expr))
diff --git a/pymbolic/mapper/cse_tagger.py b/pymbolic/mapper/cse_tagger.py
index ffb8e53..f6b4c45 100644
--- a/pymbolic/mapper/cse_tagger.py
+++ b/pymbolic/mapper/cse_tagger.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/dependency.py b/pymbolic/mapper/dependency.py
index 1a041b1..810472c 100644
--- a/pymbolic/mapper/dependency.py
+++ b/pymbolic/mapper/dependency.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -62,16 +60,16 @@ class DependencyMapper(CSECachingMapperMixin, Collector):
         self.include_cses = include_cses
 
     def map_variable(self, expr):
-        return set([expr])
+        return {expr}
 
     def map_call(self, expr):
         if self.include_calls == "descend_args":
             return self.combine(
                     [self.rec(child) for child in expr.parameters])
         elif self.include_calls:
-            return set([expr])
+            return {expr}
         else:
-            return super(DependencyMapper, self).map_call(expr)
+            return super().map_call(expr)
 
     def map_call_with_kwargs(self, expr):
         if self.include_calls == "descend_args":
@@ -80,25 +78,25 @@ class DependencyMapper(CSECachingMapperMixin, Collector):
                     + [self.rec(val) for name, val in expr.kw_parameters.items()]
                     )
         elif self.include_calls:
-            return set([expr])
+            return {expr}
         else:
-            return super(DependencyMapper, self).map_call_with_kwargs(expr)
+            return super().map_call_with_kwargs(expr)
 
     def map_lookup(self, expr):
         if self.include_lookups:
-            return set([expr])
+            return {expr}
         else:
-            return super(DependencyMapper, self).map_lookup(expr)
+            return super().map_lookup(expr)
 
     def map_subscript(self, expr):
         if self.include_subscripts:
-            return set([expr])
+            return {expr}
         else:
-            return super(DependencyMapper, self).map_subscript(expr)
+            return super().map_subscript(expr)
 
     def map_common_subexpression_uncached(self, expr):
         if self.include_cses:
-            return set([expr])
+            return {expr}
         else:
             return Collector.map_common_subexpression(self, expr)
 
diff --git a/pymbolic/mapper/differentiator.py b/pymbolic/mapper/differentiator.py
index 2fa8e2e..1044455 100644
--- a/pymbolic/mapper/differentiator.py
+++ b/pymbolic/mapper/differentiator.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/distributor.py b/pymbolic/mapper/distributor.py
index 7b6efa3..77edd75 100644
--- a/pymbolic/mapper/distributor.py
+++ b/pymbolic/mapper/distributor.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/evaluator.py b/pymbolic/mapper/evaluator.py
index 4189b3c..6b54ea7 100644
--- a/pymbolic/mapper/evaluator.py
+++ b/pymbolic/mapper/evaluator.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -70,9 +68,9 @@ class EvaluationMapper(RecursiveMapper, CSECachingMapperMixin):
 
     def map_call_with_kwargs(self, expr):
         args = [self.rec(par) for par in expr.parameters]
-        kwargs = dict(
-                (k, self.rec(v))
-                for k, v in expr.kw_parameters.items())
+        kwargs = {
+                k: self.rec(v)
+                for k, v in expr.kw_parameters.items()}
 
         return self.rec(expr.function)(*args, **kwargs)
 
diff --git a/pymbolic/mapper/flattener.py b/pymbolic/mapper/flattener.py
index f301321..b7e114b 100644
--- a/pymbolic/mapper/flattener.py
+++ b/pymbolic/mapper/flattener.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/flop_counter.py b/pymbolic/mapper/flop_counter.py
index fc8c92f..8dfbd45 100644
--- a/pymbolic/mapper/flop_counter.py
+++ b/pymbolic/mapper/flop_counter.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/graphviz.py b/pymbolic/mapper/graphviz.py
index 14b0d42..147d45b 100644
--- a/pymbolic/mapper/graphviz.py
+++ b/pymbolic/mapper/graphviz.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2015 Andreas Kloeckner"
 
 __license__ = """
@@ -55,7 +53,7 @@ class GraphvizMapper(WalkMapper):
 
     def map_leaf(self, expr):
         self.lines.append(
-                '%s [label="%s", shape=box];' % (
+                '{} [label="{}", shape=box];'.format(
                     self.get_id(expr), str(expr).replace("\\", "\\\\")))
 
         if self.visit(expr, node_printed=True):
@@ -72,7 +70,7 @@ class GraphvizMapper(WalkMapper):
             node_id = self.get_id(expr)
 
         if self.parent_stack:
-            self.lines.append("%s -> %s;" % (
+            self.lines.append("{} -> {};".format(
                 self.get_id(self.parent_stack[-1]),
                 node_id))
 
@@ -84,7 +82,7 @@ class GraphvizMapper(WalkMapper):
 
         if not node_printed:
             self.lines.append(
-                    '%s [label="%s"];' % (
+                    '{} [label="{}"];'.format(
                         self.get_id(expr),
                         type(expr).__name__))
 
@@ -123,7 +121,7 @@ class GraphvizMapper(WalkMapper):
         node_id = self.generate_unique_id()
 
         self.lines.append(
-                '%s [label="%s",shape=box];' % (
+                '{} [label="{}",shape=box];'.format(
                     node_id,
                     expr.name))
         if not self.visit(expr, node_printed=True, node_id=node_id):
@@ -133,7 +131,7 @@ class GraphvizMapper(WalkMapper):
 
     def map_lookup(self, expr):
         self.lines.append(
-                '%s [label="Lookup[%s]",shape=box];' % (
+                '{} [label="Lookup[{}]",shape=box];'.format(
                     self.get_id(expr), expr.name))
         if not self.visit(expr, node_printed=True):
             return
@@ -149,7 +147,7 @@ class GraphvizMapper(WalkMapper):
         node_id = self.generate_unique_id()
 
         self.lines.append(
-                '%s [label="%s",shape=ellipse];' % (
+                '{} [label="{}",shape=ellipse];'.format(
                     node_id,
                     str(expr)))
         if not self.visit(expr, node_printed=True, node_id=node_id):
@@ -160,10 +158,10 @@ class GraphvizMapper(WalkMapper):
     def map_call(self, expr):
         from pymbolic.primitives import Variable
         if not isinstance(expr.function, Variable):
-            return super(GraphvizMapper, self).map_call(expr)
+            return super().map_call(expr)
 
         self.lines.append(
-                '%s [label="Call[%s]",shape=box];' % (
+                '{} [label="Call[{}]",shape=box];'.format(
                     self.get_id(expr), str(expr.function)))
         if not self.visit(expr, node_printed=True):
             return
diff --git a/pymbolic/mapper/persistent_hash.py b/pymbolic/mapper/persistent_hash.py
index dbbac23..6b8ffa2 100644
--- a/pymbolic/mapper/persistent_hash.py
+++ b/pymbolic/mapper/persistent_hash.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/stringifier.py b/pymbolic/mapper/stringifier.py
index 821b940..497adcd 100644
--- a/pymbolic/mapper/stringifier.py
+++ b/pymbolic/mapper/stringifier.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -158,7 +156,7 @@ class StringifyMapper(pymbolic.mapper.Mapper):
                 tuple(self.rec(ch, PREC_NONE, *args, **kwargs)
                       for ch in expr.parameters)
                 +  # noqa: W504
-                tuple("%s=%s" % (name, self.rec(ch, PREC_NONE, *args, **kwargs))
+                tuple("{}={}".format(name, self.rec(ch, PREC_NONE, *args, **kwargs))
                     for name, ch in expr.kw_parameters.items()))
         return self.format("%s(%s)",
                 self.rec(expr.function, PREC_CALL, *args, **kwargs),
@@ -338,7 +336,7 @@ class StringifyMapper(pymbolic.mapper.Mapper):
         if len(expr.shape) == 1 and max_length < 15:
             return "array(%s)" % ", ".join(str_array)
         else:
-            lines = ["  %s: %s\n" % (
+            lines = ["  {}: {}\n".format(
                 ",".join(str(i_i) for i_i in i), val)
                 for i, val in numpy.ndenumerate(str_array)]
             if max_length > 70:
@@ -362,7 +360,7 @@ class StringifyMapper(pymbolic.mapper.Mapper):
 
     def map_if(self, expr, enclosing_prec, *args, **kwargs):
         return self.parenthesize_if_needed(
-                "%s if %s else %s" % (
+                "{} if {} else {}".format(
                     self.rec(expr.then, PREC_LOGICAL_OR, *args, **kwargs),
                     self.rec(expr.condition, PREC_LOGICAL_OR, *args, **kwargs),
                     self.rec(expr.else_, PREC_LOGICAL_OR, *args, **kwargs)),
@@ -370,7 +368,7 @@ class StringifyMapper(pymbolic.mapper.Mapper):
 
     def map_if_positive(self, expr, enclosing_prec, *args, **kwargs):
         return self.parenthesize_if_needed(
-                "%s if %s > 0 else %s" % (
+                "{} if {} > 0 else {}".format(
                     self.rec(expr.then, PREC_LOGICAL_OR, *args, **kwargs),
                     self.rec(expr.criterion, PREC_LOGICAL_OR, *args, **kwargs),
                     self.rec(expr.else_, PREC_LOGICAL_OR, *args, **kwargs)),
@@ -388,15 +386,15 @@ class StringifyMapper(pymbolic.mapper.Mapper):
                 "d/d%s" % v
                 for v in expr.variables)
 
-        return "%s %s" % (
+        return "{} {}".format(
                 derivs, self.rec(expr.child, PREC_PRODUCT, *args, **kwargs))
 
     def map_substitution(self, expr, enclosing_prec, *args, **kwargs):
         substs = ", ".join(
-                "%s=%s" % (name, self.rec(val, PREC_NONE, *args, **kwargs))
+                "{}={}".format(name, self.rec(val, PREC_NONE, *args, **kwargs))
                 for name, val in zip(expr.variables, expr.values))
 
-        return "[%s]{%s}" % (
+        return "[{}]{{{}}}".format(
                 self.rec(expr.child, PREC_NONE, *args, **kwargs),
                 substs)
 
@@ -427,7 +425,7 @@ class StringifyMapper(pymbolic.mapper.Mapper):
 
 # {{{ cse-splitting stringifier
 
-class CSESplittingStringifyMapperMixin(object):
+class CSESplittingStringifyMapperMixin:
     """A :term:`mix-in` for subclasses of
     :class:`StringifyMapper` that collects
     "variable assignments" for
@@ -502,7 +500,7 @@ class CSESplittingStringifyMapperMixin(object):
         return cse_name
 
     def get_cse_strings(self):
-        return ["%s : %s" % (cse_name, cse_str)
+        return [f"{cse_name} : {cse_str}"
                 for cse_name, cse_str in
                     sorted(getattr(self, "cse_name_list", []))]
 
@@ -705,7 +703,7 @@ class LaTeXMapper(StringifyMapper):
 
     def map_substitution(self, expr, enclosing_prec, *args, **kwargs):
         substs = ", ".join(
-                "%s=%s" % (name, self.rec(val, PREC_NONE, *args, **kwargs))
+                "{}={}".format(name, self.rec(val, PREC_NONE, *args, **kwargs))
                 for name, val in zip(expr.variables, expr.values))
 
         return self.format(r"[%s]\{%s\}",
diff --git a/pymbolic/mapper/substitutor.py b/pymbolic/mapper/substitutor.py
index 177a9b1..82e62f3 100644
--- a/pymbolic/mapper/substitutor.py
+++ b/pymbolic/mapper/substitutor.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/mapper/unifier.py b/pymbolic/mapper/unifier.py
index 4ac8f2c..eca76b4 100644
--- a/pymbolic/mapper/unifier.py
+++ b/pymbolic/mapper/unifier.py
@@ -1,8 +1,4 @@
-from __future__ import division
-from __future__ import absolute_import
 import six
-from six.moves import range
-from six.moves import zip
 
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
@@ -32,7 +28,7 @@ from pymbolic.primitives import Variable
 
 def unify_map(map1, map2):
     result = map1.copy()
-    for name, value in six.iteritems(map2):
+    for name, value in map2.items():
         if name in map1:
             if map1[name] != value:
                 return None
@@ -42,7 +38,7 @@ def unify_map(map1, map2):
     return result
 
 
-class UnificationRecord(object):
+class UnificationRecord:
 
     def __init__(self, equations, lmap=None, rmap=None):
         self.equations = equations
@@ -81,7 +77,7 @@ class UnificationRecord(object):
 
     def __repr__(self):
         return "UnificationRecord(%s)" % (
-                ", ".join("%s = %s" % (str(lhs), str(rhs))
+                ", ".join("{} = {}".format(str(lhs), str(rhs))
                 for lhs, rhs in self.equations))
 
 
@@ -361,8 +357,7 @@ class UnidirectionalUnifier(UnifierBase):
         # Combine the unification candidates of children in all possible ways.
         def match_children(urec, next_cand_idx, other_leftovers):
             if next_cand_idx >= len(non_var_children):
-                for match in match_plain_var_candidates(urec, other_leftovers):
-                    yield match
+                yield from match_plain_var_candidates(urec, other_leftovers)
                 return
 
             for other_idx, pair_urecs in unification_candidates[next_cand_idx]:
@@ -371,12 +366,11 @@ class UnidirectionalUnifier(UnifierBase):
                     continue
 
                 new_urecs = unify_many(pair_urecs, urec)
-                new_rhs_leftovers = other_leftovers - set([other_idx])
+                new_rhs_leftovers = other_leftovers - {other_idx}
 
                 for cand_urec in new_urecs:
-                    for result_urec in match_children(
-                            cand_urec, next_cand_idx + 1, new_rhs_leftovers):
-                        yield result_urec
+                    yield from match_children(
+                            cand_urec, next_cand_idx + 1, new_rhs_leftovers)
 
         def match_plain_var_candidates(urec, other_leftovers):
             if len(plain_var_candidates) == len(other_leftovers) == 0:
@@ -391,8 +385,7 @@ class UnidirectionalUnifier(UnifierBase):
             def subsets(s, max_size):
                 from itertools import combinations
                 for size in range(1, max_size + 1):
-                    for subset in combinations(s, size):
-                        yield subset
+                    yield from combinations(s, size)
 
             def partitions(s, k):
                 if k == 1:
@@ -417,8 +410,7 @@ class UnidirectionalUnifier(UnifierBase):
                         yield result
                         return
                     # urecs was not merged in, do it here.
-                    for unif in unify_many(urecs, result):
-                        yield unif
+                    yield from unify_many(urecs, result)
 
         for urec in match_children(
                 UnificationRecord([]), 0, set(range(len(other.children)))):
diff --git a/pymbolic/parser.py b/pymbolic/parser.py
index 26cabd4..ea9f580 100644
--- a/pymbolic/parser.py
+++ b/pymbolic/parser.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -95,7 +93,7 @@ def _join_to_slice(left, right):
         return Slice((left, right))
 
 
-class FinalizedContainer(object):
+class FinalizedContainer:
     """A base class for containers  that may not have elements appended to it,
     because they were terminated by a closing delimiter.
     """
@@ -109,7 +107,7 @@ class FinalizedList(list, FinalizedContainer):
     pass
 
 
-class Parser(object):
+class Parser:
     lex_table = [
             (_equal, pytools.lex.RE(r"==")),
             (_notequal, pytools.lex.RE(r"!=")),
diff --git a/pymbolic/polynomial.py b/pymbolic/polynomial.py
index a8e53b1..f4b83dd 100644
--- a/pymbolic/polynomial.py
+++ b/pymbolic/polynomial.py
@@ -1,7 +1,4 @@
-from __future__ import division
-from __future__ import absolute_import
-from __future__ import print_function
-from six.moves import range, intern
+from six.moves import intern
 
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
@@ -291,7 +288,7 @@ class Polynomial(Expression):
 
     def as_primitives(self):
         deps = _get_dependencies(self)
-        context = dict((dep, dep) for dep in deps)
+        context = {dep: dep for dep in deps}
         return pymbolic.evaluate(self, context)
 
     def get_coefficient(self, sought_exp):
diff --git a/pymbolic/primitives.py b/pymbolic/primitives.py
index 7009199..f1f3d7c 100644
--- a/pymbolic/primitives.py
+++ b/pymbolic/primitives.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -25,7 +23,7 @@ THE SOFTWARE.
 import pymbolic.traits as traits
 
 import six
-from six.moves import range, zip, intern
+from six.moves import intern
 
 __doc__ = """
 Expression base class
@@ -174,7 +172,7 @@ def disable_subscript_by_getitem():
     pass
 
 
-class Expression(object):
+class Expression:
     """Superclass for parts of a mathematical expression. Overrides operators
     to implicitly construct :class:`Sum`, :class:`Product` and other expressions.
 
@@ -435,7 +433,7 @@ class Expression(object):
     def a(self):
         """Provide a spelling ``expr.a.name`` for encoding attribute lookup.
         """
-        class AttributeLookupCreator(object):
+        class AttributeLookupCreator:
             def __init__(self, aggregate):
                 self.aggregate = aggregate
 
@@ -476,7 +474,7 @@ class Expression(object):
             if isinstance(child, tuple):
                 # Make sure limit propagates at least through tuples
 
-                return "(%s%s)" % (
+                return "({}{})".format(
                         ", ".join(strify_child(i, limit-1) for i in child),
                         "," if len(child) == 1 else "")
 
@@ -489,7 +487,7 @@ class Expression(object):
                 strify_child(i, limit-1)
                 for i in self.__getinitargs__())
 
-        return "%s(%s)" % (self.__class__.__name__, initargs_str)
+        return f"{self.__class__.__name__}({initargs_str})"
 
     def __repr__(self):
         """Provides a default :func:`repr` based on
@@ -685,7 +683,7 @@ class Variable(Leaf):
             return NotImplemented
 
     def __setstate__(self, val):
-        super(Variable, self).__setstate__(val)
+        super().__setstate__(val)
 
         self.name = intern(self.name)
 
@@ -1693,7 +1691,7 @@ def make_common_subexpression(field, prefix=None, scope=None):
     from pymbolic.geometric_algebra import MultiVector
     if isinstance(field, MultiVector):
         new_data = {}
-        for bits, coeff in six.iteritems(field.data):
+        for bits, coeff in field.data.items():
             if prefix is not None:
                 blade_str = field.space.blade_bits_to_str(bits, "")
                 component_prefix = prefix+"_"+blade_str
diff --git a/pymbolic/rational.py b/pymbolic/rational.py
index 7ab677f..90d1b30 100644
--- a/pymbolic/rational.py
+++ b/pymbolic/rational.py
@@ -1,5 +1,3 @@
-from __future__ import division, absolute_import, print_function
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/pymbolic/traits.py b/pymbolic/traits.py
index ac1d7a3..c9e7e5b 100644
--- a/pymbolic/traits.py
+++ b/pymbolic/traits.py
@@ -1,5 +1,3 @@
-from __future__ import division
-from __future__ import absolute_import
 import six
 from functools import reduce
 
@@ -42,7 +40,7 @@ def traits(x):
     except AttributeError:
         if isinstance(x, (complex, float)):
             return FieldTraits()
-        elif isinstance(x, six.integer_types):
+        elif isinstance(x, int):
             return IntegerTraits()
         else:
             raise NoTraitsError
@@ -62,7 +60,7 @@ def common_traits(*args):
     return reduce(common_traits_two, (traits(arg) for arg in args))
 
 
-class Traits(object):
+class Traits:
     pass
 
 
diff --git a/setup.py b/setup.py
index 74b31b5..bc9277e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-# -*- coding: latin-1 -*-
 
 from setuptools import setup, find_packages
 
diff --git a/test/simple.py b/test/simple.py
index 5d5e337..21caaba 100644
--- a/test/simple.py
+++ b/test/simple.py
@@ -1,5 +1,3 @@
-from __future__ import division, print_function
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/test/test_maxima.py b/test/test_maxima.py
index 441884c..7a2257d 100644
--- a/test/test_maxima.py
+++ b/test/test_maxima.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py
index 2b74738..2caad51 100644
--- a/test/test_pymbolic.py
+++ b/test/test_pymbolic.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
 
 __license__ = """
@@ -195,7 +193,7 @@ def test_fft():
     code = [ccm(tfi, PREC_NONE) for tfi in traced_fft]
 
     for cse_name, cse_str in enumerate(ccm.cse_name_list):
-        print("%s = %s" % (cse_name, cse_str))
+        print(f"{cse_name} = {cse_str}")
 
     for i, line in enumerate(code):
         print("result[%d] = %s" % (i, line))
@@ -316,8 +314,8 @@ def test_func_dep_consistency():
     f = var("f")
     x = var("x")
     dep_map = DependencyMapper(include_calls="descend_args")
-    assert dep_map(f(x)) == set([x])
-    assert dep_map(f(x=x)) == set([x])
+    assert dep_map(f(x)) == {x}
+    assert dep_map(f(x=x)) == {x}
 
 
 def test_conditions():
@@ -420,7 +418,7 @@ def test_geometric_algebra(dims):
 
         # reverse properties (Sec 2.9.5 [DFM])
         assert c.rev().rev() == c
-        assert (b ^ c).rev() .close_to((c.rev() ^ b.rev()))
+        assert (b ^ c).rev() .close_to(c.rev() ^ b.rev())
 
         # dual properties
         # (1.2.26) in [HS]
@@ -429,7 +427,7 @@ def test_geometric_algebra(dims):
 
         # involution properties (Sec 2.9.5 DFW)
         assert c.invol().invol() == c
-        assert (b ^ c).invol() .close_to((b.invol() ^ c.invol()))
+        assert (b ^ c).invol() .close_to(b.invol() ^ c.invol())
 
         # commutator properties
 
@@ -497,29 +495,29 @@ def test_unifier():
 
     recs = UnidirectionalUnifier("abc")(a+b*c, d+e*f)
     assert len(recs) == 2
-    assert match_found(recs, set([(a, d), (b, e), (c, f)]))
-    assert match_found(recs, set([(a, d), (b, f), (c, e)]))
+    assert match_found(recs, {(a, d), (b, e), (c, f)})
+    assert match_found(recs, {(a, d), (b, f), (c, e)})
 
     recs = UnidirectionalUnifier("abc")(a+b, d+e+f)
     assert len(recs) == 6
-    assert match_found(recs, set([(a, d), (b, e+f)]))
-    assert match_found(recs, set([(a, e), (b, d+f)]))
-    assert match_found(recs, set([(a, f), (b, d+e)]))
-    assert match_found(recs, set([(b, d), (a, e+f)]))
-    assert match_found(recs, set([(b, e), (a, d+f)]))
-    assert match_found(recs, set([(b, f), (a, d+e)]))
+    assert match_found(recs, {(a, d), (b, e+f)})
+    assert match_found(recs, {(a, e), (b, d+f)})
+    assert match_found(recs, {(a, f), (b, d+e)})
+    assert match_found(recs, {(b, d), (a, e+f)})
+    assert match_found(recs, {(b, e), (a, d+f)})
+    assert match_found(recs, {(b, f), (a, d+e)})
 
     vals = [var("v" + str(i)) for i in range(100)]
     recs = UnidirectionalUnifier("a")(sum(vals[1:]) + a, sum(vals))
     assert len(recs) == 1
-    assert match_found(recs, set([(a, var("v0"))]))
+    assert match_found(recs, {(a, var("v0"))})
 
     recs = UnidirectionalUnifier("abc")(a+b+c, d+e)
     assert len(recs) == 0
 
     recs = UnidirectionalUnifier("abc")(f(a+b, f(a+c)), f(b+c, f(b+d)))
     assert len(recs) == 1
-    assert match_found(recs, set([(a, b), (b, c), (c, d)]))
+    assert match_found(recs, {(a, b), (b, c), (c, d)})
 
 
 def test_long_sympy_mapping():
@@ -556,7 +554,7 @@ def test_latex_mapper():
 
     def add(expr):
         # Add an equation to the list of tests.
-        equations.append(r"\[%s\] %% from: %s" % (tm(expr), sm(expr)))
+        equations.append(r"\[{}\] % from: {}".format(tm(expr), sm(expr)))
 
     add(parse("a * b + c"))
     add(parse("f(a,b,c)"))
@@ -634,8 +632,8 @@ def test_multiplicative_stringify_preserves_association():
             if outer == inner:
                 continue
 
-            assert_parse_roundtrip("x%s(y%sz)" % (outer, inner))
-            assert_parse_roundtrip("(y%sz)%sx" % (inner, outer))
+            assert_parse_roundtrip(f"x{outer}(y{inner}z)")
+            assert_parse_roundtrip(f"(y{inner}z){outer}x")
 
     assert_parse_roundtrip("(-1)*(((-1)*x) / 5)")
 
diff --git a/test/test_sympy.py b/test/test_sympy.py
index 3cd55ac..f454c52 100644
--- a/test/test_sympy.py
+++ b/test/test_sympy.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 __copyright__ = "Copyright (C) 2017 Matt Wala"
 
 __license__ = """
-- 
GitLab