Skip to content

add slice in Vector.fs and Matrix.fs, add reduceRows and reduceCols in Matrix.fs, add tests#18

Open
Brulevich-Nikita wants to merge 3 commits into
Lamagraph:mainfrom
Brulevich-Nikita:brulevich-n-work
Open

add slice in Vector.fs and Matrix.fs, add reduceRows and reduceCols in Matrix.fs, add tests#18
Brulevich-Nikita wants to merge 3 commits into
Lamagraph:mainfrom
Brulevich-Nikita:brulevich-n-work

Conversation

@Brulevich-Nikita
Copy link
Copy Markdown

add:
slice in Vector.fs
slice in Matrix.fs
reduceRows in Matrix.fs
reduceCols in Matrix.fs
map in Matrix.fs
(mapi in Matrix.fs and Vector.fs have been already released)
4 map tests in Vector.fs
3 fromCoordinateList tests in Vector.fs
4 mapi tests in Vector.fs
11 slice tests in Vector.fs
6 map tests in Matrix.fs
3 fromCoordinateList tests in Matrix.fs
5 mapi tests in Matrix.fs
21 slice tests in Matrix.fs
7 reduceRows tests in Matrix.fs
7 reduceCols tests in Matrix.fs

to be done:
kronecker with tests

[<Fact>]
let ``fromCoordinateList with index out of range`` () =
let coo =
CoordinateList(6UL<nrows>, 6UL<ncols>, [ (9UL<rowindex>, 9UL<colindex>, 13) ])
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.

Ну вообще неплохо бы падать в таком случае. Ну, только не Failwith, а Result.

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.

Понял, буду использовать Result вместо failwith. Тесты переделаю.

[<Fact>]
let ``fromCoordinateList with zero length and some values returns empty matrix`` () =
let coo =
CoordinateList(
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.

Извините, не очень понял. Название теста (и соответствующего теста в Векторе) заменю на более информативное.

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.

Я, скорее, про то, что сами по себе такие случаи, кажется, должнв приводить к ошибке, а не "молча" обрабатываться.

let result = toCoordinateList vec
Assert.Equal(CoordinateList(7UL<dataLength>, [ (1UL<index>, 100); (3UL<index>, 2); (5UL<index>, 3) ]), result)
(*
[<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.

Что не так с этим тестом?

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.

Я написал тест на дубликат индекса, при тестировании выяснил, что поведение в данном случае не определено реализацией. Что лучше сделать: убрать данный тест или добавить обработку дубликатов индексов?

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.

Можно добавить обработку. Можно поступтть как в GraphBLAS: принимать ещё и функцию, которая агрегирует значения с одинаковым индексом.

Comment thread QuadTree/Matrix.fs Outdated
(colStart: int)
(colEnd: int)
: Result<SparseMatrix<'a>, string> =
match () with
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.

Ээээ. А зачем такой странный match?

Comment thread QuadTree/Matrix.fs Outdated
let newRows = uint64 (rowEnd - rowStart + 1) * 1UL<nrows>
let newCols = uint64 (colEnd - colStart + 1) * 1UL<ncols>

let coo =
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.

Для получения сдвинутых координат я выбрал и правда не самый рациональный подход в виду перевода в coo и обратно, попробую изменить реализацию через рекурсию (и для матрицы, и для вектора)

Comment thread QuadTree/Matrix.fs Outdated
Ok newMatrix

let reduceRows (op: 'a option -> 'a option -> 'a option) (matrix: SparseMatrix<'a>) : Vector.SparseVector<'a> =
let rows = matrix.nrows
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.

Не очень понял Ваш вопрос. Если Вы имеете в виду, что одну из функций (например, reduceRows) можно реализовать как есть, а reduceCols через transpose + reduceRows, то она чуть проиграет по производительности, поэтому (на мой взгляд) лучше оставить двумя функциями.

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/Vector.fs Outdated

let slice (_start: int) (_end: int) (vector: SparseVector<'a>) : Result<SparseVector<'a>, string> =
match () with
| _ when _start < 0 -> Error "Start should be >= 0"
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.

Старался что здесь, что в Матрице избежать излишних ifов, придерживаясь функционального стиля, поэтому воспользовался такой конструкцией. Подскажите пожалуйста, как сделать лучше: оставить данный match или заменить на if-else? Или реализовать через что-то по типу match (_start < 0, _end < 0, _start > int vector.length - 1, _end > int vector.length - 1, _start > _end) with ...?

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.

if (elif, если надо) --- нормальная конструкция. Пользуйтесь ей.

Comment thread QuadTree/Matrix.fs Outdated
| false, false ->
Node(emptySub, emptySub, emptySub, insert half (row - halfRow) (col - halfCol) value emptySub)

let foldQuadtree folder state size tree =
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.

Правда ли, что это не просто общего фида fold и его не надо на верхний уровень вынести?

Comment thread QuadTree/Matrix.fs
let mat2Cols = uint64 matrix2.ncols
let initialTree = empty newSize

let foldMatrixB acc rowMat1 colMat1 valMat1 =
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.

Что-то тут сложно происходит. Можете рассказать, как у Вас кронекер работает?

@Brulevich-Nikita
Copy link
Copy Markdown
Author

@gsvgit , здравствуйте! Изменение получилось большим, поэтому опишу по пунктам основные изменения, чтобы проще было понимать:

  1. поправил небольшие недочёты

  2. fold и правда общего вида, я вынес его в отдельную функцию и переписал с её использованием reduceRows, reduceCols и Kronecker

  3. Вы спрашивали про принцип работы Кронекера (извините, что сразу не увидел Ваше сообщение). Если кратко, то:

    1. создаётся пустое хранилище нужного размера для итоговой матрицы
    2. инициализируется функция insert для вставки значения
    3. foldMatrixB обходит всё дерево B, применяя для каждого элемента f, вычисляя результат Some computedVal, высчитывает новые координаты элемента и вставляет в итоговое дерево (acc) через insert
    4. основной проход по A при помощи foldQuadtree (вынес его в отдельную внешнюю функцию по Вашей рекомендации), для каждого элемента из A вызывает foldMatrixB, передавая текущее итоговое дерево, координаты и значение A
    5. Подсчитывает nvals и в случае успеха создаёт SparseMatrix
      Данный способ показался мне наиболее приемлемым (пытался сделать чуть проще - не получалось).
  4. Обернул fromCoordinateList в Option (Result Error / Ok) для матрицы и вектора, из-за этого пришлось переделывать все места во всех файлах QTreeFSharp'а, где использовался fromCoordinateList. Во многих местах из-за этого теряется читаемость и внешний вид, но таким образом валидация данных происходит сразу на уровне координатного списка и избегаются failwith'ы. Подскажите пожалуйста, есть ли у Вас замечания по данному решению?

  5. Исправил свои и чужие failwith'ы, а так же два моих ThrowException, теперь вместо них Result

  6. В данном пункте я могу ошибаться, но хотел бы его с Вами обсудить. Когда я поменял сигнатуру fromCoordinateList'а, пришлось менять все тесты, но изменения, само собой, чисто под сигнатуру, без изменения данных. При запуске все тесты прошли, кроме тестов Борувки. Я посидел с Борувкой и заметил несколько вещей:

    1. Тесты, если я не ошибаюсь, вообще проходят мимо, так как в исходном checkResult функция принимает три аргумента "let checkResult name actual expected =", а в каждом тесте два аргумента "checkResult (Graph.Boruvka.mst graph) expected".
    2. Когда я это поправил, некоторые тесты продолжили падать из-за своей невалидности. Посмотрите, пожалуйста, как минимум на тест Boruvka MST 10 nodes random.. actual там равно expected на графе с 13 рёбрами и 10 вершинами (что невозможно для Борувки), так к тому же expected содержит явный цикл (0 - 1 - 5 - 6), а изначальный тест горит зелёным.
    3. Даже несмотря на пропуск тестов, реализация была не до конца корректной из-за симметричного дублирования рёбер, поэтому я внёс небольшие изменения с каноническим представлением ребра, чтобы алгоритм работал корректно, оставил //комментарии по аналогии с теми, что были.
      Я не до конца могу быть уверен в том, что верно всё понял про Борувку, поэтому, возможно, залез куда не требовалось, но всё же хочу попросить вас посмотреть и проверить, насколько верны мои изменения (как минимум очень смущает случай с якобы проходящим тестом, где в expected циклический граф, когда Борувка должна строить минимальное остовное дерево, что друг другу противоречит), и если был не прав, то откачу изменения назад

Если изменения верные, то начну с замерами.

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