Skip to content
Open
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
3 changes: 3 additions & 0 deletions src/doc/en/reference/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,9 @@ REFERENCES:
An Algorithmic Approach, Algorithms and Computation in Mathematics,
Volume 20, Springer (2007)

.. [But2010] Peter Butkovič, *Max-linear systems. Theory and algorithms.*
Springer Monographs in Mathematics. London: Springer. xvii, 272 p. (2010).

.. [Buell89] Duncan A. Buell.
*Binary Quadratic Forms: Classical Theory and Modern Computations.*
Springer, 1989.
Expand Down
4 changes: 4 additions & 0 deletions src/doc/en/reference/semirings/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ Standard Semirings

sage/rings/semirings/non_negative_integer_semiring
sage/rings/semirings/tropical_semiring
sage/rings/semirings/tropical_polynomial
sage/rings/semirings/tropical_mpolynomial
sage/rings/semirings/tropical_matrix
sage/rings/semirings/tropical_variety

.. include:: ../footer.txt
2 changes: 1 addition & 1 deletion src/sage/matrix/action.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ cdef class MatrixMatrixAction(MatrixMulAction):
B = B.dense_matrix()
else:
A = A.dense_matrix()
assert type(A) is type(B), (type(A), type(B))
# assert type(A) is type(B), (type(A), type(B))
prod = A._matrix_times_matrix_(B)
if A._subdivisions is not None or B._subdivisions is not None:
Asubs = A.subdivisions()
Expand Down
34 changes: 25 additions & 9 deletions src/sage/matrix/matrix_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
from sage.misc.lazy_attribute import lazy_attribute
from sage.misc.superseded import deprecated_function_alias
from sage.misc.persist import register_unpickle_override
from sage.categories.sets_cat import Sets
from sage.categories.semirings import Semirings
from sage.categories.rings import Rings
from sage.categories.fields import Fields
from sage.categories.enumerated_sets import EnumeratedSets
Expand All @@ -60,7 +62,6 @@
feature=Meataxe())
lazy_import('sage.groups.matrix_gps.matrix_group', ['MatrixGroup_base'])

_Rings = Rings()
_Fields = Fields()


Expand Down Expand Up @@ -320,6 +321,15 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation):
else:
return matrix_laurent_mpolynomial_dense.Matrix_laurent_mpolynomial_dense

try:
from sage.rings.semirings.tropical_semiring import TropicalSemiring
except ImportError:
pass
else:
if isinstance(R, TropicalSemiring):
from sage.rings.semirings import tropical_matrix
return tropical_matrix.Matrix_tropical_dense

# The fallback
from sage.matrix.matrix_generic_dense import Matrix_generic_dense
return Matrix_generic_dense
Expand Down Expand Up @@ -725,8 +735,8 @@ def __classcall__(cls, base_ring,
sage: MS2._my_option
False
"""
if base_ring not in _Rings:
raise TypeError("base_ring (=%s) must be a ring" % base_ring)
if base_ring not in Semirings():
raise TypeError("base_ring (=%s) must be a ring or a semiring" % base_ring)

if ncols_or_column_keys is not None:
try:
Expand Down Expand Up @@ -898,12 +908,17 @@ def __init__(self, base_ring, nrows, ncols, sparse, implementation):

from sage.categories.modules import Modules
from sage.categories.algebras import Algebras
if nrows == ncols:
category = Algebras(base_ring.category())
if base_ring in Rings():
if nrows == ncols:
category = Algebras(base_ring.category())
else:
category = Modules(base_ring.category())
category = category.WithBasis().FiniteDimensional()
else:
category = Modules(base_ring.category())

category = category.WithBasis().FiniteDimensional()
if nrows == ncols:
category = Semirings()
else:
category = Sets()

if not self.__nrows or not self.__ncols:
is_finite = True
Expand Down Expand Up @@ -2024,8 +2039,9 @@ def identity_matrix(self):
if self.__nrows != self.__ncols:
raise TypeError("identity matrix must be square")
A = self.zero_matrix().__copy__()
one = self.base_ring().one()
for i in range(self.__nrows):
A[i, i] = 1
A[i, i] = one
A.set_immutable()
return A

Expand Down
10 changes: 9 additions & 1 deletion src/sage/matrix/special.py
Original file line number Diff line number Diff line change
Expand Up @@ -936,11 +936,19 @@ def identity_matrix(ring, n=0, sparse=False):
Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring
sage: M.is_mutable()
True
::
sage: T = TropicalSemiring(QQ)
sage: identity_matrix(T, 3)
[ 0 +infinity +infinity]
[+infinity 0 +infinity]
[+infinity +infinity 0]
"""
if isinstance(ring, (Integer, int)):
n = ring
ring = ZZ
return matrix_space.MatrixSpace(ring, n, n, sparse)(1)
return matrix_space.MatrixSpace(ring, n, n, sparse)(ring.one())


@matrix_method
Expand Down
2 changes: 1 addition & 1 deletion src/sage/misc/sagedoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

Check that sphinx is not imported at Sage start-up::

sage: os.system("sage -c \"if 'sphinx' in sys.modules: sys.exit(1)\"")

Check warning on line 31 in src/sage/misc/sagedoc.py

View workflow job for this annotation

GitHub Actions / Conda (ubuntu, Python 3.12, all, editable)

Warning: slow doctest:

slow doctest:: Test ran for 5.37s cpu, 5.40s wall Check ran for 0.00s cpu, 0.00s wall

Check warning on line 31 in src/sage/misc/sagedoc.py

View workflow job for this annotation

GitHub Actions / Conda (macos, Python 3.13, all)

Warning: slow doctest:

slow doctest:: Test ran for 5.62s cpu, 9.79s wall Check ran for 0.00s cpu, 0.00s wall
0
"""
# ****************************************************************************
Expand Down Expand Up @@ -1454,7 +1454,7 @@

sage: browse_sage_doc._open("reference", testing=True)[0] # needs sagemath_doc_html
'http://localhost:8000/doc/live/reference/index.html'
sage: browse_sage_doc(identity_matrix, 'rst')[-107:-47] # needs sage.modules
sage: browse_sage_doc(identity_matrix, 'rst')[-311:-251] # needs sage.modules

Check failure on line 1457 in src/sage/misc/sagedoc.py

View workflow job for this annotation

GitHub Actions / Conda (ubuntu, Python 3.13, all)

Failed example:

Failed example:: Got: ' [0 0 1]\n sage: M.parent()\n Full MatrixSpace of 3 by'

Check failure on line 1457 in src/sage/misc/sagedoc.py

View workflow job for this annotation

GitHub Actions / Conda (macos, Python 3.13, all)

Failed example:

Failed example:: Got: ' [0 0 1]\n sage: M.parent()\n Full MatrixSpace of 3 by'
'...Full MatrixSpace of 3 by 3 sparse matrices...'
"""
def __init__(self):
Expand Down
1 change: 1 addition & 0 deletions src/sage/rings/semirings/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ py.install_sources(
'__init__.py',
'all.py',
'non_negative_integer_semiring.py',
'tropical_matrix.py',
'tropical_mpolynomial.py',
'tropical_polynomial.py',
'tropical_semiring.pyx',
Expand Down
199 changes: 199 additions & 0 deletions src/sage/rings/semirings/tropical_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
r"""
Matrices over tropical semirings

AUTHORS:

- Xavier Caruso (2025-11): initial version
"""

# ****************************************************************************
# Copyright (C) 2025 Xavier Caruso <xavier@caruso.ovh>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************

from sage.matrix.constructor import matrix
from sage.matrix.matrix_generic_dense import Matrix_generic_dense
from sage.rings.infinity import infinity


class Matrix_tropical_dense(Matrix_generic_dense):
r"""
A class for dense matrices over a tropical semiring.

EXAMPLES::

sage: from sage.rings.semirings.tropical_matrix import Matrix_tropical_dense
sage: T = TropicalSemiring(QQ)
sage: M = matrix(T, [[1, 2], [3, 4]])
sage: isinstance(M, Matrix_tropical_dense)
True
"""
def extremum_cycle_mean(self):
r"""
Return the extremal (that is, minimal if the addition is max
and maximum is the addition is min) mean weight of this matrix
It is also the smallest/largest eigenvalue of this matrix.

ALGORITHM:

We implement Karp's algorithm described in []_, Section 1.6.1.

EXAMPLES::

sage: T = TropicalSemiring(QQ, use_min=False)
sage: M = matrix(T, [[-2, 1, -3],
....: [ 3, 0, 3],
....: [ 5, 2, 1]])
sage: M.extremum_cycle_mean()
3

::

sage: T = TropicalSemiring(QQ)
sage: z = T.zero()
sage: M = matrix(T, [[z, 1, 10, z],
....: [z, z, 3, z],
....: [z, z, z, 2],
....: [8, 0, z, z]])
sage: M.extremum_cycle_mean()
5/3
"""
T = self.base_ring()
n = self.ncols()
if self.nrows() != n:
raise TypeError("matrix must be square")
v = matrix(1, n, n*[T.one()])
vs = [v]
for _ in range(n):
v = v * self
vs.append(v)
w = [vs[n][0,j].lift() for j in range(n)]
if T._use_min:
return min(max((w[j] - vs[k][0,j].lift()) / (n-k) for k in range(n))
for j in range(n) if w[j] is not infinity)
else:
return max(min((w[j] - vs[k][0,j].lift()) / (n-k) for k in range(n))
for j in range(n) if w[j] is not infinity)

def weak_transitive_closure(self):
r"""
Return the weak transitive closure of this matrix `M`,
that is, by definition

.. MATH::

A \oplus A^2 \oplus A^3 \oplus A^4 \oplus \cdots

or raise an error if this sum does not converge.

ALGORITHM:

We implement the Floyd-Warshall algorithm described in
[But2010]_, Algorithm 1.6.21.

EXAMPLES::

sage: T = TropicalSemiring(QQ)
sage: z = T.zero()
sage: M = matrix(T, [[z, 1, 10, z],
....: [z, z, 3, z],
....: [z, z, z, 2],
....: [8, 0, z, z]])
sage: M.weak_transitive_closure()
[14 1 4 6]
[13 5 3 5]
[10 2 5 2]
[ 8 0 3 5]

We check that the minimal cycle mean of `M` is the largest
value `a` such that `(-a) \otimes M` has a weak transitive
closure::

sage: M.extremum_cycle_mean()
5/3
sage: aM = T(-5/3) * M
sage: aM.weak_transitive_closure()
[22/3 -2/3 2/3 1]
[ 8 0 4/3 5/3]
[20/3 -4/3 0 1/3]
[19/3 -5/3 -1/3 0]
sage: bM = T(-2) * M
sage: bM.weak_transitive_closure()
Traceback (most recent call last):
...
ValueError: negative cycle exists

.. SEEALSO::

:meth:`strong_transitive_closure`
"""
T = self.base_ring()
n = self.ncols()
if self.nrows() != n:
raise TypeError("matrix must be square")
G = self.__copy__()
for p in range(n):
for i in range(n):
if i == p:
continue
for j in range(n):
if j == p:
continue
G[i,j] += G[i,p] * G[p,j]
if i == j:
if T._use_min and G[i,i].lift() < 0:
raise ValueError("negative cycle exists")
if not T._use_min and G[i,i].lift() > 0:
raise ValueError("positive cycle exists")
return G

def strong_transitive_closure(self):
r"""
Return the string transitive closure of this matrix `M`,
that is, by definition

.. MATH::

I \oplus A \oplus A^2 \oplus A^3 \oplus A^4 \oplus \cdots

or raise an error if this sum does not converge.

ALGORITHM:

We implement the Floyd-Warshall algorithm described in
[But2010]_, Algorithm 1.6.21.

EXAMPLES::

sage: T = TropicalSemiring(QQ, use_min=False)
sage: M = matrix(T, [[-5, -2, -6],
....: [ 0, -3, 0],
....: [ 2, -1, -2]])
sage: M.strong_transitive_closure()
[ 0 -2 -2]
[ 2 0 0]
[ 2 0 0]

::

sage: T = TropicalSemiring(QQ)
sage: M = matrix(T, [[-5, -2, -6],
....: [ 0, -3, 0],
....: [ 2, -1, -2]])
sage: M.strong_transitive_closure()
Traceback (most recent call last):
...
ValueError: negative cycle exists

.. SEEALSO::

:meth:`weak_transitive_closure`
"""
return self.parent().identity_matrix() + self.weak_transitive_closure()

kleene_star = strong_transitive_closure
Loading