From 4a5e7ef46bdc54525a528efcaa6ff8615efe6a05 Mon Sep 17 00:00:00 2001 From: michielstock Date: Thu, 23 Oct 2025 10:23:53 +0200 Subject: [PATCH 1/7] :tada: similarity matrix --- src/encoding.jl | 17 +++++++++-------- src/inference.jl | 25 +++++++++++++++++++------ test/inference.jl | 8 ++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/encoding.jl b/src/encoding.jl index 90d6c89..df6e113 100644 --- a/src/encoding.jl +++ b/src/encoding.jl @@ -54,7 +54,7 @@ where `V` is the hypervector collection, `m` is the size of the hypervector coll - [`multibind`](@ref): Multibind encoding, binding-variant of this encoder """ -function multiset(vs::AbstractVector{<:T})::T where {T <: AbstractHV} +function multiset(vs::AbstractVector{<:T})::T where {T<:AbstractHV} return bundle(vs) end @@ -301,7 +301,7 @@ and `\\oplus` are the binding and bundling operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.hash_table.html) """ -function hashtable(keys::T, values::T) where {T <: AbstractVector{<:AbstractHV}} +function hashtable(keys::T, values::T) where {T<:AbstractVector{<:AbstractHV}} @assert length(keys) == length(values) "Number of keys and values aren't equal" return bundle(map(prod, zip(keys, values))) end @@ -369,7 +369,7 @@ and binding operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.cross_product.html) """ -function crossproduct(U::T, V::T) where {T <: AbstractVector{<:AbstractHV}} +function crossproduct(U::T, V::T) where {T<:AbstractVector{<:AbstractHV}} # TODO: This should be bundled without normalizing return bind(multiset(U), multiset(V)) end @@ -439,11 +439,11 @@ and shift operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.ngrams.html) """ -function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int = 3) +function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int=3) l = length(vs) p = l - n + 1 @assert 1 <= n <= length(vs) "`n` must be 1 ≤ n ≤ $l" - return bundle([bind([shift(vs[i + j], j) for j in 0:(n - 1)]) for i in 1:p]) + return bundle([bind([shift(vs[i+j], j) for j in 0:(n-1)]) for i in 1:p]) end """ @@ -485,7 +485,7 @@ hypervector collection, `i` is the position of the entry in the collection, and - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.graph.html) """ -function graph(source::T, target::T; directed::Bool = false) where {T <: AbstractVector{<:AbstractHV}} +function graph(source::T, target::T; directed::Bool=false) where {T<:AbstractVector{<:AbstractHV}} @assert length(source) == length(target) "`source` and `target` must be the same length" return hashtable(source, shift.(target, convert(Int, directed))) end @@ -501,7 +501,7 @@ Creates a set of level correlated hypervectors, where the first and last hyperve - `v::HV`: Base hypervector - `n::Int`: Number of levels """ -function level(v::HV, n::Int) where {HV <: AbstractHV} +function level(v::HV, n::Int) where {HV<:AbstractHV} hvs = [v] p = 2 / n while length(hvs) < n @@ -510,4 +510,5 @@ function level(v::HV, n::Int) where {HV <: AbstractHV} end return hvs end -level(HV::Type{<:AbstractHV}, n::Int; dims::Int = 10_000) = level(HV(dims), n) + +level(HV::Type{<:AbstractHV}, n::Int; dims::Int=10_000) = level(HV(dims), n) diff --git a/src/inference.jl b/src/inference.jl index c6c14cd..7f654cf 100644 --- a/src/inference.jl +++ b/src/inference.jl @@ -42,11 +42,24 @@ function similarity(x::AbstractVector, y::AbstractVector; method::Symbol) end end + +similarity(hvs::AbstractVector{<:AbstractHV}) = [similarity(hv1, hv2) for hv1 in hvs, hv2 in hvs] + +""" + similarity(hvs::AbstractVector{<:AbstractHV}) + +Computes the similarity matrix for a vector of hypervectors using +the similarity metrics defined by the pairwise version of `similarity`. +""" +similarity(hvs::AbstractVector{<:AbstractHV}; method...) = [similarity(hv1, hv2; method...) + for hv1 in hvs, hv2 in hvs] + + nearest_neighbor(x, collection; kwargs...) = maximum( - (similarity(x, xi; kwargs...), i, xi) + (similarity(x, xi; kwargs...), i, xi) for (i, xi) in enumerate(collection) -) + ) nearest_neighbor(x, collection::Dict; kwargs...) = maximum((similarity(x, xi; kwargs...), k, xi) for (k, xi) in collection) @@ -67,15 +80,15 @@ list of `(τ, i)`. function nearest_neighbor(x, collection, k::Int; kwargs...) sims = [ (similarity(x, xi; kwargs...), i) - for (i, xi) in enumerate(collection) + for (i, xi) in enumerate(collection) ] - return partialsort!(sims, 1:k, rev = true) + return partialsort!(sims, 1:k, rev=true) end function nearest_neighbor(x, collection::Dict, k::Int; kwargs...) sims = [ (similarity(x, xi; kwargs...), i) - for (i, xi) in collection + for (i, xi) in collection ] - return partialsort!(sims, 1:k, rev = true) + return partialsort!(sims, 1:k, rev=true) end diff --git a/test/inference.jl b/test/inference.jl index 733fd10..80ace3c 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -57,6 +57,14 @@ @test similarity(x, y) ≈ sim_cos(x.v, y.v) ≈ dot(xd, yd) / norm(xd) / norm(yd) end + @testset "Similarity matrix" begin + levels = level(RealHV(100), 10) + M = similarity(levels) + @test M isa Matrix + @test size(M) == (10, 10) + @test M ≈ M' + end + @testset "NN" begin x = BinaryHV(trues(5)) From a4dc905a1a257b2c039abc4a2a324ce8d75b76b6 Mon Sep 17 00:00:00 2001 From: michielstock Date: Thu, 23 Oct 2025 11:29:15 +0200 Subject: [PATCH 2/7] :tada: levels encoding --- src/HyperdimensionalComputing.jl | 5 +- src/encoding.jl | 91 +++++++++++++++++++++++++++++++- test/encoding.jl | 28 +++++++--- 3 files changed, 115 insertions(+), 9 deletions(-) diff --git a/src/HyperdimensionalComputing.jl b/src/HyperdimensionalComputing.jl index ab891ea..3415f02 100644 --- a/src/HyperdimensionalComputing.jl +++ b/src/HyperdimensionalComputing.jl @@ -30,7 +30,10 @@ export multiset, crossproduct, ngrams, graph, - level + level, + level_encoder, + level_decoder, + levels_encoder_decoder include("inference.jl") export similarity, diff --git a/src/encoding.jl b/src/encoding.jl index df6e113..bb8ccc1 100644 --- a/src/encoding.jl +++ b/src/encoding.jl @@ -499,7 +499,7 @@ Creates a set of level correlated hypervectors, where the first and last hyperve # Arguments - `v::HV`: Base hypervector -- `n::Int`: Number of levels +- `n::Int`: Number of levels (alternatively, provide a vector to be encoded) """ function level(v::HV, n::Int) where {HV<:AbstractHV} hvs = [v] @@ -512,3 +512,92 @@ function level(v::HV, n::Int) where {HV<:AbstractHV} end level(HV::Type{<:AbstractHV}, n::Int; dims::Int=10_000) = level(HV(dims), n) + +level(HVv, vals::AbstractVector) = level(HVv, length(vals)) +level(HVv, vals::UnitRange) = level(HVv, length(vals)) + + + +""" + level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) + +Generate an encoding function based on `level`, for encoding numerical values. It returns a function +that gives the corresponding hypervector for a given numerical input. + +# Arguments +- hvlevels::AbstractVector{<:AbstractHV}: vector of hypervectors representing the level encoding +- numvalues: the range or vector with the corresponding numerical values +- [testbound=false]: optional keyword argument to check whether the provided value is in bounds + +# Example +```julia +numvalues = range(0, 2pi, 100) +hvlevels = level(BipolarHV(), 100) + +encoder = level_encoder(hvlevels, numvalues) + +encoder(pi/3) # hypervector that best represents this numerical value +``` +""" +function level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) + @assert length(hvlevels) == length(numvalues) "HV levels do not match numerical values" + # construct the encoder + function encoder(x::Number) + @assert !testbound || minimum(numvalues) ≤ x ≤ maximum(numvalues) "x not in numerical range" + (_, ind) = findmin(v -> abs(x - v), numvalues) + return hvlevels[ind] + end + return encoder +end + +""" + level_encoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) + +See `level_encoder`, same but provide lower (`a`) and upper (`b`) limit of the interval to be encoded. +""" +level_encoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) = level_encoder(hvlevels, range(a, b, length(hvlevels)); testbound) + +level_encoder(HV, numvalues; testbound=false) = level_encoder(level(HV, length(numvalues)), numvalues; testbound) + + +""" + level_decoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues) + +Generate a decoding function based on `level`, for decoding numerical values. It returns a function +that gives the numerical value for a given hypervector, based on similarity matching. + +# Arguments +- hvlevels::AbstractVector{<:AbstractHV}: vector of hypervectors representing the level encoding +- numvalues: the range or vector with the corresponding numerical values + +# Example +```julia +numvalues = range(0, 2pi, 100) +hvlevels = level(BipolarHV(), 100) + +decoder = level_decoder(hvlevels, numvalues) + +decoder(hvlevels[17]) # value that closely matches the corresponding HV +``` +""" +function level_decoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues) + @assert length(hvlevels) == length(numvalues) "HV levels do not match numerical values" + # construct the decoder + function decoder(hv::AbstractHV) + (_, ind) = findmax(v -> similarity(v, hv), hvlevels) + return numvalues[ind] + end + return decoder +end + +level_decoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number) = level_decoder(hvlevels, range(a, b, length(hvlevels))) + +level_decoder(HV, numvalues; testbound=false) = level_decoder(level(HV, length(numvalues)), numvalues) + +""" + levels_encoder_decoder(hvlevels, numvals..., kwargs...) + +Creates the `encoder` and `decoder` for a level incoding in one step. See `level_encoder` +and `level_decoder` for their respective documentations. +""" +levels_encoder_decoder(hvlevels, numvals...; kwargs...) = level_encoder(hvlevels, numvals...; kwargs...), level_decoder(hvlevels, numvals..., kwargs...) \ No newline at end of file diff --git a/test/encoding.jl b/test/encoding.jl index 69bf5d9..d1b64e7 100644 --- a/test/encoding.jl +++ b/test/encoding.jl @@ -1,12 +1,12 @@ @testset "encoding" begin hvs = BinaryHV.( [ - [1, 0, 0, 0, 0], - [1, 1, 0, 0, 0], - [1, 1, 1, 0, 0], - [1, 1, 1, 1, 0], - [1, 1, 1, 1, 1], - ] + [1, 0, 0, 0, 0], + [1, 1, 0, 0, 0], + [1, 1, 1, 0, 0], + [1, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + ] ) @testset "multiset" begin @@ -46,7 +46,21 @@ s = [1, 3, 4, 2, 5] t = [3, 4, 2, 1, 4] @test graph(hvs[s], hvs[t]) == Bool.([0, 0, 0, 0, 0]) - @test graph(hvs[s], hvs[t]; directed = true) == Bool.([1, 0, 0, 1, 0]) + @test graph(hvs[s], hvs[t]; directed=true) == Bool.([1, 0, 0, 1, 0]) @test_throws AssertionError graph(hvs[s], hvs[[1, 2, 3]]) end + + @testset "levels" begin + numvals = 0:0.1:2pi + levels = level(BinaryHV(100), numvals) + + @test length(levels) == length(numvals) + @test eltype(levels) <: BinaryHV + + encoder, decoder = levels_encoder_decoder(levels, numvals) + hv = encoder(1.467) + @test hv isa BinaryHV + x = decoder(hv) + @test 1 ≤ x ≤ 2 + end end From 0c24aec4a90590ea902464fa71d29c7cc62f1b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Vigil-V=C3=A1squez?= Date: Thu, 23 Oct 2025 11:35:59 +0200 Subject: [PATCH 3/7] style: format with Runic Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/encoding.jl | 27 +++++++++++++-------------- src/inference.jl | 18 ++++++++++-------- test/encoding.jl | 14 +++++++------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/encoding.jl b/src/encoding.jl index bb8ccc1..a806127 100644 --- a/src/encoding.jl +++ b/src/encoding.jl @@ -54,7 +54,7 @@ where `V` is the hypervector collection, `m` is the size of the hypervector coll - [`multibind`](@ref): Multibind encoding, binding-variant of this encoder """ -function multiset(vs::AbstractVector{<:T})::T where {T<:AbstractHV} +function multiset(vs::AbstractVector{<:T})::T where {T <: AbstractHV} return bundle(vs) end @@ -301,7 +301,7 @@ and `\\oplus` are the binding and bundling operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.hash_table.html) """ -function hashtable(keys::T, values::T) where {T<:AbstractVector{<:AbstractHV}} +function hashtable(keys::T, values::T) where {T <: AbstractVector{<:AbstractHV}} @assert length(keys) == length(values) "Number of keys and values aren't equal" return bundle(map(prod, zip(keys, values))) end @@ -369,7 +369,7 @@ and binding operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.cross_product.html) """ -function crossproduct(U::T, V::T) where {T<:AbstractVector{<:AbstractHV}} +function crossproduct(U::T, V::T) where {T <: AbstractVector{<:AbstractHV}} # TODO: This should be bundled without normalizing return bind(multiset(U), multiset(V)) end @@ -439,11 +439,11 @@ and shift operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.ngrams.html) """ -function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int=3) +function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int = 3) l = length(vs) p = l - n + 1 @assert 1 <= n <= length(vs) "`n` must be 1 ≤ n ≤ $l" - return bundle([bind([shift(vs[i+j], j) for j in 0:(n-1)]) for i in 1:p]) + return bundle([bind([shift(vs[i + j], j) for j in 0:(n - 1)]) for i in 1:p]) end """ @@ -485,7 +485,7 @@ hypervector collection, `i` is the position of the entry in the collection, and - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.graph.html) """ -function graph(source::T, target::T; directed::Bool=false) where {T<:AbstractVector{<:AbstractHV}} +function graph(source::T, target::T; directed::Bool = false) where {T <: AbstractVector{<:AbstractHV}} @assert length(source) == length(target) "`source` and `target` must be the same length" return hashtable(source, shift.(target, convert(Int, directed))) end @@ -501,7 +501,7 @@ Creates a set of level correlated hypervectors, where the first and last hyperve - `v::HV`: Base hypervector - `n::Int`: Number of levels (alternatively, provide a vector to be encoded) """ -function level(v::HV, n::Int) where {HV<:AbstractHV} +function level(v::HV, n::Int) where {HV <: AbstractHV} hvs = [v] p = 2 / n while length(hvs) < n @@ -511,13 +511,12 @@ function level(v::HV, n::Int) where {HV<:AbstractHV} return hvs end -level(HV::Type{<:AbstractHV}, n::Int; dims::Int=10_000) = level(HV(dims), n) +level(HV::Type{<:AbstractHV}, n::Int; dims::Int = 10_000) = level(HV(dims), n) level(HVv, vals::AbstractVector) = level(HVv, length(vals)) level(HVv, vals::UnitRange) = level(HVv, length(vals)) - """ level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) @@ -539,7 +538,7 @@ encoder = level_encoder(hvlevels, numvalues) encoder(pi/3) # hypervector that best represents this numerical value ``` """ -function level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) +function level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound = false) @assert length(hvlevels) == length(numvalues) "HV levels do not match numerical values" # construct the encoder function encoder(x::Number) @@ -555,9 +554,9 @@ end See `level_encoder`, same but provide lower (`a`) and upper (`b`) limit of the interval to be encoded. """ -level_encoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) = level_encoder(hvlevels, range(a, b, length(hvlevels)); testbound) +level_encoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound = false) = level_encoder(hvlevels, range(a, b, length(hvlevels)); testbound) -level_encoder(HV, numvalues; testbound=false) = level_encoder(level(HV, length(numvalues)), numvalues; testbound) +level_encoder(HV, numvalues; testbound = false) = level_encoder(level(HV, length(numvalues)), numvalues; testbound) """ @@ -592,7 +591,7 @@ end level_decoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number) = level_decoder(hvlevels, range(a, b, length(hvlevels))) -level_decoder(HV, numvalues; testbound=false) = level_decoder(level(HV, length(numvalues)), numvalues) +level_decoder(HV, numvalues; testbound = false) = level_decoder(level(HV, length(numvalues)), numvalues) """ levels_encoder_decoder(hvlevels, numvals..., kwargs...) @@ -600,4 +599,4 @@ level_decoder(HV, numvalues; testbound=false) = level_decoder(level(HV, length(n Creates the `encoder` and `decoder` for a level incoding in one step. See `level_encoder` and `level_decoder` for their respective documentations. """ -levels_encoder_decoder(hvlevels, numvals...; kwargs...) = level_encoder(hvlevels, numvals...; kwargs...), level_decoder(hvlevels, numvals..., kwargs...) \ No newline at end of file +levels_encoder_decoder(hvlevels, numvals...; kwargs...) = level_encoder(hvlevels, numvals...; kwargs...), level_decoder(hvlevels, numvals..., kwargs...) diff --git a/src/inference.jl b/src/inference.jl index 7f654cf..e293855 100644 --- a/src/inference.jl +++ b/src/inference.jl @@ -51,15 +51,17 @@ similarity(hvs::AbstractVector{<:AbstractHV}) = [similarity(hv1, hv2) for hv1 in Computes the similarity matrix for a vector of hypervectors using the similarity metrics defined by the pairwise version of `similarity`. """ -similarity(hvs::AbstractVector{<:AbstractHV}; method...) = [similarity(hv1, hv2; method...) - for hv1 in hvs, hv2 in hvs] +similarity(hvs::AbstractVector{<:AbstractHV}; method...) = [ + similarity(hv1, hv2; method...) + for hv1 in hvs, hv2 in hvs +] nearest_neighbor(x, collection; kwargs...) = maximum( - (similarity(x, xi; kwargs...), i, xi) + (similarity(x, xi; kwargs...), i, xi) for (i, xi) in enumerate(collection) - ) +) nearest_neighbor(x, collection::Dict; kwargs...) = maximum((similarity(x, xi; kwargs...), k, xi) for (k, xi) in collection) @@ -80,15 +82,15 @@ list of `(τ, i)`. function nearest_neighbor(x, collection, k::Int; kwargs...) sims = [ (similarity(x, xi; kwargs...), i) - for (i, xi) in enumerate(collection) + for (i, xi) in enumerate(collection) ] - return partialsort!(sims, 1:k, rev=true) + return partialsort!(sims, 1:k, rev = true) end function nearest_neighbor(x, collection::Dict, k::Int; kwargs...) sims = [ (similarity(x, xi; kwargs...), i) - for (i, xi) in collection + for (i, xi) in collection ] - return partialsort!(sims, 1:k, rev=true) + return partialsort!(sims, 1:k, rev = true) end diff --git a/test/encoding.jl b/test/encoding.jl index d1b64e7..d3cdda8 100644 --- a/test/encoding.jl +++ b/test/encoding.jl @@ -1,12 +1,12 @@ @testset "encoding" begin hvs = BinaryHV.( [ - [1, 0, 0, 0, 0], - [1, 1, 0, 0, 0], - [1, 1, 1, 0, 0], - [1, 1, 1, 1, 0], - [1, 1, 1, 1, 1], - ] + [1, 0, 0, 0, 0], + [1, 1, 0, 0, 0], + [1, 1, 1, 0, 0], + [1, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + ] ) @testset "multiset" begin @@ -46,7 +46,7 @@ s = [1, 3, 4, 2, 5] t = [3, 4, 2, 1, 4] @test graph(hvs[s], hvs[t]) == Bool.([0, 0, 0, 0, 0]) - @test graph(hvs[s], hvs[t]; directed=true) == Bool.([1, 0, 0, 1, 0]) + @test graph(hvs[s], hvs[t]; directed = true) == Bool.([1, 0, 0, 1, 0]) @test_throws AssertionError graph(hvs[s], hvs[[1, 2, 3]]) end From 4ddd966658e7c1754f47ad78bd9725c2fca61418 Mon Sep 17 00:00:00 2001 From: michielstock Date: Thu, 23 Oct 2025 12:00:35 +0200 Subject: [PATCH 4/7] :broom: rename functions --- src/HyperdimensionalComputing.jl | 6 ++-- src/encoding.jl | 48 ++++++++++++++++---------------- test/encoding.jl | 16 +++++------ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/HyperdimensionalComputing.jl b/src/HyperdimensionalComputing.jl index 3415f02..d98fd51 100644 --- a/src/HyperdimensionalComputing.jl +++ b/src/HyperdimensionalComputing.jl @@ -31,9 +31,9 @@ export multiset, ngrams, graph, level, - level_encoder, - level_decoder, - levels_encoder_decoder + encodelevel, + decodelevel, + convertlevel include("inference.jl") export similarity, diff --git a/src/encoding.jl b/src/encoding.jl index a806127..ac49c3d 100644 --- a/src/encoding.jl +++ b/src/encoding.jl @@ -54,7 +54,7 @@ where `V` is the hypervector collection, `m` is the size of the hypervector coll - [`multibind`](@ref): Multibind encoding, binding-variant of this encoder """ -function multiset(vs::AbstractVector{<:T})::T where {T <: AbstractHV} +function multiset(vs::AbstractVector{<:T})::T where {T<:AbstractHV} return bundle(vs) end @@ -301,7 +301,7 @@ and `\\oplus` are the binding and bundling operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.hash_table.html) """ -function hashtable(keys::T, values::T) where {T <: AbstractVector{<:AbstractHV}} +function hashtable(keys::T, values::T) where {T<:AbstractVector{<:AbstractHV}} @assert length(keys) == length(values) "Number of keys and values aren't equal" return bundle(map(prod, zip(keys, values))) end @@ -369,7 +369,7 @@ and binding operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.cross_product.html) """ -function crossproduct(U::T, V::T) where {T <: AbstractVector{<:AbstractHV}} +function crossproduct(U::T, V::T) where {T<:AbstractVector{<:AbstractHV}} # TODO: This should be bundled without normalizing return bind(multiset(U), multiset(V)) end @@ -439,11 +439,11 @@ and shift operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.ngrams.html) """ -function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int = 3) +function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int=3) l = length(vs) p = l - n + 1 @assert 1 <= n <= length(vs) "`n` must be 1 ≤ n ≤ $l" - return bundle([bind([shift(vs[i + j], j) for j in 0:(n - 1)]) for i in 1:p]) + return bundle([bind([shift(vs[i+j], j) for j in 0:(n-1)]) for i in 1:p]) end """ @@ -485,7 +485,7 @@ hypervector collection, `i` is the position of the entry in the collection, and - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.graph.html) """ -function graph(source::T, target::T; directed::Bool = false) where {T <: AbstractVector{<:AbstractHV}} +function graph(source::T, target::T; directed::Bool=false) where {T<:AbstractVector{<:AbstractHV}} @assert length(source) == length(target) "`source` and `target` must be the same length" return hashtable(source, shift.(target, convert(Int, directed))) end @@ -501,7 +501,7 @@ Creates a set of level correlated hypervectors, where the first and last hyperve - `v::HV`: Base hypervector - `n::Int`: Number of levels (alternatively, provide a vector to be encoded) """ -function level(v::HV, n::Int) where {HV <: AbstractHV} +function level(v::HV, n::Int) where {HV<:AbstractHV} hvs = [v] p = 2 / n while length(hvs) < n @@ -511,14 +511,14 @@ function level(v::HV, n::Int) where {HV <: AbstractHV} return hvs end -level(HV::Type{<:AbstractHV}, n::Int; dims::Int = 10_000) = level(HV(dims), n) +level(HV::Type{<:AbstractHV}, n::Int; dims::Int=10_000) = level(HV(dims), n) level(HVv, vals::AbstractVector) = level(HVv, length(vals)) level(HVv, vals::UnitRange) = level(HVv, length(vals)) """ - level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) + encodelevel(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) Generate an encoding function based on `level`, for encoding numerical values. It returns a function that gives the corresponding hypervector for a given numerical input. @@ -533,12 +533,12 @@ that gives the corresponding hypervector for a given numerical input. numvalues = range(0, 2pi, 100) hvlevels = level(BipolarHV(), 100) -encoder = level_encoder(hvlevels, numvalues) +encoder = encodelevel(hvlevels, numvalues) encoder(pi/3) # hypervector that best represents this numerical value ``` """ -function level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound = false) +function encodelevel(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) @assert length(hvlevels) == length(numvalues) "HV levels do not match numerical values" # construct the encoder function encoder(x::Number) @@ -550,17 +550,17 @@ function level_encoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbo end """ - level_encoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) + encodelevel(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) -See `level_encoder`, same but provide lower (`a`) and upper (`b`) limit of the interval to be encoded. +See `encodelevel`, same but provide lower (`a`) and upper (`b`) limit of the interval to be encoded. """ -level_encoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound = false) = level_encoder(hvlevels, range(a, b, length(hvlevels)); testbound) +encodelevel(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) = encodelevel(hvlevels, range(a, b, length(hvlevels)); testbound) -level_encoder(HV, numvalues; testbound = false) = level_encoder(level(HV, length(numvalues)), numvalues; testbound) +encodelevel(HV, numvalues; testbound=false) = encodelevel(level(HV, length(numvalues)), numvalues; testbound) """ - level_decoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues) + decodelevel(hvlevels::AbstractVector{<:AbstractHV}, numvalues) Generate a decoding function based on `level`, for decoding numerical values. It returns a function that gives the numerical value for a given hypervector, based on similarity matching. @@ -574,12 +574,12 @@ that gives the numerical value for a given hypervector, based on similarity matc numvalues = range(0, 2pi, 100) hvlevels = level(BipolarHV(), 100) -decoder = level_decoder(hvlevels, numvalues) +decoder = decodelevel(hvlevels, numvalues) decoder(hvlevels[17]) # value that closely matches the corresponding HV ``` """ -function level_decoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues) +function decodelevel(hvlevels::AbstractVector{<:AbstractHV}, numvalues) @assert length(hvlevels) == length(numvalues) "HV levels do not match numerical values" # construct the decoder function decoder(hv::AbstractHV) @@ -589,14 +589,14 @@ function level_decoder(hvlevels::AbstractVector{<:AbstractHV}, numvalues) return decoder end -level_decoder(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number) = level_decoder(hvlevels, range(a, b, length(hvlevels))) +decodelevel(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number) = decodelevel(hvlevels, range(a, b, length(hvlevels))) -level_decoder(HV, numvalues; testbound = false) = level_decoder(level(HV, length(numvalues)), numvalues) +decodelevel(HV, numvalues; testbound=false) = decodelevel(level(HV, length(numvalues)), numvalues) """ - levels_encoder_decoder(hvlevels, numvals..., kwargs...) + convertlevel(hvlevels, numvals..., kwargs...) -Creates the `encoder` and `decoder` for a level incoding in one step. See `level_encoder` -and `level_decoder` for their respective documentations. +Creates the `encoder` and `decoder` for a level incoding in one step. See `encodelevel` +and `decodelevel` for their respective documentations. """ -levels_encoder_decoder(hvlevels, numvals...; kwargs...) = level_encoder(hvlevels, numvals...; kwargs...), level_decoder(hvlevels, numvals..., kwargs...) +convertlevel(hvlevels, numvals...; kwargs...) = encodelevel(hvlevels, numvals...; kwargs...), decodelevel(hvlevels, numvals..., kwargs...) diff --git a/test/encoding.jl b/test/encoding.jl index d3cdda8..e80a24a 100644 --- a/test/encoding.jl +++ b/test/encoding.jl @@ -1,12 +1,12 @@ @testset "encoding" begin hvs = BinaryHV.( [ - [1, 0, 0, 0, 0], - [1, 1, 0, 0, 0], - [1, 1, 1, 0, 0], - [1, 1, 1, 1, 0], - [1, 1, 1, 1, 1], - ] + [1, 0, 0, 0, 0], + [1, 1, 0, 0, 0], + [1, 1, 1, 0, 0], + [1, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + ] ) @testset "multiset" begin @@ -46,7 +46,7 @@ s = [1, 3, 4, 2, 5] t = [3, 4, 2, 1, 4] @test graph(hvs[s], hvs[t]) == Bool.([0, 0, 0, 0, 0]) - @test graph(hvs[s], hvs[t]; directed = true) == Bool.([1, 0, 0, 1, 0]) + @test graph(hvs[s], hvs[t]; directed=true) == Bool.([1, 0, 0, 1, 0]) @test_throws AssertionError graph(hvs[s], hvs[[1, 2, 3]]) end @@ -57,7 +57,7 @@ @test length(levels) == length(numvals) @test eltype(levels) <: BinaryHV - encoder, decoder = levels_encoder_decoder(levels, numvals) + encoder, decoder = convertlevel(levels, numvals) hv = encoder(1.467) @test hv isa BinaryHV x = decoder(hv) From c1bf67f722e2e099faa97c432731c9afcb581326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Vigil-V=C3=A1squez?= Date: Thu, 23 Oct 2025 12:06:17 +0200 Subject: [PATCH 5/7] style: format with Runic Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/encoding.jl | 24 ++++++++++++------------ test/encoding.jl | 14 +++++++------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/encoding.jl b/src/encoding.jl index ac49c3d..a7d4b26 100644 --- a/src/encoding.jl +++ b/src/encoding.jl @@ -54,7 +54,7 @@ where `V` is the hypervector collection, `m` is the size of the hypervector coll - [`multibind`](@ref): Multibind encoding, binding-variant of this encoder """ -function multiset(vs::AbstractVector{<:T})::T where {T<:AbstractHV} +function multiset(vs::AbstractVector{<:T})::T where {T <: AbstractHV} return bundle(vs) end @@ -301,7 +301,7 @@ and `\\oplus` are the binding and bundling operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.hash_table.html) """ -function hashtable(keys::T, values::T) where {T<:AbstractVector{<:AbstractHV}} +function hashtable(keys::T, values::T) where {T <: AbstractVector{<:AbstractHV}} @assert length(keys) == length(values) "Number of keys and values aren't equal" return bundle(map(prod, zip(keys, values))) end @@ -369,7 +369,7 @@ and binding operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.cross_product.html) """ -function crossproduct(U::T, V::T) where {T<:AbstractVector{<:AbstractHV}} +function crossproduct(U::T, V::T) where {T <: AbstractVector{<:AbstractHV}} # TODO: This should be bundled without normalizing return bind(multiset(U), multiset(V)) end @@ -439,11 +439,11 @@ and shift operations. - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.ngrams.html) """ -function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int=3) +function ngrams(vs::AbstractVector{<:AbstractHV}, n::Int = 3) l = length(vs) p = l - n + 1 @assert 1 <= n <= length(vs) "`n` must be 1 ≤ n ≤ $l" - return bundle([bind([shift(vs[i+j], j) for j in 0:(n-1)]) for i in 1:p]) + return bundle([bind([shift(vs[i + j], j) for j in 0:(n - 1)]) for i in 1:p]) end """ @@ -485,7 +485,7 @@ hypervector collection, `i` is the position of the entry in the collection, and - [Torchhd documentation](https://torchhd.readthedocs.io/en/stable/generated/torchhd.graph.html) """ -function graph(source::T, target::T; directed::Bool=false) where {T<:AbstractVector{<:AbstractHV}} +function graph(source::T, target::T; directed::Bool = false) where {T <: AbstractVector{<:AbstractHV}} @assert length(source) == length(target) "`source` and `target` must be the same length" return hashtable(source, shift.(target, convert(Int, directed))) end @@ -501,7 +501,7 @@ Creates a set of level correlated hypervectors, where the first and last hyperve - `v::HV`: Base hypervector - `n::Int`: Number of levels (alternatively, provide a vector to be encoded) """ -function level(v::HV, n::Int) where {HV<:AbstractHV} +function level(v::HV, n::Int) where {HV <: AbstractHV} hvs = [v] p = 2 / n while length(hvs) < n @@ -511,7 +511,7 @@ function level(v::HV, n::Int) where {HV<:AbstractHV} return hvs end -level(HV::Type{<:AbstractHV}, n::Int; dims::Int=10_000) = level(HV(dims), n) +level(HV::Type{<:AbstractHV}, n::Int; dims::Int = 10_000) = level(HV(dims), n) level(HVv, vals::AbstractVector) = level(HVv, length(vals)) level(HVv, vals::UnitRange) = level(HVv, length(vals)) @@ -538,7 +538,7 @@ encoder = encodelevel(hvlevels, numvalues) encoder(pi/3) # hypervector that best represents this numerical value ``` """ -function encodelevel(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound=false) +function encodelevel(hvlevels::AbstractVector{<:AbstractHV}, numvalues; testbound = false) @assert length(hvlevels) == length(numvalues) "HV levels do not match numerical values" # construct the encoder function encoder(x::Number) @@ -554,9 +554,9 @@ end See `encodelevel`, same but provide lower (`a`) and upper (`b`) limit of the interval to be encoded. """ -encodelevel(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound=false) = encodelevel(hvlevels, range(a, b, length(hvlevels)); testbound) +encodelevel(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number; testbound = false) = encodelevel(hvlevels, range(a, b, length(hvlevels)); testbound) -encodelevel(HV, numvalues; testbound=false) = encodelevel(level(HV, length(numvalues)), numvalues; testbound) +encodelevel(HV, numvalues; testbound = false) = encodelevel(level(HV, length(numvalues)), numvalues; testbound) """ @@ -591,7 +591,7 @@ end decodelevel(hvlevels::AbstractVector{<:AbstractHV}, a::Number, b::Number) = decodelevel(hvlevels, range(a, b, length(hvlevels))) -decodelevel(HV, numvalues; testbound=false) = decodelevel(level(HV, length(numvalues)), numvalues) +decodelevel(HV, numvalues; testbound = false) = decodelevel(level(HV, length(numvalues)), numvalues) """ convertlevel(hvlevels, numvals..., kwargs...) diff --git a/test/encoding.jl b/test/encoding.jl index e80a24a..c4f76f1 100644 --- a/test/encoding.jl +++ b/test/encoding.jl @@ -1,12 +1,12 @@ @testset "encoding" begin hvs = BinaryHV.( [ - [1, 0, 0, 0, 0], - [1, 1, 0, 0, 0], - [1, 1, 1, 0, 0], - [1, 1, 1, 1, 0], - [1, 1, 1, 1, 1], - ] + [1, 0, 0, 0, 0], + [1, 1, 0, 0, 0], + [1, 1, 1, 0, 0], + [1, 1, 1, 1, 0], + [1, 1, 1, 1, 1], + ] ) @testset "multiset" begin @@ -46,7 +46,7 @@ s = [1, 3, 4, 2, 5] t = [3, 4, 2, 1, 4] @test graph(hvs[s], hvs[t]) == Bool.([0, 0, 0, 0, 0]) - @test graph(hvs[s], hvs[t]; directed=true) == Bool.([1, 0, 0, 1, 0]) + @test graph(hvs[s], hvs[t]; directed = true) == Bool.([1, 0, 0, 1, 0]) @test_throws AssertionError graph(hvs[s], hvs[[1, 2, 3]]) end From cec0b2c448e78c0f02a7df38e918e3f101895435 Mon Sep 17 00:00:00 2001 From: michielstock Date: Thu, 30 Oct 2025 09:37:45 +0100 Subject: [PATCH 6/7] =?UTF-8?q?:wrench:=20sim=20matrix=20+=20=CE=B4=20alia?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/HyperdimensionalComputing.jl | 1 + src/inference.jl | 40 +++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/HyperdimensionalComputing.jl b/src/HyperdimensionalComputing.jl index d98fd51..ba099a1 100644 --- a/src/HyperdimensionalComputing.jl +++ b/src/HyperdimensionalComputing.jl @@ -37,6 +37,7 @@ export multiset, include("inference.jl") export similarity, + δ, nearest_neighbor diff --git a/src/inference.jl b/src/inference.jl index e293855..bd85a81 100644 --- a/src/inference.jl +++ b/src/inference.jl @@ -42,26 +42,38 @@ function similarity(x::AbstractVector, y::AbstractVector; method::Symbol) end end - -similarity(hvs::AbstractVector{<:AbstractHV}) = [similarity(hv1, hv2) for hv1 in hvs, hv2 in hvs] - """ - similarity(hvs::AbstractVector{<:AbstractHV}) + similarity(hvs::AbstractVector{<:AbstractHV}; [method]) Computes the similarity matrix for a vector of hypervectors using the similarity metrics defined by the pairwise version of `similarity`. """ -similarity(hvs::AbstractVector{<:AbstractHV}; method...) = [ - similarity(hv1, hv2; method...) - for hv1 in hvs, hv2 in hvs -] +function similarity(hvs::AbstractVector{<:AbstractHV}; kwargs...) + n = length(hvs) + S = zeros(n, n) + for i in 1:n + for j in i:n + S[i, j] = S[j, i] = similarity(hvs[i], hvs[j]; kwargs...) + end + end + return S +end + +""" + δ(x::AbstractHV, y::AbstractHV; [method]) + δ(x::AbstractHV; [method]) + δ(hvs::AbstractVector{<:AbstractHV}; [method]) + +Alias for `similarity`. See `similarity` for the main documentation. +""" +δ = similarity nearest_neighbor(x, collection; kwargs...) = maximum( - (similarity(x, xi; kwargs...), i, xi) + (similarity(x, xi; kwargs...), i, xi) for (i, xi) in enumerate(collection) -) + ) nearest_neighbor(x, collection::Dict; kwargs...) = maximum((similarity(x, xi; kwargs...), k, xi) for (k, xi) in collection) @@ -82,15 +94,15 @@ list of `(τ, i)`. function nearest_neighbor(x, collection, k::Int; kwargs...) sims = [ (similarity(x, xi; kwargs...), i) - for (i, xi) in enumerate(collection) + for (i, xi) in enumerate(collection) ] - return partialsort!(sims, 1:k, rev = true) + return partialsort!(sims, 1:k, rev=true) end function nearest_neighbor(x, collection::Dict, k::Int; kwargs...) sims = [ (similarity(x, xi; kwargs...), i) - for (i, xi) in collection + for (i, xi) in collection ] - return partialsort!(sims, 1:k, rev = true) + return partialsort!(sims, 1:k, rev=true) end From 8c0e237a392cb126c967dfa1c595e9967e51c321 Mon Sep 17 00:00:00 2001 From: michielstock Date: Thu, 30 Oct 2025 09:54:22 +0100 Subject: [PATCH 7/7] :wrench: homogen names + onearg sim --- src/inference.jl | 67 +++++++++++++++++++++++++++-------------------- test/inference.jl | 7 ++++- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/inference.jl b/src/inference.jl index bd85a81..2cd50d6 100644 --- a/src/inference.jl +++ b/src/inference.jl @@ -14,31 +14,31 @@ sim_jacc(u::AbstractVector, v::AbstractVector) = dot(u, v) / sum(ui + vi - ui * dist_hamming(u::AbstractVector, v::AbstractVector) = sum(abs(ui - vi) for (ui, vi) in zip(u, v)) -similarity(x::BipolarHV, y::BipolarHV) = sim_cos(x, y) -similarity(x::TernaryHV, y::TernaryHV) = sim_cos(x, y) -similarity(x::GradedBipolarHV, y::GradedBipolarHV) = sim_cos(x, y) -similarity(x::RealHV, y::RealHV) = sim_cos(x, y) -similarity(x::BinaryHV, y::BinaryHV) = sim_jacc(x, y) -similarity(x::GradedHV, y::GradedHV) = sim_jacc(x, y) +similarity(u::BipolarHV, v::BipolarHV) = sim_cos(u, v) +similarity(u::TernaryHV, v::TernaryHV) = sim_cos(u, v) +similarity(u::GradedBipolarHV, v::GradedBipolarHV) = sim_cos(u, v) +similarity(u::RealHV, v::RealHV) = sim_cos(u, v) +similarity(u::BinaryHV, v::BinaryHV) = sim_jacc(u, v) +similarity(u::GradedHV, v::GradedHV) = sim_jacc(u, v) """ - similarity(x::AbstractVector, y::AbstractVector; method::Symbol) + similarity(u::AbstractVector, v::AbstractVector; method::Symbol) Computes similarity between two (hyper)vectors using a `method` ∈ `[:cosine, :jaccard, :hamming]`. When no method is given, a default is used (cosine for vectors that can have negative elements and Jaccard for those that only have positive elements). """ -function similarity(x::AbstractVector, y::AbstractVector; method::Symbol) - @assert length(x) == length(y) "Vectors have to be of the same length" +function similarity(u::AbstractVector, v::AbstractVector; method::Symbol) + @assert length(u) == length(v) "Vectors have to be of the same length" methods = [:cosine, :jaccard, :hamming] @assert method ∈ methods "`method` has to be one of $methods" if method == :cosine - return sim_cos(x, y) + return sim_cos(u, v) elseif method == :jaccard - return sim_jacc(x, y) + return sim_jacc(u, v) elseif method == :hamming - return length(x) - dist_hamming(x, y) + return length(u) - dist_hamming(u, v) end end @@ -60,8 +60,17 @@ function similarity(hvs::AbstractVector{<:AbstractHV}; kwargs...) end """ - δ(x::AbstractHV, y::AbstractHV; [method]) - δ(x::AbstractHV; [method]) + similarity(u::AbstractHV; [method]) + +Create a function that computes the similarity between its argument and `u`` +using `similarity`, i.e. a function equivalent to `v -> similarity(u, v)`. +""" +similarity(u::AbstractHV; kwargs...) = v -> similarity(u, v; kwargs...) + + +""" + δ(u::AbstractHV, v::AbstractHV; [method]) + δ(u::AbstractHV; [method]) δ(hvs::AbstractVector{<:AbstractHV}; [method]) Alias for `similarity`. See `similarity` for the main documentation. @@ -69,19 +78,19 @@ Alias for `similarity`. See `similarity` for the main documentation. δ = similarity -nearest_neighbor(x, collection; kwargs...) = +nearest_neighbor(u::AbstractHV, collection; kwargs...) = maximum( - (similarity(x, xi; kwargs...), i, xi) + (similarity(u, xi; kwargs...), i, xi) for (i, xi) in enumerate(collection) - ) +) -nearest_neighbor(x, collection::Dict; kwargs...) = - maximum((similarity(x, xi; kwargs...), k, xi) for (k, xi) in collection) +nearest_neighbor(u::AbstractHV, collection::Dict; kwargs...) = + maximum((similarity(u, xi; kwargs...), k, xi) for (k, xi) in collection) """ - nearest_neighbor(x, collection[, k::Int]; kwargs...) + nearest_neighbor(u::AbstractHV, collection[, k::Int]; kwargs...) -Returns the element of `collection` that is most similar to `x`. +Returns the element of `collection` that is most similar to `u`. Function outputs `(τ, i, xi)` with `τ` the highest similarity value, `i` the index (or key if `collection` is a dictionary) of the closest @@ -91,18 +100,18 @@ for the similarity search. If a number `k` is given, the `k` closest neighbor are returned, as a sorted list of `(τ, i)`. """ -function nearest_neighbor(x, collection, k::Int; kwargs...) +function nearest_neighbor(u::AbstractHV, collection, k::Int; kwargs...) sims = [ - (similarity(x, xi; kwargs...), i) - for (i, xi) in enumerate(collection) + (similarity(u, xi; kwargs...), i) + for (i, xi) in enumerate(collection) ] - return partialsort!(sims, 1:k, rev=true) + return partialsort!(sims, 1:k, rev = true) end -function nearest_neighbor(x, collection::Dict, k::Int; kwargs...) +function nearest_neighbor(u::AbstractHV, collection::Dict, k::Int; kwargs...) sims = [ - (similarity(x, xi; kwargs...), i) - for (i, xi) in collection + (similarity(u, xi; kwargs...), i) + for (i, xi) in collection ] - return partialsort!(sims, 1:k, rev=true) + return partialsort!(sims, 1:k, rev = true) end diff --git a/test/inference.jl b/test/inference.jl index 80ace3c..1d83c94 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -7,6 +7,7 @@ y = BinaryHV([false, false, false, true]) @test similarity(x, y) ≈ 1 / 3 ≈ sim_jacc(x.v, y.v) + @test similarity(x, y) == δ(x)(y) end @testset "GradedHV" begin @@ -14,7 +15,7 @@ y = GradedHV([0.9, 0.8, 0.1, 0.3]) @test similarity(x, y) ≈ sim_jacc(x.v, y.v) ≈ dot(x.v, y.v) / sum(xi + yi - xi * yi for (xi, yi) in zip(x, y)) - + @test similarity(x, y) == δ(x)(y) end @testset "BipolarHV" begin @@ -25,6 +26,7 @@ yd = collect(y) @test similarity(x, y) ≈ sim_cos(x, y) ≈ dot(xd, yd) / norm(xd) / norm(yd) + @test similarity(x, y) == δ(x)(y) end @testset "TernaryHV" begin @@ -35,6 +37,7 @@ yd = collect(y) @test similarity(x, y) ≈ sim_cos(x.v, y.v) ≈ dot(xd, yd) / norm(xd) / norm(yd) + @test similarity(x, y) == δ(x)(y) end @testset "GradedBipolarHV" begin @@ -45,6 +48,7 @@ yd = collect(y) @test similarity(x, y) ≈ sim_cos(x.v, y.v) ≈ dot(xd, yd) / norm(xd) / norm(yd) + @test similarity(x, y) == δ(x)(y) end @testset "RealHV" begin @@ -55,6 +59,7 @@ yd = collect(y) @test similarity(x, y) ≈ sim_cos(x.v, y.v) ≈ dot(xd, yd) / norm(xd) / norm(yd) + @test similarity(x, y) == δ(x)(y) end @testset "Similarity matrix" begin