Skip to content

Commit

Permalink
Basic Python polynomial type.
Browse files Browse the repository at this point in the history
  • Loading branch information
Attila Oláh committed May 29, 2020
1 parent 9ab5562 commit efc6bfd
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 10 deletions.
13 changes: 13 additions & 0 deletions poly/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ py_library(
deps = [":int_t"],
)

py_library(
name = "int_p",
srcs = ["int_p.py"],
deps = [":int_t"],
)

py_test(
name = "int_p_test",
srcs = ["int_p_test.py"],
python_version = "PY3",
deps = [":int_p"],
)

py_library(
name = "int_t",
srcs = ["int_t.py"],
Expand Down
59 changes: 59 additions & 0 deletions poly/int_p.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Module int_p implements a polynomial wint integer terms and coefficients."""
from typing import Iterable, List, Tuple

from poly.int_t import IntT


class IntP(List[IntT]):
"""A polynomial with int terms and coefficients.
This type implements a sparse representation, i.e. only non-zero terms are
stored.
"""

def __init__(self, *args: Tuple[int, Iterable[int]]) -> None:
"""Initialise with a list of constants and terms."""
for const, ind in args:
self.append(IntT(const, ind))

def __add__(self, other: 'IntP') -> 'IntP':
"""Add two polynomials together."""
if not isinstance(other, type(self)):
return NotImplemented
ret = self.__class__()
ret.extend(self)
ret.extend(other)
return ret._compact()

def __repr__(self) -> str:
terms: List[str] = []

for term in self:
if not term.const:
# Exclude "+ 0" terms.
continue
if term.const > 0:
terms += "+"
else:
terms += "-"
term.const *= -1
terms.append(repr(term))

return ' '.join(terms[1:])

def _compact(self):
"""Merges terms with the same indeterminates."""
self.sort(reverse=True)

ret = self.__class__()
for term in self:
size = len(ret)
if not size:
ret.append(term)
continue
if ret[size-1].ind_eq(term):
ret[size-1].const += term.const
continue
print('XXX: {} > {}'.format(ret, term))
ret.append(term)
return ret
33 changes: 33 additions & 0 deletions poly/int_p_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Tests for module int_p."""
import unittest

from poly import int_p


class TestIntP(unittest.TestCase):
"""Tests the int_p.IntP class."""

def test_add(self):
"""Tests the add method."""
self.assertEqual(
repr(int_p.IntP(
(2, (1,)),
) + int_p.IntP(
(2, (1,)),
)),
'4x'
)
self.assertEqual(
repr(int_p.IntP(
(1, (0, 1)),
(1, (1, 0)),
(2, (1, 1)),
) + int_p.IntP(
(2, (1, 1)),
)),
'4xy + x + y'
)


if __name__ == '__main__':
unittest.main()
17 changes: 14 additions & 3 deletions poly/int_t.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def __mul__(self, other: Union['Ind', int]) -> 'Ind':
def __eq__(self, other: 'Ind') -> bool:
"""Compares two sets of indeterminates for equality."""
if not isinstance(other, type(self)):
return False
raise TypeError(
"'==' not supported between instances of '{}' and '{}'"
.format(type(self), type(other)))
if len(self) != len(other):
return False
for ind_a, ind_b in zip(self, other):
Expand Down Expand Up @@ -101,9 +103,12 @@ def __mul__(self, other: Union['IntT', int]) -> 'IntT':
return self.__class__(self.const*other.const, super().__mul__(other))

def __eq__(self, other: 'IntT') -> bool:
"""Compares two terms for equality."""
if not isinstance(other, type(self)):
return False
return (self.const == other.const) and super().__eq__(other)
raise TypeError(
"'==' not supported between instances of '{}' and '{}'"
.format(type(self), type(other)))
return self.ind_eq(other) and (self.const == other.const)

def __gt__(self, other: 'IntT') -> bool:
"""Compares two terms."""
Expand All @@ -129,3 +134,9 @@ def __repr__(self) -> str:
return str(self.const)

return '{:d}{}'.format(self.const, ret)

def ind_eq(self, other: 'IntT') -> bool:
"""Compares only the indeterminates (not the constant) for equality."""
if not isinstance(other, type(self)):
return NotImplemented
return super().__eq__(other)
14 changes: 7 additions & 7 deletions poly/int_t_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ def test_repr(self):
for item, res in [
(int_t.IntT(), '0'),
(int_t.IntT(0), '0'),
(int_t.IntT(0, [1, 2, 3]), '0'),
(int_t.IntT(10, [1, 2, 3]), '10xy²z³'),
(int_t.IntT(-20, [-1, 0, 1]), '-20x¯¹z'),
(int_t.IntT(5, []), '5'),
(int_t.IntT(-8, [0, 0]), '-8'),
(int_t.IntT(1, [0, 0, 1]), 'z'),
(int_t.IntT(1, [0, 1, 1]), 'yz'),
(int_t.IntT(0, (1, 2, 3)), '0'),
(int_t.IntT(10, (1, 2, 3)), '10xy²z³'),
(int_t.IntT(-20, (-1, 0, 1)), '-20x¯¹z'),
(int_t.IntT(5, ()), '5'),
(int_t.IntT(-8, (0, 0)), '-8'),
(int_t.IntT(1, (0, 0, 1)), 'z'),
(int_t.IntT(1, (0, 1, 1)), 'yz'),
]:
self.assertEqual(repr(item), res)

Expand Down

0 comments on commit efc6bfd

Please sign in to comment.