Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Border matrix #37606

Merged
merged 6 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 119 additions & 16 deletions src/sage/matrix/matrix0.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1795,7 +1795,9 @@ cdef class Matrix(sage.structure.element.Matrix):
return self.str()

def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None,
*, unicode=False, shape=None, character_art=False):
*, unicode=False, shape=None, character_art=False,
left_border=None, right_border=None,
top_border=None, bottom_border=None):
r"""
Return a nice string representation of the matrix.

Expand Down Expand Up @@ -1845,6 +1847,16 @@ cdef class Matrix(sage.structure.element.Matrix):
:class:`~sage.typeset.unicode_art.UnicodeArt` which support line
breaking of wide matrices that exceed the window width

- ``left_border``, ``right_border`` -- sequence (default: ``None``);
if not ``None``, call :func:`str` on the elements and use the
results as labels for the rows of the matrix. The labels appear
outside of the parentheses.

- ``top_border``, ``bottom_border`` -- sequence (default: ``None``);
if not ``None``, call :func:`str` on the elements and use the
results as labels for the columns of the matrix. The labels appear
outside of the parentheses.

EXAMPLES::

sage: R = PolynomialRing(QQ,6,'z')
Expand Down Expand Up @@ -1920,6 +1932,21 @@ cdef class Matrix(sage.structure.element.Matrix):
[ 0.333333333333333 66.6666666666667]
[ -3.00000000000000 1.00000000000000e6]

Matrices with borders::

sage: M = matrix([[1,2,3], [4,5,6], [7,8,9]])
sage: M.subdivide(None, 2)
sage: print(M.str(unicode=True,
....: top_border=['ab', 'cde', 'f'],
....: bottom_border=['*', '', ''],
....: left_border=[1, 10, 100],
....: right_border=['', ' <', '']))
ab cde f
1⎛ 1 2│ 3⎞
10⎜ 4 5│ 6⎟ <
100⎝ 7 8│ 9⎠
*

TESTS:

Prior to :issue:`11544` this could take a full minute to run (2011). ::
Expand Down Expand Up @@ -1957,6 +1984,32 @@ cdef class Matrix(sage.structure.element.Matrix):
sage: matrix(R, [[2/3 - 10^6 * x^3 + 3 * y + O(x, y)^4]])
[2/3 + 3*y - 1000000*x^3 + O(x, y)^4]
sage: matrix.options._reset()

Edge cases of matrices with borders::

sage: print(matrix(ZZ, 0, 0).str(
....: top_border=[], bottom_border=[], left_border=[], right_border=[]))
[]
sage: print(matrix(ZZ, 0, 4).str(
....: unicode=True,
....: top_border='abcd', bottom_border=range(4)))
()
sage: print(matrix(ZZ, 1, 4).str(
....: unicode=True,
....: top_border='abcd', bottom_border=range(4)))
a b c d
(0 0 0 0)
0 1 2 3
sage: print(matrix(ZZ, 2, 4).str(
....: unicode=True,
....: top_border='abcd', bottom_border=range(4), left_border='uv'))
a b c d
u⎛0 0 0 0⎞
v⎝0 0 0 0⎠
0 1 2 3
sage: print(matrix(ZZ, 2, 0).str(
....: top_border='', left_border='uv', right_border=['*', '']))
[]
"""
cdef Py_ssize_t nr, nc, r, c
nr = self._nrows
Expand Down Expand Up @@ -2062,6 +2115,12 @@ cdef class Matrix(sage.structure.element.Matrix):

# compute column widths
S = []
if top_border is not None:
for x in top_border:
S.append(str(x))
top_count = 1
else:
top_count = 0
for x in entries:
# Override the usual representations with those specified
if callable(rep_mapping):
Expand All @@ -2074,39 +2133,83 @@ cdef class Matrix(sage.structure.element.Matrix):
else:
rep = repr(x)
S.append(rep)
if bottom_border is not None:
for x in bottom_border:
S.append(str(x))
bottom_count = 1
else:
bottom_count = 0

width = max(map(len, S))
left = []
rows = []
right = []

hline = cl.join(hl * ((width + 1)*(b - a) - 1)
for a,b in zip([0] + col_divs, col_divs + [nc]))
for a,b in zip([0] + col_divs, col_divs + [nc]))

# compute rows
for r from 0 <= r < nr:
rows += [hline] * row_divs.count(r)
for r in range(-top_count, nr + bottom_count):
if 0 <= r < nr:
n = row_divs.count(r)
if n:
left.extend([""] * n)
rows.extend([hline] * n)
right.extend([""] * n)
if left_border is not None and 0 <= r < nr:
left.append(str(left_border[r]))
else:
left.append("")
s = ""
for c from 0 <= c < nc:
if col_div_counts[c]:
sep = vl * col_div_counts[c]
if 0 <= r < nr:
sep = vl * col_div_counts[c]
else:
sep = " " * col_div_counts[c]
elif c == 0:
sep = ""
else:
sep = " "
entry = S[r * nc + c]
entry = S[(r + top_count) * nc + c]
entry = " " * (width - len(entry)) + entry
s = s + sep + entry
s = s + vl * col_div_counts[nc]
else:
if 0 <= r < nr:
s = s + vl * col_div_counts[nc]
else:
s = s + " " * col_div_counts[nc]
rows.append(s)
rows += [hline] * row_divs.count(nr)

last_row = len(rows) - 1
if last_row == 0:
rows[0] = slb + rows[0] + srb
if right_border is not None and 0 <= r < nr:
right.append(str(right_border[r]))
else:
right.append("")
else:
if nr == nr + bottom_count:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really want to check if bottom_count == 0 ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that's the same. i've written it in this peculiar way to match the for loop.

n = row_divs.count(nr)
if n:
left.extend([""] * n)
rows.extend([hline] * n)
right.extend([""] * n)

# left and right brackets
for i in range(top_count):
rows[i] = " "*len(slb) + rows[i] + " "*len(srb)
if len(rows) == top_count + 1 + bottom_count:
rows[top_count] = slb + rows[top_count] + srb
else:
rows[0] = tlb + rows[0] + trb
for r from 1 <= r < last_row:
rows[r] = mlb + rows[r] + mrb
rows[last_row] = blb + rows[last_row] + brb
rows[top_count] = tlb + rows[top_count] + trb
for i in range(top_count + 1, len(rows) - bottom_count - 1):
rows[i] = mlb + rows[i] + mrb
rows[-1 - bottom_count] = blb + rows[-1 - bottom_count] + brb
for i in range(bottom_count):
rows[-1 - i] = " "*len(slb) + rows[-1 - i] + " "*len(srb)

# left and right border
left_width = max(len(s) for s in left)
right_width = max(len(s) for s in right)
for i in range(len(rows)):
rows[i] = left[i].rjust(left_width) + rows[i] + right[i].rjust(right_width)

if character_art:
breakpoints = []
Expand Down
24 changes: 21 additions & 3 deletions src/sage/matrix/matrix_mod2_dense.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,9 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse


def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None,
*, unicode=False, shape=None, character_art=False):
*, unicode=False, shape=None, character_art=False,
left_border=None, right_border=None,
top_border=None, bottom_border=None):
r"""
Return a nice string representation of the matrix.

Expand Down Expand Up @@ -384,6 +386,16 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
:class:`~sage.typeset.unicode_art.UnicodeArt` which support line
breaking of wide matrices that exceed the window width

- ``left_border``, ``right_border`` -- sequence (default: ``None``);
if not ``None``, call :func:`str` on the elements and use the
results as labels for the rows of the matrix. The labels appear
outside of the parentheses.

- ``top_border``, ``bottom_border`` -- sequence (default: ``None``);
if not ``None``, call :func:`str` on the elements and use the
results as labels for the columns of the matrix. The labels appear
outside of the parentheses.

EXAMPLES::

sage: B = matrix(GF(2), 3, 3, [0, 1, 0, 0, 1, 1, 0, 0, 0])
Expand Down Expand Up @@ -416,13 +428,19 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
# Set the mapping based on keyword arguments
# We ignore minus_one (it's only there for compatibility with Matrix)
if (rep_mapping is not None or zero is not None or plus_one is not None
or unicode or shape is not None or character_art):
or unicode or shape is not None or character_art
or left_border is not None or right_border is not None
or top_border is not None or bottom_border is not None):
# Shunt mappings off to the generic code since they might not be
# single characters
return matrix_dense.Matrix_dense.str(self, rep_mapping=rep_mapping,
zero=zero, plus_one=plus_one,
unicode=unicode, shape=shape,
character_art=character_art)
character_art=character_art,
left_border=left_border,
right_border=right_border,
top_border=top_border,
bottom_border=bottom_border)

if self._nrows == 0 or self._ncols == 0:
return "[]"
Expand Down
Loading