Skip to content

feat: add Coordinate List (COO) sparse matrix with get/map/mapi/set/mxm - #20

Open
narysh wants to merge 51 commits into
Lamagraph:mainfrom
narysh:narysev-work
Open

narysh wants to merge 51 commits into
Lamagraph:mainfrom
narysh:narysev-work

Conversation

@narysh

@narysh narysh commented Jun 12, 2026

Copy link
Copy Markdown

Implement:

  • Vector.filter / Matrix.filter
  • Vector.exists / Vector.forall (short-circuit || / &&)
  • Matrix.exists / Matrix.forall (short-circuit || / &&)

Tests (64 total):

  • Vector.filter: 9
  • Vector.exists: 12
  • Vector.forall: 12
  • Matrix.filter: 9
  • Matrix.exists: 11
  • Matrix.forall: 11

Benchmarks:

  • Filters.fs: Vector/Matrix filter (dense + sparse), N=64,256,4096
  • Exists.fs: Vector/Matrix exists (dense + sparse), N=64,256,4096
  • Forall.fs: Vector/Matrix forall (dense + sparse), N=64,256,4096
  • Register in BenchmarkSwitcher and .fsproj

narysh added 2 commits June 12, 2026 22:04
Implement:
  - Vector.filter / Matrix.filter
  - Vector.exists / Vector.forall (short-circuit || / &&)
  - Matrix.exists / Matrix.forall (short-circuit || / &&)

Tests (64 total):
  - Vector.filter: 9
  - Vector.exists: 12
  - Vector.forall: 12
  - Matrix.filter: 9
  - Matrix.exists: 11
  - Matrix.forall: 11

Benchmarks:
  - Filters.fs: Vector/Matrix filter (dense + sparse), N=64,256,4096
  - Exists.fs: Vector/Matrix exists (dense + sparse), N=64,256,4096
  - Forall.fs: Vector/Matrix forall (dense + sparse), N=64,256,4096
  - Register in BenchmarkSwitcher and .fsproj
@gsvgit

gsvgit commented Jun 13, 2026

Copy link
Copy Markdown
Member

Правда ли, что то, что Вы сделали, не выражается через map тривиальным образом,

@narysh

narysh commented Jun 13, 2026

Copy link
Copy Markdown
Author

Правда ли, что то, что вы сделали, нельзя выразить через map тривиальным образом?

filter выразить можно, но это было одной из моих задач в проекте
forall и exists можно выразить через аналог fold, но тогда он пройдёт всё дерево, а forall и exists это оптимизируют и полный обход случится, только в худшем случае.
Я понимаю, что этого мало. Остальные задачи, которые у меня были изначально, вы уже реализовали, может вы можете тогда подсказать другие функции, которые нужно реализовать?

@narysh narysh changed the title feat(quadtree): add filter, exists, forall with tests and benchmarks feat: add Coordinate List (COO) sparse matrix with get/map/mapi/set/mxm Sep 17, 2026
Comment thread QuadTree.Tests/Tests.COOArray.fs Outdated
)
| Error e -> failwithf "unexpected error %A" e

[<Fact>]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

А чего ни одного Property-based теста?

Assert.Equal(Ok(Some 11), get result 0UL<rowindex> 0UL<colindex>)
Assert.Equal(Ok(Some 121), get result 1UL<rowindex> 1UL<colindex>)

[<Fact>]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

И тут property не проверяете.

Comment thread QuadTree/Matrix.fs Outdated
(Leaf(UserValue(res)), nnz) |> Ok
let get (matrix: SparseMatrix<'a>) (row: uint64<rowindex>) (col: uint64<colindex>) : Result<option<'a>, Error> =
if uint64 row >= uint64 matrix.nrows || uint64 col >= uint64 matrix.ncols then
Error Error.InvalidElementIndex

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Посмотрите, как обычно называется такая ситуация в .NET

Comment thread QuadTree/Matrix.fs
| LeftValuesOnlyIndexed of (uint64<rowindex> -> uint64<colindex> -> 'a -> Option<'b> -> Option<'c>)

let private applyBinary
(op: BinaryOp<'a, 'b, 'c>)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

А нельзя ли всё это дело как-то не копировать?

…COO to COOArray

- COOArray: walk the sorted array with a cursor in full-cell cooMap/cooMapi
  paths and buffer results in ResizeArray instead of building a lookup Map;
  rewrite mergeBinary as a two-pointer merge over sorted arrays
- COOArray/COOList: strengthen mxmcoo optimization detection by probing all
  distinct values rather than only the first entry
- COOList: replace Map-based lookup in full-cell cooMap/cooMap2 with a pointer
  walk over the sorted list
- Tests: add FsCheck property tests for mxmcoo against a naive reference and
  cross-checks between the array and list implementations
- Benchmark: add list-backed get/set cases (COOLIST_get/set, resultListVal)
  to Format, DenseFormat and RealMatrix benchmarks
- Rename module COO to COOArray for consistency with COOList: file rename,
  fsproj entries, open statements, test module, and Fantomas formatting
Comment thread QuadTree.Benchmark/FormatBenchmarks.fs Outdated

[<Benchmark(Description = "Dense_COO_mxm")>]
member this.DenseCooMxm() =
let op_add x y =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

А можно не делать миллион копий одинаковых функций? Это уже не первое дублирование кода. Проверьте весь код на аккуртаность.

@gsvgit gsvgit left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Перечитайте ВЕСЬ код. У Вас дубликатов море. И заодно порешайте концликты и коммиты подпишите.

@gsvgit gsvgit left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Всё ещё тонны дублирующегося кода.

Comment thread QuadTree.Tests/PropertyTests.fs Outdated
let nrows = uint64 coo.nrows * 1UL<rowindex>
let ncols = uint64 coo.ncols * 1UL<colindex>

let cooGetThrows =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I hope, there is a conventional way to check whether method throws particular exception.

Comment thread QuadTree.Tests/Tests.COOFormats.fs Outdated
let toArray<'T> isArray (m: obj) : COOEntry<'T> array =
let listProp = m.GetType().GetProperty("list")

if listProp <> null then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fix compiler warnings.

Comment thread QuadTree.Tests/Tests.COOFormats.fs Outdated
else
Result.map box (COOList.cooMap2iValues (unbox m1) (unbox m2) f)

let cooMap2iLeftValues isArray (m1: obj) (m2: obj) f =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

obj type in F#? Really?

…rt.Throws, reuse OptionMonoid

- Replace untyped obj, box/unbox and runtime reflection in Formats test helper with strictly typed type COO<'T> = Array | List discriminated union.

- Fix all 42 compiler warnings (FS0686, FS0064) in Tests.COOFormats.fs by adding explicit generic type parameters and equality constraints.

- Replace manual try/with boilerplate in PropertyTests out-of-bounds test with idiomatic Assert.Throws<ArgumentOutOfRangeException>.

- Remove duplicated local definitions of opAdd and opMul in PropertyTests.fs in favor of canonical OptionMonoid.op_add and OptionMonoid.op_mult.
@narysh

narysh commented Oct 12, 2026

Copy link
Copy Markdown
Author

поменял try ... with на идиоматичный в F# Assert.Throws
заменил obj на дженерики
пофиксил warningи

Comment thread QuadTree/Boruvka.fs

type Error =
| EdgesCalculationProblem of LinearAlgebra.Error
| CEdgesCalculationProblem of Vector.Error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But why? What the reason of such changes?

Comment thread QuadTree/BFS.fs

type Error =
| NewFrontierCalculationProblem of LinearAlgebra.Error
| FrontierCalculationProblem of Vector.Error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But why? What the reason of such changes?

Comment thread QuadTree/COOArray.fs Outdated

let cooGet (coo: ArrayCOO<'a>, rowindex: uint64<rowindex>, colindex: uint64<colindex>) : Result<option<'a>, Error> =
if uint64 rowindex >= uint64 coo.nrows then
raise (System.ArgumentOutOfRangeException("rowindex", "Row index is outside the matrix bounds."))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No. We do not use exceptions.

Comment thread QuadTree/COOArray.fs

i <- i + 1UL

let cooGet (coo: ArrayCOO<'a>, rowindex: uint64<rowindex>, colindex: uint64<colindex>) : Result<option<'a>, Error> =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you use indexed properties?

Comment thread QuadTree/COOArray.fs Outdated
let private cooMapInner (coo: ArrayCOO<'a>) (op: UnaryOp<'a, 'b>) : ArrayCOO<'b> =
match op with
| UnaryOp.ValuesOnly f ->
let buf = ResizeArray<COOEntry<'b>>(coo.list.Length)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No resize arrays.

Comment thread QuadTree/COOArray.fs Outdated
| UnaryOp.ValuesOnly f ->
let buf = ResizeArray<COOEntry<'b>>(coo.list.Length)

for (i, j, v) in coo.list do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No loops.

Comment thread QuadTree/COOList.fs Outdated

let cooGet (coo: ListCOO<'a>, rowindex: uint64<rowindex>, colindex: uint64<colindex>) : Result<option<'a>, Error> =
if uint64 rowindex >= uint64 coo.nrows then
raise (System.ArgumentOutOfRangeException("rowindex", "Row index is outside the matrix bounds."))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like code duplicates.

Comment thread QuadTree/Matrix.fs Outdated
(pr + halfSize * 1UL<rowindex>, pc), // SOUTH WEST
(pr + halfSize * 1UL<rowindex>, pc + halfSize * 1UL<colindex>) // SOUTH EAST
[<Struct>]
type ArrayCOO<'value> =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Array in matrix?

- Matrix: return Error.InconsistentSizeOfArguments in get and set instead of raising ArgumentOutOfRangeException
- COOArray: use array instead of seq in constructors/SortEntries, clone before sort, check op_add None None in canOptimize
- COOList: remove unused range function, check op_add None None in canOptimize
- Tests: update get/set test assertions to expect Error and add mxmcoo semiring fallback test
…yCOO and ListCOO

- In ArrayCOO: added bounds validation, stable deduplication (last occurrence wins), and TryCreate returning Result<_, Error>.

- In ListCOO: added sorting, deduplication, and bounds validation on construction, preventing silent None misses in findEntry.

- Internal operations now construct via CreatePresorted with DEBUG assertions.

- Added comprehensive unit tests in Tests.COOFormats.fs.
…, and optimize COO formats

- Normalize in-bounds Dummy leaves to UserValue None in mapInner and map2Inner

- Check storage.size equality before quadtree traversal in map2Inner

- Add Int32 overflow guard before dense materialization in COOArray.cooMap2

- Replace entryComparer and Unchecked.defaultof with pure tail-recursive binary search in COOArray

- Deduplicate array truncation logic and remove unused m1ByRow in COOList

- Restrict CreatePresorted accessibility to internal in ArrayCOO and ListCOO

- Add regression tests for overflow checks, storage size mismatches, and Dummy padding
…e comparison and monoid validation

- Add compareCoords, entryCoordCompare, isAddNeutral and canMergeOpAdd helpers to Common.fs

- Deduplicate cooMapInner and cooMap2Inner dense traversals using advanceCursor and runDense in COOList and COOArray

- Unify coordinate comparisons across all sorting and search functions

- Unify leaf nnz calculation in Matrix.fs with leafNnz helper and remove unreachable pattern FS0026
…low safety, and hot-path allocations

- Align map2Inner Dummy handling with mapInner: handle boundary-spanning quadrants via recursive splitting and correct leafNnz calculation

- Guard against Int32 overflow in COOArray.cooMapInner and COOList.cooMap2Inner on large dense operations

- Add m2.nrows overflow check to COOArray.mxmcoo canOptimize

- Unbox coordinates in COOList.mxmcoo reduceAdjacent to eliminate hot-path tuple allocations

- Deduplicate applyBinary emission in mergeBinary across COOList and COOArray

- Simplify delta calculation in Matrix.set and remove dead code
@narysh

narysh commented Oct 18, 2026

Copy link
Copy Markdown
Author

У большинства операций скорость не просела, даже увеличилась прикрепляю замеры
COO_Comparison_Now_vs_Before.md

@gsvgit gsvgit left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

За счёт чего производительность улучшилась?

Comment thread QuadTree/SSSP.fs

type Error =
| NewFrontierCalculationProblem of LinearAlgebra.Error
| FrontierCalculationProblem of Vector.Error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Зачем убрали специфичность ошибки? Причём везде. Тип ошибки позволяет понять, где она произошла.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

пофиксю, верну как было

@gsvgit

gsvgit commented Oct 20, 2026

Copy link
Copy Markdown
Member

Кстати, а чего отчёт не обновили?

@narysh

narysh commented Oct 20, 2026

Copy link
Copy Markdown
Author

обновил локально, сейчас открою PR

@narysh

narysh commented Oct 20, 2026 •

Copy link
Copy Markdown
Author

За счёт чего производительность улучшилась?

убрал ResizeArray заменив быстрым предподсчётом и аллокацией памяти 1 раз в начале
поменял сравнение координат (i1, j1) < (i2, j2) которое делалось через обобщённый метод на простое let c = compare i1 i2; if c <> 0 then c else compare j1 j2
в set сортировку поменял на поиск места вставки бин поиском + сдвиг

- Restore domain-specific Matrix.Error, Vector.Error, and LinearAlgebra.Error

- Restore diagnostic error contexts in BFS, SSSP, Boruvka, MST, and TriangleCount

- Move checkBounds to Matrix.fs and remove generic Common.Error
@narysh

narysh commented Oct 20, 2026

Copy link
Copy Markdown
Author

Кстати, а чего отчёт не обновили?

gsvgit/1_year_practice_2026_reports#42
вот, актуализировал замеры, остальное не трогал там

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants