diff --git a/Project.toml b/Project.toml index 770428b..c022c78 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "RandomBooleanMatrices" uuid = "9ae346a0-3d16-5633-ad70-ddb60ab77eac" license = "MIT" authors = ["mkborregaard (0), diff(SparseArrays.getcolptr(m))) + +# The default number of trades per call: five per column that can trade. +_defaulttrades(m::SparseMatrixCSC) = 5 * length(_tradingcolumns(m)) + +function _curveball!(m::SparseMatrixCSC{Bool, Int}, rng = Random.GLOBAL_RNG, trades::Int = _defaulttrades(m)) + cols = _tradingcolumns(m) + C = length(cols) + C < 2 && return m mcs = min(2maximum(diff(m.colptr)), size(m, 1)) not_shared, shared = Vector{Int}(undef, mcs), Vector{Int}(undef, mcs) newa, newb = Vector{Int}(undef, mcs), Vector{Int}(undef, mcs) for rep ∈ 1:trades - A, B = rand(rng, 1:C,2) + A, B = cols[rand(rng, 1:C)], cols[rand(rng, 1:C)] # use views directly into the sparse matrix to avoid copying a, b = view(m.rowval, m.colptr[A]:m.colptr[A+1]-1), view(m.rowval, m.colptr[B]:m.colptr[B+1]-1) diff --git a/test/runtests.jl b/test/runtests.jl index 81399cc..486104c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,6 +66,25 @@ end # the generator warm-starts from an independent draw and preserves margins csm5, rsm5 = margins(m5) @test margins(rand(matrixrandomizer(m5, method = curveball, trades = 10))) == (csm5, rsm5) + + # a sparse matrix with many empty columns still mixes: trades are drawn among + # the non-empty columns, so the empty ones do not use them up + sp = spzeros(Bool, 6, 20_000) + for (k, j) in enumerate(randperm(MersenneTwister(5), 20_000)[1:60]) + sp[mod1(k, 6), j] = true + sp[mod1(k + 2, 6), j] = true + end + @test matrixrandomizer(sp, method = curveball).state.trades == 5 * 60 + spcsm, sprsm = margins(sp) + rmg = matrixrandomizer(sp, MersenneTwister(6), method = curveball) + draws = [rand(rmg) for _ in 1:20] + @test all(d -> margins(d) == (spcsm, sprsm), draws) + @test minimum(i -> count(draws[i] .& .!draws[i + 1]), 1:19) >= 10 + + # with fewer than two non-empty columns nothing can trade + single = spzeros(Bool, 4, 50) + single[2, 7] = true + @test randomize_matrix!(copy(single), method = curveball) == single end @testset "sis" begin