summaryrefslogtreecommitdiff
path: root/numpy/polynomial/_polybase.py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/polynomial/_polybase.py')
-rw-r--r--numpy/polynomial/_polybase.py116
1 files changed, 113 insertions, 3 deletions
diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py
index 53efbb90f..30887b670 100644
--- a/numpy/polynomial/_polybase.py
+++ b/numpy/polynomial/_polybase.py
@@ -6,6 +6,7 @@ for the various polynomial classes. It operates as a mixin, but uses the
abc module from the stdlib, hence it is only available for Python >= 2.6.
"""
+import os
import abc
import numbers
@@ -67,6 +68,37 @@ class ABCPolyBase(abc.ABC):
# Limit runaway size. T_n^m has degree n*m
maxpower = 100
+ # Unicode character mappings for improved __str__
+ _superscript_mapping = str.maketrans({
+ "0": "⁰",
+ "1": "¹",
+ "2": "²",
+ "3": "³",
+ "4": "⁴",
+ "5": "⁵",
+ "6": "⁶",
+ "7": "⁷",
+ "8": "⁸",
+ "9": "⁹"
+ })
+ _subscript_mapping = str.maketrans({
+ "0": "₀",
+ "1": "₁",
+ "2": "₂",
+ "3": "₃",
+ "4": "₄",
+ "5": "₅",
+ "6": "₆",
+ "7": "₇",
+ "8": "₈",
+ "9": "₉"
+ })
+ # Some fonts don't support full unicode character ranges necessary for
+ # the full set of superscripts and subscripts, including common/default
+ # fonts in Windows shells/terminals. Therefore, default to ascii-only
+ # printing on windows.
+ _use_unicode = not os.name == 'nt'
+
@property
@abc.abstractmethod
def domain(self):
@@ -283,10 +315,88 @@ class ABCPolyBase(abc.ABC):
name = self.__class__.__name__
return f"{name}({coef}, domain={domain}, window={window})"
+ def __format__(self, fmt_str):
+ if fmt_str == '':
+ return self.__str__()
+ if fmt_str not in ('ascii', 'unicode'):
+ raise ValueError(
+ f"Unsupported format string '{fmt_str}' passed to "
+ f"{self.__class__}.__format__. Valid options are "
+ f"'ascii' and 'unicode'"
+ )
+ if fmt_str == 'ascii':
+ return self._generate_string(self._str_term_ascii)
+ return self._generate_string(self._str_term_unicode)
+
def __str__(self):
- coef = str(self.coef)
- name = self.nickname
- return f"{name}({coef})"
+ if self._use_unicode:
+ return self._generate_string(self._str_term_unicode)
+ return self._generate_string(self._str_term_ascii)
+
+ def _generate_string(self, term_method):
+ """
+ Generate the full string representation of the polynomial, using
+ ``term_method`` to generate each polynomial term.
+ """
+ # Get configuration for line breaks
+ linewidth = np.get_printoptions().get('linewidth', 75)
+ if linewidth < 1:
+ linewidth = 1
+ out = f"{self.coef[0]}"
+ for i, coef in enumerate(self.coef[1:]):
+ out += " "
+ power = str(i + 1)
+ # Polynomial coefficient
+ # The coefficient array can be an object array with elements that
+ # will raise a TypeError with >= 0 (e.g. strings or Python
+ # complex). In this case, represent the coeficient as-is.
+ try:
+ if coef >= 0:
+ next_term = f"+ {coef}"
+ else:
+ next_term = f"- {-coef}"
+ except TypeError:
+ next_term = f"+ {coef}"
+ # Polynomial term
+ next_term += term_method(power, "x")
+ # Length of the current line with next term added
+ line_len = len(out.split('\n')[-1]) + len(next_term)
+ # If not the last term in the polynomial, it will be two
+ # characters longer due to the +/- with the next term
+ if i < len(self.coef[1:]) - 1:
+ line_len += 2
+ # Handle linebreaking
+ if line_len >= linewidth:
+ next_term = next_term.replace(" ", "\n", 1)
+ out += next_term
+ return out
+
+ @classmethod
+ def _str_term_unicode(cls, i, arg_str):
+ """
+ String representation of single polynomial term using unicode
+ characters for superscripts and subscripts.
+ """
+ if cls.basis_name is None:
+ raise NotImplementedError(
+ "Subclasses must define either a basis_name, or override "
+ "_str_term_unicode(cls, i, arg_str)"
+ )
+ return (f"·{cls.basis_name}{i.translate(cls._subscript_mapping)}"
+ f"({arg_str})")
+
+ @classmethod
+ def _str_term_ascii(cls, i, arg_str):
+ """
+ String representation of a single polynomial term using ** and _ to
+ represent superscripts and subscripts, respectively.
+ """
+ if cls.basis_name is None:
+ raise NotImplementedError(
+ "Subclasses must define either a basis_name, or override "
+ "_str_term_ascii(cls, i, arg_str)"
+ )
+ return f" {cls.basis_name}_{i}({arg_str})"
@classmethod
def _repr_latex_term(cls, i, arg_str, needs_parens):