Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
34 changes: 0 additions & 34 deletions .github/workflows/ci-julia-nightly.yml

This file was deleted.

41 changes: 36 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
name: CI
on:
- push
- pull_request
workflow_dispatch:
push:
branches:
- master
tags: '*'
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
Expand All @@ -19,17 +28,39 @@ jobs:
arch:
- x64
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-runtest@latest
# env:
# JULIA_NUM_THREADS: 4
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v3
with:
file: lcov.info
test-nightly:
needs: test
name: Julia nightly - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
- windows-latest
arch:
- x64
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-runtest@latest
7 changes: 3 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ version = "0.3.1"

[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
PackageExtensionCompat = "65ce6f38-6b18-4e1d-a461-8949797d7930"
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"
TupleTools = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6"
VectorInterface = "409d34a3-91d5-4945-b6ec-7529ddf182d8"

[weakdeps]
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"

Expand All @@ -20,10 +19,10 @@ SparseArrayKitSparseArrays = "SparseArrays"
SparseArrayKitTensorOperations = "TensorOperations"

[compat]
Requires = "1"
PackageExtensionCompat = "1"
TensorOperations = "4"
TupleTools = "1.1"
VectorInterface = "0.3"
VectorInterface = "0.4.1"
julia = "1.6"

[extras]
Expand Down
7 changes: 1 addition & 6 deletions ext/SparseArrayKitSparseArrays.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
module SparseArrayKitSparseArrays

@static if isdefined(Base, :get_extension)
using SparseArrays: SparseMatrixCSC, nonzeros, rowvals, nzrange
else
using ..SparseArrays: SparseMatrixCSC, nonzeros, rowvals, nzrange
end

using SparseArrays: SparseMatrixCSC, nonzeros, rowvals, nzrange
using SparseArrayKit

Base.convert(T::Type{<:SparseArray}, a::SparseMatrixCSC) = T(a)
Expand Down
13 changes: 4 additions & 9 deletions ext/SparseArrayKitTensorOperations.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
module SparseArrayKitTensorOperations

@static if isdefined(Base, :get_extension)
using TensorOperations: TensorOperations, Index2Tuple, linearize
else
using ..TensorOperations: TensorOperations, Index2Tuple, linearize
end
const TO = TensorOperations

import TensorOperations as TO
using TensorOperations: Index2Tuple, linearize, numind
using SparseArrayKit: tensoradd!, tensortrace!, tensorcontract!, SparseArray

function TO.tensoradd!(C::SparseArray, pC::Index2Tuple,
Expand All @@ -30,12 +25,12 @@ function TO.tensorcontract!(C::SparseArray, pC::Index2Tuple,
end

function TO.tensoradd_type(TC, pA::Index2Tuple, ::SparseArray, ::Symbol)
return SparseArray{TC,sum(length.(pA))}
return SparseArray{TC,numind(pA)}
end

function TO.tensorcontract_type(TC, pC, ::SparseArray, pA, conjA,
::SparseArray, pB, conjB)
return SparseArray{TC,sum(length.(pC))}
return SparseArray{TC,numind(pC)}
end

end
19 changes: 2 additions & 17 deletions src/SparseArrayKit.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
module SparseArrayKit

using VectorInterface
using VectorInterface: _one
_isone(α::Number) = α === _one

using LinearAlgebra

using TupleTools

if !isdefined(Base, :get_extension)
using Requires
end

const IndexTuple{N} = NTuple{N,Int}

export SparseArray
Expand All @@ -25,16 +17,9 @@ include("linearalgebra.jl")

# Initialization
#-----------------
using PackageExtensionCompat
function __init__()
@static if !isdefined(Base, :get_extension)
@require TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2" begin
include("../ext/SparseArrayKitTensorOperations.jl")
end

@require SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" begin
include("../ext/SparseArrayKitSparseArrays.jl")
end
end
@require_extensions
end

end
4 changes: 2 additions & 2 deletions src/linearalgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,5 @@ function LinearAlgebra.mul!(C::SM, A::ASM, B::ASM, α::Number, β::Number)
return tensorcontract!(C, (1, 2), AA, CA, oindA, cindA, BB, CB, oindB, cindB, α, β)
end

LinearAlgebra.adjoint!(C::SM, A::SM) = tensoradd!(C, (2, 1), A, :C, true, false)
LinearAlgebra.transpose!(C::SM, A::SM) = tensoradd!(C, (2, 1), A, :N, true, false)
LinearAlgebra.adjoint!(C::SM, A::SM) = tensoradd!(C, (2, 1), A, :C, One(), Zero())
LinearAlgebra.transpose!(C::SM, A::SM) = tensoradd!(C, (2, 1), A, :N, One(), Zero())
13 changes: 6 additions & 7 deletions src/tensoroperations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
#-------------------------------
function tensoradd!(C::SparseArray{<:Any,N}, indCinA,
A::SparseArray{<:Any,N}, CA::Symbol,
α::Number=true, β::Number=true) where {N}
α::Number=One(), β::Number=One()) where {N}
(N == length(indCinA) && TupleTools.isperm(indCinA)) ||
throw(ArgumentError("Invalid permutation of length $N: $indCinA"))
size(C) == TupleTools.getindices(size(A), indCinA) ||
throw(DimensionMismatch("non-matching sizes while adding arrays"))

β == one(β) || (iszero(β) ? _zero!(C) : LinearAlgebra.lmul!(β, C))
scale!(C, β)
for (IA, vA) in A.data
IC = CartesianIndex(TupleTools.getindices(IA.I, indCinA))
C[IC] += α * (CA == :C ? conj(vA) : vA)
Expand All @@ -18,7 +17,7 @@ end

function tensortrace!(C::SparseArray{<:Any,NC}, indCinA,
A::SparseArray{<:Any,NA}, CA::Symbol, cindA1, cindA2,
α::Number=true, β::Number=false) where {NA,NC}
α::Number=One(), β::Number=Zero()) where {NA,NC}
NC == length(indCinA) ||
throw(ArgumentError("Invalid selection of $NC out of $NA: $indCinA"))
NA - NC == 2 * length(cindA1) == 2 * length(cindA2) ||
Expand All @@ -35,7 +34,7 @@ function tensortrace!(C::SparseArray{<:Any,NC}, indCinA,
sizeC == TupleTools.getindices(sizeA, indCinA) ||
throw(DimensionMismatch("non-matching sizes"))

β == one(β) || (iszero(β) ? _zero!(C) : LinearAlgebra.lmul!(β, C))
scale!(C, β)
for (IA, v) in A.data
IAc1 = CartesianIndex(TupleTools.getindices(IA.I, cindA1))
IAc2 = CartesianIndex(TupleTools.getindices(IA.I, cindA2))
Expand All @@ -50,7 +49,7 @@ end
function tensorcontract!(C::SparseArray, indCinoAB,
A::SparseArray, CA::Symbol, oindA, cindA,
B::SparseArray, CB::Symbol, oindB, cindB,
α::Number=true, β::Number=false)
α::Number=One(), β::Number=Zero())
pA = (oindA..., cindA...)
(length(pA) == ndims(A) && TupleTools.isperm(pA)) ||
throw(ArgumentError("invalid permutation of length $(ndims(A)): $pA"))
Expand All @@ -76,7 +75,7 @@ function tensorcontract!(C::SparseArray, indCinoAB,
TupleTools.getindices((osizeA..., osizeB...), indCinoAB) == size(C) ||
throw(DimensionMismatch("non-matching sizes in uncontracted dimensions"))

β == one(β) || (iszero(β) ? _zero!(C) : LinearAlgebra.lmul!(β, C))
scale!(C, β)

keysA = sort!(collect(nonzero_keys(A));
by=IA -> CartesianIndex(TupleTools.getindices(IA.I, cindA)))
Expand Down
53 changes: 21 additions & 32 deletions src/vectorinterface.jl
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
# Vector interface implementation for 'SparseArray
# Vector interface implementation for SparseArray
##################################################################
# zerovector & zerovector!!
#---------------------------
function VectorInterface.zerovector(x::SparseArray, ::Type{S}) where {S<:Number}
T = typeof(zero(eltype(x)) * zero(S))
return SparseArray{T}(undef, size(x))
return SparseArray{S}(undef, size(x))
end

VectorInterface.zerovector!(x::SparseArray) = _zero!(x)
VectorInterface.zerovector!!(x::SparseArray) = zerovector!(x)

# scale, scale! & scale!!
#-------------------------
VectorInterface.scale(x::SparseArray, α::Number) = _isone(α) ? copy(x) : x * α

function VectorInterface.scale(x::SparseArray, α::Number)
α === One() && return copy(x)
α === Zero() && return zerovector(x)
return x * α
end
function VectorInterface.scale!(x::SparseArray, α::Number)
_isone(α) && return x
iszero(α) && return zerovector!(x)
# typical occupation in a dict is about 30% from experimental testing
# the benefits of scaling all values (e.g. SIMD) largely outweight the extra work
scale!(x.data.vals, α)
Expand All @@ -25,24 +26,22 @@ function VectorInterface.scale!(y::SparseArray, x::SparseArray, α::Number)
ax = axes(x)
ay = axes(y)
ax == ay || throw(DimensionMismatch("output axes $ay differ from input axes $ax"))
_zero!(y)
zerovector!(y)
for (k, v) in nonzero_pairs(x)
y[k] = scale!!(v, α)
end
return y
end

function VectorInterface.scale!!(x::SparseArray, α::Number)
T = scalartype(x)
if promote_type(T, typeof(α)) <: T
α === One() && return x
if VectorInterface.promote_scale(x, α) <: scalartype(x)
return scale!(x, α)
else
return scale(x, α)
end
end
function VectorInterface.scale!!(y::SparseArray, x::SparseArray, α::Number)
T = scalartype(y)
if promote_type(T, typeof(α), scalartype(x)) <: T
if VectorInterface.promote_scale(x, α) <: scalartype(y)
return scale!(y, x, α)
else
return scale(x, α)
Expand All @@ -51,40 +50,30 @@ end

# add, add! & add!!
#-------------------
function VectorInterface.add(y::SparseArray,
x::SparseArray,
α::Number=_one,
β::Number=_one)
function VectorInterface.add(y::SparseArray, x::SparseArray, α::Number, β::Number)
ax = axes(x)
ay = axes(y)
ax == ay || throw(DimensionMismatch("output axes $ay differ from input axes $ax"))
T = promote_type(scalartype(y), scalartype(x), typeof(α), typeof(β))
T = VectorInterface.promote_add(y, x, α, β)
z = SparseArray{T}(undef, size(y))
scale!(z, y, β)
add!(z, x, α)
return z
end

function VectorInterface.add!(y::SparseArray,
x::SparseArray,
α::Number=_one,
β::Number=_one)
function VectorInterface.add!(y::SparseArray, x::SparseArray, α::Number, β::Number)
ax = axes(x)
ay = axes(y)
ax == ay || throw(DimensionMismatch("output axes $ay differ from input axes $ax"))
_isone(β) || (iszero(β) ? _zero!(y) : scale!(y, β))
scale!(y, β)
for (k, v) in nonzero_pairs(x)
increaseindex!(y, scale!!(v, α), k)
end
return y
end

function VectorInterface.add!!(y::SparseArray,
x::SparseArray,
α::Number=_one,
β::Number=_one)
T = scalartype(y)
if promote_type(T, typeof(α), typeof(β), scalartype(x)) <: T
function VectorInterface.add!!(y::SparseArray, x::SparseArray, α::Number, β::Number)
if VectorInterface.promote_add(y, x, α, β) <: scalartype(y)
return add!(y, x, α, β)
else
return add(y, x, α, β)
Expand All @@ -97,14 +86,14 @@ function VectorInterface.inner(x::SparseArray, y::SparseArray)
ax = axes(x)
ay = axes(y)
ax == ay || throw(DimensionMismatch("dot arguments have non-matching axes $ax and $ay"))
s = dot(zero(eltype(x)), zero(eltype(y)))
s = zero(VectorInterface.promote_inner(x, y))
if nonzero_length(x) >= nonzero_length(y)
@inbounds for I in nonzero_keys(x)
s += dot(x[I], y[I])
s += VectorInterface.inner(x[I], y[I])
end
else
@inbounds for I in nonzero_keys(y)
s += dot(x[I], y[I])
s += VectorInterface.inner(x[I], y[I])
end
end
return s
Expand Down
Loading