## ~kb/sparse

602e043a5ffbd4e47b925ab4058502e36e8322c5 — Kim Burgess 3 years ago
Initial Monoid, Semiring, Vector types
4 files changed, 171 insertions(+), 0 deletions(-)

M src/sparse.cr
A src/sparse/monoid.cr
A src/sparse/semiring.cr
A src/sparse/vector.cr
M src/sparse.cr => src/sparse.cr +2 -0
@@ 1,3 1,5 @@
require "./sparse/*"
module Sparse
VERSION = `shards version`
end

A src/sparse/monoid.cr => src/sparse/monoid.cr +23 -0
@@ 0,0 1,23 @@
module Sparse
module Monoid(T)
# The binary operator that provides an assocatiate combination.
#
# ```
# ```
#
# The above most hold in the mathematical sense as ordering of this may be
# changed. Non associatively due to floating point precessicion erros may be
# ignored in this context.
@[AlwaysInline]
abstract def add(a : T, b : T) : T
# Identity element.
#
# ```
# add(a, id) == add(id, a) == a
# ```
@[AlwaysInline]
abstract def id : T
end
end

A src/sparse/semiring.cr => src/sparse/semiring.cr +13 -0
@@ 0,0 1,13 @@
require "./monoid"
module Sparse
module Semiring(T, U, V)
macro extended
extend Monoid(T)
end
# Multiplicative binary operator.
@[AlwaysInline]
abstract def mul(a : U,  b : V) : T
end
end

A src/sparse/vector.cr => src/sparse/vector.cr +133 -0
@@ 0,0 1,133 @@
require "set"
module Sparse
struct Vector(T, N)
@idx : Array(UInt64)
@val : Array(T)
# Creates a new `Vector` with *initial_capacity* pre-allocated for elements.
def initialize(initial_capacity : Int = 0)
@idx = typeof(@idx).new initial_capacity
@val = typeof(@val).new initial_capacity
end
private def inititialize(@idx, @val)
end
# Creates a new `Vector` with the same domain, size and contents.
def dup : Vector(T, N)
idx = @idx.dup
val = @val.dup
Vector(T, N).new idx, val
end
# Change the size of an existing vector.
#
# NOTE: this will mutate the internal state of the passed *vec*, but return
# this encased in a new `Vector` with the new dimensions encoded within the
# type. Any references to the passed source vector should be discarded.
macro resize!(vec, size)
begin
%idx, %val = {{vec}}.extract_tuples
%top = %idx[-1]?
if %top && %top >= {{size}}
%del = %idx.bsearch_index { |i| i >= {{size}} }
%idx.delete_at %del, %idx.size
%val.delete_at %val, %cal.size
end
Vector(typeof({{vec}}[0]), {{size}}).new %idx, %val
end
end
# Removes all elements.
def clear : self
@idx.clear
@val.clear
self
end
# The dimensionality this vector represents.
def size : UInt64
N
end
# The number of stored elements.
def nvals : Int
@idx.size
end
# Store the passed elements in *self*.
#
# If duplicate values exists for the same index, the result of callig *dup*
# with the previes and new values will be stored.
def build(indicies : Indexable(UInt64), values : Indexable(T), dup : T, T -> T) : self
raise "cannot build into a populated Vector" unless nvals.zero?
seen = Set(UInt64).new
indicies.zip values do |idx, val|
next if idx >= size
if idx.in? seen
old = unsafe_fetch(idx).not_nil!
val = dup.call old, val
set_element idx, val if old != val
else
set_element idx, val
end
seen << idx
end
end
# Set one element to a given *value*.
def set_element(index : Int, value : T) : T
check_index_out_of_bounds index
pos = @idx.bsearch_index &.>=(index)
if @idx[pos] == idx
@val[pos] = value
else
@idx.insert pos index
@val.insert pos value
end
end
# Remove one stored element.
def remove_element(index : Int) : T?
check_index_out_of_bounds index
pos = @idx.bsearch_index &.==(index)
if del
@idx.delete_at pos
@val.delete_at pos
end
end
# Extract one element of *self* into a scalar.
def extract_element(index : Int) : T?
check_index_out_of_bounds index
pos = @idx.bsearch_index &.==(index)
if pos
@val.unsafe_fetch pos if pos
end
end
# Extract all elements with values assigned.
def extract_tuples : { Indexable(Idx), Indexable(T) }
{ @idx, @val }
end
@[AlwaysInline]
private def unsafe_fetch(index : Int) : T?
pos = @idx.bsearch_index &.==(index)
@val.unsafe_fetch pos if pos
end
private def check_index_out_of_bounds(index)
check_index_out_of_bound { raise IndexError.new "Invalid index (#{index})" }
end
private def check_index_out_of_bounds(index, &)
if index < 0 || index >= size
yield index
else
index
end
end
end
end