Table of Contents

Domácí úkol 3

Úkol

Úkolem je implementovat efektivní výpočty s maticemi a přetížit základní funkce (např. +, -, *), aby fungovaly se speciálními typy matic. Vaším úkolem je implementovat následující typy:

* DiagonalMatrix, * IdentityMatrix.

Budeme pracovat pouze se čtvercovými maticemi (2D). Oba nové typy proto implementujte jako podtypy AbstractMatrix{T} (jak je uvedeno v šabloně):

struct Diagonal{T} <: AbstractMatrix{T}
struct Identity{T} <: AbstractMatrix{T}

Všimněte si, že typ AbstractMatrix{T} je alias pro AbstractArray{T, 2}. Implementujte typy tak, aby mohly být vytvořeny následovně:

d = [1, 2, 3]   # diagonála
D = Diagonal(d) # diagonální matice s diagonálou `d`
 
n = 3
I = Identity(n) # jednotková matice velikosti `n` 

Pro oba typy implementujte následující metody z modulu Base pro práci s maticemi:

* getindex: pro indexy i, j vrátí odpovídající prvek, * size: vrátí n-tici rozměrů matice.

Pokud tyto dvě metody správně implementujete pro naše speciální typy matic, některé základní operace již budou fungovat. Například sčítání dvou diagonálních matic bude fungovat, i když neefektivně:

d = Diagonal(...)
d + d  # funguje

Dále implementujte i další metody z Base:

* sum: vrátí součet všech prvků matice, * convert: funguje pro Diagonal a Identity stejně jako pro běžné matice (např. convert(Matrix{Float32}, [1 2; 3 4]) převede původní matici typu Matrix{Float64} na Matrix{Float32}).

Navíc implementujte následující operace s maticemi:

* diagonal: vrátí diagonálu jako vektor, * trace: vrátí stopu matice.

Nezapomeňte tyto funkce implementovat také pro původní typ Matrix.

Poté definujte funkce Base +, -, * pro oba nové typy matic, podobně jako existují metody typu (+)(x::Matrix, y::Matrix). Implementujte +, -, *:

* pro všechny kombinace maticových vstupů, * pro matici a vektor, * pro matici a skalár (číslo).

Nepřidávejte varianty s broadcastingem.

Každá metoda by měla být implementována co nejefektivněji z hlediska výpočtu. Zajistěte také, aby návratový typ byl správný, tedy že součet dvou instancí DiagonalMatrix je DiagonalMatrix, zatímco součet Matrix a DiagonalMatrix má být typu Matrix. Matice by měly být typově stabilní a řídit se standardními pravidly pro propagaci typů (např. promote(Int64, Float64) = Float64).

Nakonec implementujte doplňující konstruktory pro převod mezi typy, kde to dává smysl:

* DiagonalMatrix(::Matrix) vrátí DiagonalMatrix obsahující pouze diagonální prvky původní matice, * Matrix(::DiagonalMatrix) vytvoří běžnou matici se stejnou diagonálou a nulami mimo ni, * Matrix(::IdentityMatrix) vytvoří běžnou jednotkovou matici s jedničkami na diagonále a nulami mimo ni.

Budou prováděny tři kategorie testů:

* úplnost: všechny metody jsou implementovány pro všechny typy, * správnost: metody vracejí správné výsledky (a správné typy výstupů), * efektivita: metody jsou výpočetně efektivní a využívají vlastnosti příslušného typu matice (některé metody budou časovány a benchmarkovány).

Tipy

Pamatujte, že máte omezený počet odevzdání a při implementaci může nastat mnoho chyb. Ujistěte se proto, že své typy a metody důkladně testujete a přemýšlíte předem.

Obecně se snažte vracet co nejjednodušší možný typ matice. Představte si hierarchii Identity < Diagonal < Matrix. Pokud vaše funkce vrací matici, která je diagonálního charakteru (prvky mimo diagonálu jsou nulové), měli byste vrátit typ Diagonal.

Na druhou stranu to zbytečně nepřekombinujte. Nedělejte metody příliš restriktivní na typ vstupu. Otestujte, které metody po implementaci základní funkcionality už fungují správně (a vracejí správný výstup), abyste je nemuseli psát ručně a zbytečně tak nezvyšovali šanci na chyby.

Můžete také použít balíček BenchmarkTools.jl k měření výkonu vašich metod, například ve srovnání s běžnými maticovými operacemi. Můžete porovnat i s balíčkem LinearAlgebra, ale neočekává se od vás stejný výkon. Cílem je být rychlejší u operací s Diagonal než při práci s plnou maticí, která má nulové prvky mimo diagonálu.

courses/b0b36jul/hw/hw3.txt · Last modified: 2025/10/16 11:05 by maskomic