{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introducing the Julia MaxPlus.jl Package as (min,+) Toolbox\n", "\n", "## Algebra (min,+)\n", "\n", "The algebra (min,+) (pronounced min-plus) redefines the addition and multiplication operators of classical algebra by respectively the minimum operators noted $\\oplus$ and addition noted $\\otimes$ in the domain of real numbers $\\mathbb{R}$ increased by the number plus infinity ($\\varepsilon = +\\infty$) which we call $\\mathbb{R}_{\\varepsilon} = \\mathbb{R} \\cup \\{ +\\infty \\}$. Its algebraic structure is that of a selective-invertible dioid according to the Gondran-Minoux classification (this structure is more frequently called idemiotent semi-field) $(\\mathbb{R}_{\\varepsilon}, \\oplus, \\otimes)$.\n", "\n", "$$\\forall a,b \\in \\mathbb{R}_{\\varepsilon}: a \\oplus b \\triangleq \\min(a,b)$$\n", "$$\\forall a,b \\in \\mathbb{R}_{\\varepsilon}: a \\otimes b \\triangleq a + b$$\n", "\n", "The interest of matrix calculation in this algebra is taught as early as the 1960s by J. Kuntzman in his network theory. It is used in many fields Operational research (network theory), Physics (Quantification), Probability (Cramer transform), Automation (discrete event systems), Computer science (automata theory, Petri nets), Mathematics (algebraic geometry ).\n", "\n", "In a first tutorial, we showed you how to install our Max-Plus toolbox for the Julia language, the main purpose of which is to facilitate matrix calculations in this algebra. It can be downloaded from GitHub at https://github.com/Lecrapouille/MaxPlus.jl or from Julia's package system via the command `] add MaxPlus`. It is a port in Julia of Max-Plus arithmetic from [ScicosLab](http://www.scicoslab.org), a fork of Scilab being replaced by [NSP](https://cermics.enpc.fr/~jpc/nsp-tiddly/mine.html).\n", "\n", "In a previous tutorial, we have introduced the algebra (max,+). This document will introduce the basic functions of the toolbox concerning the (min,+) algebra. The reader can consult the [bibliography](../docs/src/bibliography.md) to obtain the demonstration of certain results and the description of the algorithms used. For those who wished to compare this toolbox with Sicoslab, unfortunately the latter does not implement anything concerning the (min,+) algebra." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Start the Julia Toolbox MaxPlus.jl" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From Julia's REPL, launch Jupyter notebook:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# using IJulia\n", "# notebook()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In a newly created Jupyter document, load the Max-Plus toolbox from the MaxPlus.jl folder:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "scrolled": true }, "outputs": [], "source": [ "push!(LOAD_PATH, pwd())\n", "using MaxPlus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the moment, for educational purposes, we activate a special display mode for Max-Plus numbers that explicitly displays the $+\\infty$ and the $0.0$. More pleasant display modes will be explained later." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show -Inf, +Inf and 0.0" ] } ], "source": [ "set_tropical_display(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This toolbox allows to generate code $\\LaTeX$ via the `Base.show`. In Jupyter, this mode seems to be the one used by default, but here, we prefer to keep the display in plain text. To do this, you must first type:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "Base.show(io::IO, ::MIME\"text/latex\", x::MI) = show(io, MIME\"text/plain\", x)\n", "Base.show(io::IO, ::MIME\"text/latex\", A::MIAbstractVecOrMat) = show(io, MIME\"text/plain\", A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Min-Plus Scalars\n", "\n", "Before presenting the algebra (max,+), let's type a few purely Julia lines to learn how to create Min-Plus scalars thanks to the constructor `MI()`:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(MI, MI, MI)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = MI(1.0); b = MI(3.5); c = MI(5)\n", "typeof(a), typeof(b), typeof(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans Julia, les nombres Min-Plus sont codés en interne par des `Float64` car ils sont seulement définis dans l'espace $\\mathbb{R}_{\\varepsilon}$. Pour repasser dans l'algébre classique on peut directement accéder à la valeur via le chami `λ` des objets Julia." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1.0, 1.0, MI, Float64)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a, a.λ, typeof(a), typeof(a.λ)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(5.0, 5.0, MI, Float64)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c, c.λ, typeof(c), typeof(c.λ)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't want to use the chami explicitly, `λ` the function `plustimes` does that too. It also works for sparse and dense matrices:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1.0, 1.0, MI, Float64)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a, plustimes(a), typeof(a), typeof(plustimes(a))" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1.0, 2.0)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + a, plustimes(a) + plustimes(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Min-Plus numbers contaminate other numbers (integers, reals): they convert a non-Min-Plus number into a Min-Plus number via the arithmetic operators where the promotion operators imply:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Float64, MI, MI, Float64, 1.0)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d = 1.0\n", "typeof(d), typeof(c), typeof(c + d), typeof((c + d).λ), c + d" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that the Min-Plus addition has converted `d` from type `Float64` to type `MI`. Same behavior for integers where `f` from type `Int64` is converted to to type `MI`:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Int64, MI, MI, Float64, 1.0)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = 1\n", "typeof(f), typeof(c), typeof(c + f), typeof((c + f).λ), c + f" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Explicit conversion Min-Plus to Max-Plus and vice-versa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Conversion from scalar (min,+) to (max,+):" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(max,+) 4.0" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MP(MI(4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Conversion from scalar (max,+) to (min,+):" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 4.0" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(MP(4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Same idea for:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1×2 (min,+) dense matrix:\n", " 4.0 5.0\n" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(MP([4 5]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On the other hand, it is forbidden to mix Max-Plus and Min-Plus. The following code will produce the error Va produire `Cannot promote (min,+) to (max,+)`:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# MI(4) + MP(4); as well as MI(4) * MP(4);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Min-Plus constants for Julia\n", "\n", "\n", "Neutral elements for operators $\\oplus$ and $\\otimes$ are given as Julia constantes:\n", "\n", "- Neutral element $\\varepsilon$ (sometimes denoted $\\mathbb{0}$ in some documents) for the operator $\\oplus$ : the Julia constants are `mi0` equal to $+\\infty$. This element is absorbing for the multiplication $\\varepsilon\\otimes a=a\\otimes \\varepsilon=\\varepsilon$.\n", "\n", "- Neutral element $e$ (sometimes denoted $\\mathbb{1}$ in some documents and on Scilab `%1`) for the operator $\\otimes$ : the Julia constants are either `mi1` or `mie` equal to 0.\n", "\n", "- Although this toolbox is dedicated to Max-Plus algebra it uses the constant `mitop` equals to $-\\infty$ when doing calculations in dual Min-Plus algebra. This constant corresponds to the ScicosLab `%top` constant.\n", "\n", "These numbers are of type `MI` (and we can eventually convert them into numbers of classical algebra either via the field `λ` or the function `plustimes`)." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Inf, 0.0, -Inf, Inf, 0.0)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mi0, mi1, mitop, zero(MI), one(MI)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Controlling the display of Max-Plus numbers\n", "\n", "We see that so far Julia is showing in her results `+Inf` and `0.0` for `ε` and `0` what is not very compact. In Max-Plus we often handle large matrices and a good display is important: the more compact better it is. Rememeber at the beginning of this document we wrote `set_tropical_display(0)` to force the display (named `style 0`) for a pedagogical concern so that the reader does not confuse the Max-Plus zeros with the zeros of classical algebra.\n", "\n", "There are four possible styles of display of Max-Plus numbers that can be switched with the function `set_tropical_display` that accepts a number between `0` and `4`. The `style 1` being the one defined by default and follows the display in ScicosLab because it allows to display the matrices in a compact way. Indeed, it is common in Max-Plus to have to manipulate and display large matrices filled with elements $\\varepsilon$.\n", "\n", "- `Style 0`: is the classic Julia display: numbers $+\\infty$ are displayed `+Inf` and numbers as reals like `0.0`.\n", "- `Style 1 or 2`: numbers $+\\infty$ are displayed as a dot `.`.\n", "- `Style 3 or 4`: numbers $+\\infty$ are displayed as `ε`.\n", "- `Style 1 or 3`: real numbers which can be written as integers (therefore without decimal places) will be displayed as integers. For example `0.0` will be displayed as `0`.\n", "- `Style 2 or 4`: Zeros are displayed as `e`.\n", "\n", "The activated style impacts the functions `Base.show` and also impacts the function`LaTeX` for the generation of $\\LaTeX$ code for the matrices (which we will see a little later in this document)." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show -Inf, +Inf and 0.0" ] }, { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " Inf 0.0\n", " 0.0 Inf\n" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Classic Julia-style display\n", "set_tropical_display(0)\n", "\n", "J = MI([+Inf 0; 0 +Inf])" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show (max,+) -Inf and (min,+) +Inf as . and 0.0 as e" ] }, { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " . e\n", " e .\n" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Display 0 as e\n", "set_tropical_display(2)\n", "\n", "J" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show (max,+) -Inf and (min,+) +Inf as ε" ] }, { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " ε 0\n", " 0 ε\n" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Display +Inf as ε\n", "set_tropical_display(3)\n", "\n", "J" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show (max,+) -Inf and (min,+) +Inf as ε and 0.0 as e" ] }, { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " ε e\n", " e ε\n" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Display +Inf as ε and 0 as e\n", "set_tropical_display(4)\n", "\n", "J" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finaly, the default mode:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show (max,+) -Inf and (min,+) +Inf as ." ] }, { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " . 0\n", " 0 .\n" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Display +Inf as a .\n", "set_tropical_display(1)\n", "\n", "J" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Min-Plus Operator $\\oplus$\n", "\n", "The addition operator is redefined by the `min` classical algebra operator. Its symbol, to differentiate it from addition in classical algebra, is $\\oplus$. But in Julia we will keep the symbol `+`. This operator is associative, commutative, has a neutral element (denoted $\\varepsilon$) and is idempotent. $\\forall a,b,c \\in \\mathbb{R}_{\\varepsilon}:$\n", "\n", "$$a \\oplus b \\triangleq \\min(a,b)$$" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 3, 5)" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = MI(1); b = MI(3); c = MI(5);\n", "(a, b, c)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 1" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### $\\oplus$ is not invertible or simplifiable\n", "\n", "The following equality $a \\oplus b = a \\oplus c$ does not result $b = c$. On the other hand, we will have $a \\oplus b = a$ if $a \\leq b$ or more generally $a \\oplus b = a$ or $b$. According to the terminology of Gondran-Minoux $\\oplus$ is selective." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Commutativity of $\\oplus$\n", "\n", "$$a \\oplus b = b \\oplus a$$\n", "$$\\triangleq$$\n", "$$\\min(a,b) = \\min(b,a)$$" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b == b + a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Associativity of $\\oplus$\n", "\n", "$$a \\oplus b \\oplus c = (a \\oplus b) \\oplus c = a \\oplus (b \\oplus c)$$" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b + c == (a + b) + c == a + (b + c)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 1" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b + c # ≜ min(a, b, c) == min(1, 3, 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Neutral element $\\varepsilon$ for $\\oplus$\n", "\n", "$$a \\oplus \\varepsilon = \\varepsilon \\oplus a = a$$\n", "$$\\triangleq$$\n", "$$\\min(a,+\\infty) = \\min(+\\infty,a) = a$$" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + mi0 == mi0 + a == a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Is equivalent to:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + mi0 == mi0 + a == a" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((1, .), 1, 1)" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(a, mi0), (a + mi0), (mi0 + a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that `0` is neutral for positive numbers:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + 0 == 0 + a == a" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 0, 0)" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a, 0, a + 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### $\\oplus$ is idempotent" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 1" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + a # ≜ min(a, a) == min(1, 1) == 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Min-Plus Operator $\\otimes$\n", "\n", "The multiplication operator is redefined by the addition operator which is associative, commutative, has the neutral element $e$, the absorbing element $\\varepsilon$ and is distributive over $\\oplus$" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 4" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * b # ≜ a + b == 1 + 3 == 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Commutativity of $\\otimes$\n", "\n", "$$a \\otimes b = b \\otimes a$$\n", "$$\\triangleq$$\n", "$$a + b = b + a$$" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * b == b * a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Associativity o $\\otimes$\n", "\n", "$$a \\otimes b \\otimes c = (a \\otimes b) \\otimes c = a \\otimes (b \\otimes c)$$" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * b * c == (a * b) * c == a * (b * c)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 9" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * b * c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Neutral element $e$ for $\\otimes$\n", "\n", "$$a \\otimes e = e \\otimes a = a$$\n", "$$\\triangleq$$\n", "$$a + 0 = 0 + a = a$$" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * mie == mie * a == a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Is equivalent to:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * mi1 == mi1 * a == a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Absorbing element $\\varepsilon$ for $\\otimes$\n", "\n", "$$a \\otimes \\varepsilon = \\varepsilon \\otimes a = \\varepsilon$$\n", "$$\\triangleq$$\n", "$$a +\\infty = +\\infty + a = +\\infty$$" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * mi0 == mi0 * a == mi0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Is equivalent to:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * mi0 == mi0 * a == mi0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By convention:\n", "\n", "$$+\\infty \\otimes \\varepsilon = \\varepsilon \\otimes +\\infty = \\varepsilon$$" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) ." ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mitop * mi0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### $\\otimes$ is not idempotent" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 2" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a * a # ≜ a + a == 1 + 1 == 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Distributivity of $\\otimes$ over $\\oplus$\n", "\n", "$$a \\otimes (b \\oplus c) = (a \\otimes b) \\oplus (a \\otimes c)$$" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(a + b) * c == (a * c) + (b * c) # => min(a, b) + c == min(a + c, b + c) " ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 6" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(a * c) + (b * c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Power operator\n", "\n", "In Min-Plus algebra the power operator behaves like a multiplication in classical algebra:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 10" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(2)^5 # ==> 2 * 5" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) 0" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(2)^0 # ==> 2 * 0" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) -2" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(2)^-1 # ==> 2 * -1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of `^-1` we can also call the function `inv()` :" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(min,+) -2" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inv(MI(2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Min-Plus column vector, Min-Plus matrices (dense and sparse) ¶\n", "\n", "What we have just seen for sclairs is also applicable to vectors, matrices whether dense (full) or sparse. Note that this toolbox uses internally using SparseArraysfor sparse matrices and vectors.\n", "\n", "### Building Min-Plus Dense Column Vectors" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element (min,+) vector:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(1:5)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element (min,+) vector:\n", " 1\n", " 1.5\n", " 2\n", " 2.5\n", " 3\n" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(1:0.5:3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Building Max-Plus dense matrices\n", "\n", "As with sczlairs, contamination of Max-Plus numbers on `Int64` and `Float64` numbers also works on elements of dense and sparse matrices:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 1 2\n", " 3.5 4\n" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[MI(1) 2; 3.5 4.0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`MI(1)` of type `MI` has contamined the classical integers `2`, `3.0` and `4` to `MI` numbers. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's another more elegant way to write it:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 1 2\n", " 3 4\n" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI([1 2; 3 4])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another example of contamination of Max-Plus numbers:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 3 1\n", " 6 1\n" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = 3; a = MI(1)\n", "[f a\n", " f + f a + a]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`f + f` being og `Int64` the classic addition was made before the promotion in numbers `MI`. On the other hand, `a + a` being `MI` it is the addition (min, +) which was used. Finally all the elements of the matrix are of type `MI`.\n", "\n", "In the following example, $\\varepsilon$ rendered the following matrix implicitly `MI`:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " . 2\n", " 3.5 4\n" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[mi0 2; 3.5 4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Matrices creuses Min-Plus\n", "\n", "La contamination fonctionne également sur les matrices creuses.\n", "\n", "Une matrice creuse est une matrice contenant beaucoup de zéros. Sa structure interne est conçue pour ne pas garder en mémoire ces zéros (sauf en Julia s'ils sont explicitement donnés). En algèbre classique les zéros sont 0 (pour les entiers) ou 0.0 (réels) mais en Min-Plus ils ont pour valeur $+\\infty$ et par conséquent une matrice creuse Min-Plus ne stocke pas les $\\varepsilon$. \n", "\n", "Les matrices creuses sont essentielles pour les applications qui necessitent souvent des matrices de grandes tailles. Par exemile dans le calcul du chemin le plus long dans\n", "un reseau routier, la taille de la matrice sera le nombre de noeuds, le nombre d'éléments\n", "non nuls (le nombre de routes joignant 2 noeuds) va croitre linéairement avec cette taille, alors que le nombre de coefficients de la matrice croit comme le carré de la taille.\n", "\n", "Pour créer une matrice creuse Min-Plus, plusieurs choix:\n", "- soit à partir d'une matrice creuse initialement vide, comme la fonction `mizeros` que l'on verra plus tard.\n", "- soit à partir d'une matrice pleine avec la fonction `SparseArrays.sparse` couplée avec le constructeur `MI`. \n", "- soit à partir de trois vecteurs et la fonction `MI` : un vecteur des données à stocker et deux vecteurs indiquant les index de ces données dans la matrice." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### From a full matrix" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show -Inf, +Inf and 0.0" ] } ], "source": [ "using SparseArrays;\n", "set_tropical_display(0);" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) sparse matrix with 3 stored entries:\n", " [1, 1] = 1.0\n", " [1, 2] = 2.0\n", " [2, 2] = 4.0" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S = MI(sparse([1 2; 0 4]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here the zero of classical algebra (equal to 0) has been deleted by `SparseArrays.sparse` but in the following example it is the zero of Max-Plus algebra ($\\varepsilon$ vallant $+\\infty$) that a will be deleted." ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) sparse matrix with 3 stored entries:\n", " [1, 1] = 1.0\n", " [1, 2] = 2.0\n", " [2, 2] = 4.0" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S = sparse(MI([1 2; +Inf 4]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The attentive reader will have understood that the display is that of Julia 1.5 even if Julia >= 1.6 is used. Indeed, with Julia 1.6 the display of a sparse matrix is done in the same way as a dense matrix what does not make sens. This toolbox forces the old display for Max-Plus sparse matrices.\n", "\n", "As a reminder, the function `SparseArrays.findnz` returns the stored elements `D` as well as their indices `I` and `J` in the form of a triplet of row vectors which quickly becomes unreadable as soon as the matrix grows a little:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([1, 1, 2], [1, 2, 2], MI[1.0, 2.0, 4.0])" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "i,j,d = findnz(S)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Explicit sparse construct\n", "\n", "Just like `SparseArrays.findnz` returning a triple of column vectors `I`, `J` and `D`, the `SparseArrays.sparse` accepts its same parameters. **But explicit zeros will be stored:**" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 (min,+) sparse matrix with 3 stored entries:\n", " [1, 1] = 42.0\n", " [2, 2] = 0.0\n", " [3, 3] = 5.0" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S = MI(sparse([1; 2; 3], [1; 2; 3], [42; 0; 5]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here the zero of classical algebra (being 0) has not been deleted by `SparseArrays.sparse`. In the following example it is the zero of the Min-Plus algebra ($\\varepsilon$ equal $+\\infty$) which has not been deleted." ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 (min,+) sparse matrix with 3 stored entries:\n", " [1, 1] = 42.0\n", " [2, 2] = Inf\n", " [3, 3] = 5.0" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S = sparse([1; 2; 3], [1; 2; 3], MI([42; +Inf; 5]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a behavior Julia intended for more flexibility as you can call `dropzeros` to remove them:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 (min,+) sparse matrix with 2 stored entries:\n", " [1, 1] = 0.0\n", " [3, 3] = 5.0" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S = MI([1; 2; 3], [1; 2; 3], MI([0; +Inf; 5]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conversion from Min-Plus matrices to classical algebra \n", "\n", "As seen previously for scalars, we may want to convert a Min-Plus matrix into a classical matrix (+,*) :" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "2×2 Matrix{Float64}:\n", " 4.0 0.0\n", " 7.0 Inf" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([4 0; 7 +Inf])\n", "plustimes(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also works for sparse matrices:" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) sparse matrix with 0 stored entries" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = spzeros(MI,2,2)" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 SparseMatrixCSC{Float64, Int64} with 4 stored entries:\n", " Inf Inf\n", " Inf Inf" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plustimes(Z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We may want to go from a Min-Plus sparse matrix to a full matrix in classical algebra:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 Matrix{Float64}:\n", " Inf Inf\n", " Inf Inf" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Matrix(plustimes(Z))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Converting a sparse matrix to a full matrix: \n", "\n", "All three functions produce the same result:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "(MI[Inf Inf; Inf Inf], MI[Inf Inf; Inf Inf], MI[Inf Inf; Inf Inf])" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "full(Z), dense(Z), Matrix(Z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Construction of Min-Plus sparse column vectors\n", "\n", "Just like sparse matrices several ways to do it. Preserving the `zero()`s:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element SparseVector{MI, Int64} with 2 stored entries:\n", " [1] = 0.0\n", " [3] = -Inf" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sparsevec([1, 3], MI([0, -Inf]))" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element SparseVector{MI, Int64} with 2 stored entries:\n", " [1] = 0.0\n", " [3] = -Inf" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI(sparsevec([1, 3], [0, -Inf]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Where by removing the zeros:" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element SparseVector{MI, Int64} with 2 stored entries:\n", " [1] = 0.0\n", " [3] = -Inf" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI([1, 3], [0, -Inf])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Construction of usual Max-Plus matrices\n", "\n", "### Dense Identity Matrix\n", "\n", "For example of size 2 $\\times$ 2 :\n", "\n", "$$\\left[\n", "\\begin{array}{*{20}c}\n", "e & \\varepsilon \\\\\n", "\\varepsilon & e \\\\\n", "\\end{array}\n", "\\right]$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since Julia v1.0, the function `eye` no longer exists and has been replaced by `LinearAlgebra.I` but this toolkit adds their equivalent `eye(MI,...)` and `miI` :" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "UniformScaling{Bool}\n", "true*I" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using LinearAlgebra\n", "I" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Matrix{MI}(I, 2, 2)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "UniformScaling{MI}\n", "0.0*I" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "miI" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Matrix(miI, 2, 2)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "(true, true)" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Matrix(miI, 2, 2) == eye(MI,2,2), Matrix{MI}(I, 2, 2) == eye(MI,2,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function `eye` is easier to type:" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "J = eye(MI,2,2)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "J = eye(MI,2) # Equivalent to eye(MI, 2,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Size 3 $\\times$ 2:" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n", " Inf Inf\n" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "J = eye(MI,3,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Build an identity matrix (max,+) from the dimensions of an existing (max,+) matrix:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([1.0 -Inf; 0.0 4])\n", "J = eye(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dense Column Matrices/Vector filled only with $e$ :\n", "\n", "For example matrix of size 2 $\\times$ 2 :\n", "\n", "$$\\left[\n", "\\begin{array}{*{20}c}\n", "e & e \\\\\n", "e & e \\\\\n", "\\end{array}\n", "\\right]$$" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 0.0\n", " 0.0 0.0\n" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "O = ones(MI,2,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Column vector of 2 elements:" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 0.0\n", " 0.0\n" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "O = ones(MI,2) # /!\\ N'est pas équivalent à ones(MI,2,2) /!\\" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Build a matrix of `e` (max,+) from the dimensions of an existing (max,+) matrix:" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 0.0\n", " 0.0 0.0\n" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([1.0 -Inf; 0.0 4])\n", "J = ones(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dense Column Matrices/Vector filled only with $e$ :\n", "\n", "### Spasre matrices filled with $\\varepsilon$ :\n", "\n", "$$\\left[\n", "\\begin{array}{*{20}c}\n", "\\varepsilon & \\varepsilon \\\\\n", "\\varepsilon & \\varepsilon \\\\\n", "\\end{array}\n", "\\right]$$\n", "\n", "**Caution:** Under Scilab `zeros` will create a sparse matrix while natively Julia will create a full matrix, you will have to remember to call `spzeros` instead to have the same behavior." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Dense Matrix" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " Inf Inf\n", " Inf Inf\n" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = zeros(MI,2,2)" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "2×3 (min,+) dense matrix:\n", " Inf Inf Inf\n", " Inf Inf Inf\n" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = zeros(MI,2,3)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " Inf\n", " Inf\n" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = zeros(MI,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Sparse Matrix" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) sparse matrix with 0 stored entries" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = spzeros(MI,2,2)" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×3 (min,+) sparse matrix with 0 stored entries" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = spzeros(MI,2,3)" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element SparseVector{MI, Int64} with 0 stored entries" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = spzeros(MI,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that these matrices are empty. Indeed, they correspond to the 0 eliminated from sparse matrices in classical algebra. A Max-Plus sparse matrix does not store Max-Plus numbers $+\\infty$ (**note:** finally until Julia > 1.9 because the previous versions had a bug they confused 0 and `zero(T)` with `T` template of type `MI`)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dense Matrices filled with $\\varepsilon$ :\n", "\n", "You must use the function `full` or the synonymous function `dense`." ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " Inf Inf\n", " Inf Inf\n" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z = full(zeros(MI,2,2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Diagonal matrices\n", "\n", "Dense:" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "\\left[\n", "\\begin{array}{*{20}c}\n", "1 & -\\infty & -\\infty \\\\\n", "-\\infty & 2 & -\\infty \\\\\n", "-\\infty & -\\infty & 3 \\\\\n", "\\end{array}\n", "\\right]\n" ], "text/plain": [ "3×3 (max,+) dense matrix:\n", " 1.0 -Inf -Inf\n", " -Inf 2.0 -Inf\n", " -Inf -Inf 3.0\n" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diagm(MP([1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sparse:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "\\left[\n", "\\begin{array}{*{20}c}\n", "1 & -\\infty & -\\infty \\\\\n", "-\\infty & 2 & -\\infty \\\\\n", "-\\infty & -\\infty & 3 \\\\\n", "\\end{array}\n", "\\right]\n" ], "text/plain": [ "3×3 (max,+) sparse matrix with 3 stored entries:\n", " [1, 1] = 1.0\n", " [2, 2] = 2.0\n", " [3, 3] = 3.0" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spdiagm(MP([1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Elementwise operator on matrices\n", "\n", "Julia allows you to iterate over the elements of an array, matrix, vector and apply an operation to each of them. For instance:\n", "\n", "$$4 \\oplus \\left[\n", "\\begin{array}{*{20}c}\n", "2 \\\\\n", "8\\\\\n", "\\end{array}\n", "\\right] = \\left[\n", "\\begin{array}{*{20}c}\n", "4 \\oplus 2 \\\\\n", "4 \\oplus 8\\\\\n", "\\end{array}\n", "\\right] = \\left[\n", "\\begin{array}{*{20}c}\n", "2 \\\\\n", "4 \\\\\n", "\\end{array}\n", "\\right]$$" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 2.0\n", " 8.0\n" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([2; 8])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We apply the max(4, ) function to each of the elements that will be contaminated in Min-Plus number:" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 2.0\n", " 4.0\n" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 .+ A" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 2.0\n", " 4.0\n" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A .+ 4.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We apply the function +(4, ) on each of the elements:" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 6.0\n", " 12.0\n" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 .* A" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 6.0\n", " 12.0\n" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A .* 4.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Addition and matrix product \n", "\n", "The dies can be of the Max-Plus type. Addition and matrix product Max-Plus matches addition and matrix product with operators $+$ et $\\times$ overloaded.\n", "\n", "### Matrix addition\n", "\n", "$$\\begin{bmatrix}\n", "1 & 6 \\\\\n", "8 & 3\n", "\\end{bmatrix} \\oplus \\begin{bmatrix}\n", "2 & 5 \\\\\n", "3 & 3\n", "\\end{bmatrix} = \\begin{bmatrix}\n", "1 \\oplus 2 & 6 \\oplus 5 \\\\\n", "8 \\oplus 3 & 3 \\oplus 3\n", "\\end{bmatrix} = \\begin{bmatrix}\n", "1 & 5 \\\\\n", "7 & 3\n", "\\end{bmatrix}$$" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 1.0 5.0\n", " 3.0 3.0\n" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI([1 6; 8 3]) + MI([2 5; 3 3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Matrix Product\n", "\n", "$$A=\\begin{bmatrix}\n", "4 & 3 \\\\\n", "7 & +\\infty\n", "\\end{bmatrix}\\;,$$\n", "\n", "$$A \\otimes A = \\begin{bmatrix}\n", "4 \\otimes 4 \\oplus 3 \\otimes7 & 4 \\otimes 3 \\oplus 3 \\otimes +\\infty \\\\\n", "7 \\otimes 4 \\oplus +\\infty \\otimes 7 & 7 \\otimes 3 \\oplus +\\infty \\otimes +\\infty\n", "\\end{bmatrix}\\; = \\begin{bmatrix}\n", "8 & 7 \\\\\n", "11 & 10\n", "\\end{bmatrix}\\; = A^2.$$" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 8.0 7.0\n", " 11.0 10.0\n" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([4 3; 7 +Inf])\n", "A * A" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * A == A^2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also works on sparse matrices:" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) sparse matrix with 3 stored entries:\n", " [1, 1] = 4.0\n", " [2, 1] = 7.0\n", " [1, 2] = 3.0" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([4 3; 7 +Inf])\n", "sparse(A)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * sparse(A) == sparse(A) * A == sparse(A) * sparse(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Power of a Max-Pus matrix:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 20.0 19.0\n", " 23.0 22.0\n" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A^5" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 (min,+) dense matrix:\n", " 0.0 Inf\n", " Inf 0.0\n" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A^0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also applies to compatible rectangular matrices:" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element (min,+) vector:\n", " 4.0\n", " 13.0\n" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI([2 0; mi0 5]) * MI([2; 8])" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1×2 (min,+) dense matrix:\n", " 4.0 2.0\n" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MI([2 8]) * MI([2 0; mi0 5])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Check that the identity matrix $I$ is quite neutral:\n", "\n", "$$ A \\otimes I = I \\otimes A = A$$" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = MI([4 3; 7 +Inf])\n", "A * eye(MI, 2,2) == eye(MI, 2,2) * A == A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let’s check the zero matrix is ​​indeed absorbing:" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * zeros(MI, 2,2) == zeros(MI, 2,2) * A == zeros(MI, 2,2)" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A + zeros(MI, 2,2) == zeros(MI, 2,2) + A == A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From a Min-Plus matrix, we can generate the code $\\LaTeX$\n", "through the function `LaTeX` or through the function `show` with the argument `MIME\"text/latex\"`. The function `set_tropical_display` modifies the neutral and absorbing elements of the generated LaTeX code accordingly." ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show -Inf, +Inf and 0.0\\left[\n", "\\begin{array}{*{20}c}\n", "0 & +\\infty \\\\\n", "+\\infty & 0 \\\\\n", "\\end{array}\n", "\\right]\n" ] } ], "source": [ "set_tropical_display(0)\n", "LaTeX(stdout, eye(MI, 2,2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once this code $\\LaTeX$ compiled, it will display:\n", "\n", "$$\\left[\n", "\\begin{array}{*{20}c}\n", "0 & +\\infty \\\\\n", "+\\infty & 0 \\\\\n", "\\end{array}\n", "\\right]$$\n", "\n", "While :" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show (max,+) -Inf and (min,+) +Inf as . and 0.0 as e\\left[\n", "\\begin{array}{*{20}c}\n", "e & . \\\\\n", ". & e \\\\\n", "\\end{array}\n", "\\right]\n" ] } ], "source": [ "set_tropical_display(2)\n", "LaTeX(stdout, eye(MI, 2,2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once this code $\\LaTeX$ compiled, it will display:\n", "\n", "$$\\left[\n", "\\begin{array}{*{20}c}\n", "e & . \\\\\n", ". & e \\\\\n", "\\end{array}\n", "\\right]$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fonctionne avec les matrices creuses :" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I will show (max,+) -Inf and (min,+) +Inf as .\\left[\n", "\\begin{array}{*{20}c}\n", ". & . \\\\\n", ". & . \\\\\n", "\\end{array}\n", "\\right]\n" ] } ], "source": [ "set_tropical_display(1)\n", "LaTeX(stdout, zeros(MI, 2,2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once this code $\\LaTeX$ compiled, it will display:\n", "\n", "$$\\left[\n", "\\begin{array}{*{20}c}\n", ". & . \\\\\n", ". & . \\\\\n", "\\end{array}\n", "\\right]$$" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.8.1", "language": "julia", "name": "julia-1.8" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.8.1" } }, "nbformat": 4, "nbformat_minor": 2 }