@@ -335,7 +335,225 @@ function sparse_IJ_sorted!{Ti<:Integer}(I::AbstractVector{Ti}, J::AbstractVector
335
335
return SparseMatrixCSC (m, n, colptr, I, V)
336
336
end
337
337
338
- # # sparse() can take its inputs in unsorted order (the parent method is now in csparse.jl)
338
+ """
339
+ sparse(I, J, V,[ m, n, combine])
340
+
341
+ Create a sparse matrix `S` of dimensions `m x n` such that `S[I[k], J[k]] = V[k]`. The
342
+ `combine` function is used to combine duplicates. If `m` and `n` are not specified, they
343
+ are set to `maximum(I)` and `maximum(J)` respectively. If the `combine` function is not
344
+ supplied, `combine` defaults to `+` unless the elements of `V` are Booleans in which case
345
+ `combine` defaults to `|`. All elements of `I` must satisfy `1 <= I[k] <= m`, and all
346
+ elements of `J` must satisfy `1 <= J[k] <= n`. Numerical zeros in (`I`, `J`, `V`) are
347
+ retained as structural nonzeros.
348
+
349
+ For additional documentation and an expert driver, see `Base.SparseArrays.sparse!`.
350
+ """
351
+ function sparse {Tv,Ti<:Integer} (I:: AbstractVector{Ti} , J:: AbstractVector{Ti} , V:: AbstractVector{Tv} , m:: Integer , n:: Integer , combine)
352
+ coolen = length (I)
353
+ if length (J) != coolen || length (V) != coolen
354
+ throw (ArgumentError (string (" the first three arguments' lengths must match, " ,
355
+ " length(I) (=$(length (I)) ) == length(J) (= $(length (J)) ) == length(V) (= " ,
356
+ " $(length (V)) )" )))
357
+ end
358
+
359
+ if m == 0 || n == 0 || coolen == 0
360
+ if coolen != 0
361
+ if n == 0
362
+ throw (ArgumentError (" column indices J[k] must satisfy 1 <= J[k] <= n" ))
363
+ elseif m == 0
364
+ throw (ArgumentError (" row indices I[k] must satisfy 1 <= I[k] <= m" ))
365
+ end
366
+ end
367
+ SparseMatrixCSC (m, n, ones (Ti, n+ 1 ), Vector {Ti} (), Vector {Tv} ())
368
+ else
369
+ # Allocate storage for CSR form
370
+ csrrowptr = Vector {Ti} (m+ 1 )
371
+ csrcolval = Vector {Ti} (coolen)
372
+ csrnzval = Vector {Tv} (coolen)
373
+
374
+ # Allocate storage for the CSC form's column pointers and a necessary workspace
375
+ csccolptr = Vector {Ti} (n+ 1 )
376
+ klasttouch = Vector {Ti} (n)
377
+
378
+ # Allocate empty arrays for the CSC form's row and nonzero value arrays
379
+ # The parent method called below automagically resizes these arrays
380
+ cscrowval = Vector {Ti} ()
381
+ cscnzval = Vector {Tv} ()
382
+
383
+ sparse! (I, J, V, m, n, combine, klasttouch,
384
+ csrrowptr, csrcolval, csrnzval,
385
+ csccolptr, cscrowval, cscnzval )
386
+ end
387
+ end
388
+
389
+ """
390
+ sparse!{Tv,Ti<:Integer}(
391
+ I::AbstractVector{Ti}, J::AbstractVector{Ti}, V::AbstractVector{Tv},
392
+ m::Integer, n::Integer, combine, klasttouch::Vector{Ti},
393
+ csrrowptr::Vector{Ti}, csrcolval::Vector{Ti}, csrnzval::Vector{Tv},
394
+ [csccolptr::Vector{Ti}], [cscrowval::Vector{Ti}, cscnzval::Vector{Tv}] )
395
+
396
+ Parent of and expert driver for `sparse`; see `sparse` for basic usage. This method
397
+ allows the user to provide preallocated storage for `sparse`'s intermediate objects and
398
+ result as described below. This capability enables more efficient successive construction
399
+ of `SparseMatrixCSC`s from coordinate representations, and also enables extraction of an
400
+ unsorted-column representation of the result's transpose at no additional cost.
401
+
402
+ This method consists of three major steps: (1) Counting-sort the provided coordinate
403
+ representation into an unsorted-row CSR form including repeated entries. (2) Sweep through
404
+ the CSR form, simultaneously calculating the desired CSC form's column-pointer array,
405
+ detecting repeated entries, and repacking the CSR form with repeated entries combined;
406
+ this stage yields an unsorted-row CSR form with no repeated entries. (3) Counting-sort the
407
+ preceding CSR form into a fully-sorted CSC form with no repeated entries.
408
+
409
+ Input arrays `csrrowptr`, `csrcolval`, and `csrnzval` constitute storage for the
410
+ intermediate CSR forms and require `length(csrrowptr) >= m + 1`,
411
+ `length(csrcolval) >= length(I)`, and `length(csrnzval >= length(I))`. Input
412
+ array `klasttouch`, workspace for the second stage, requires `length(klasttouch) >= n`.
413
+ Optional input arrays `csccolptr`, `cscrowval`, and `cscnzval` constitute storage for the
414
+ returned CSC form `S`. `csccolptr` requires `length(csccolptr) >= n + 1`. If necessary,
415
+ `cscrowval` and `cscnzval` are automatically resized to satisfy
416
+ `length(cscrowval) >= nnz(S)` and `length(cscnzval) >= nnz(S)`; hence, if `nnz(S)` is
417
+ unknown at the outset, passing in empty vectors of the appropriate type (`Vector{Ti}()`
418
+ and `Vector{Tv}()` respectively) suffices, or calling the `sparse!` method
419
+ neglecting `cscrowval` and `cscnzval`.
420
+
421
+ On return, `csrrowptr`, `csrcolval`, and `csrnzval` contain an unsorted-column
422
+ representation of the result's transpose.
423
+
424
+ You may reuse the input arrays' storage (`I`, `J`, `V`) for the output arrays
425
+ (`csccolptr`, `cscrowval`, `cscnzval`). For example, you may call
426
+ `sparse!(I, J, V, csrrowptr, csrcolval, csrnzval, I, J, V)`.
427
+
428
+ For the sake of efficiency, this method performs no argument checking beyond
429
+ `1 <= I[k] <= m` and `1 <= J[k] <= n`. Use with care. Testing with `--check-bounds=yes`
430
+ is wise.
431
+
432
+ This method runs in `O(m, n, length(I))` time. The HALFPERM algorithm described in
433
+ F. Gustavson, "Two fast algorithms for sparse matrices: multiplication and permuted
434
+ transposition," ACM TOMS 4(3), 250-269 (1978) inspired this method's use of a pair of
435
+ counting sorts.
436
+
437
+ Performance note: As of January 2016, `combine` should be a functor for this method to
438
+ perform well. This caveat may disappear when the work in `jb/functions` lands.
439
+ """
440
+ function sparse! {Tv,Ti<:Integer} (I:: AbstractVector{Ti} , J:: AbstractVector{Ti} ,
441
+ V:: AbstractVector{Tv} , m:: Integer , n:: Integer , combine, klasttouch:: Vector{Ti} ,
442
+ csrrowptr:: Vector{Ti} , csrcolval:: Vector{Ti} , csrnzval:: Vector{Tv} ,
443
+ csccolptr:: Vector{Ti} , cscrowval:: Vector{Ti} , cscnzval:: Vector{Tv} )
444
+
445
+ # Compute the CSR form's row counts and store them shifted forward by one in csrrowptr
446
+ fill! (csrrowptr, 0 )
447
+ coolen = length (I)
448
+ @inbounds for k in 1 : coolen
449
+ Ik = I[k]
450
+ if 1 > Ik || m < Ik
451
+ throw (ArgumentError (" row indices I[k] must satisfy 1 <= I[k] <= m" ))
452
+ end
453
+ csrrowptr[Ik+ 1 ] += 1
454
+ end
455
+
456
+ # Compute the CSR form's rowptrs and store them shifted forward by one in csrrowptr
457
+ countsum = 1
458
+ csrrowptr[1 ] = 1
459
+ @inbounds for i in 2 : (m+ 1 )
460
+ overwritten = csrrowptr[i]
461
+ csrrowptr[i] = countsum
462
+ countsum += overwritten
463
+ end
464
+
465
+ # Counting-sort the column and nonzero values from J and V into csrcolval and csrnzval
466
+ # Tracking write positions in csrrowptr corrects the row pointers
467
+ @inbounds for k in 1 : coolen
468
+ Ik, Jk = I[k], J[k]
469
+ if 1 > Jk || n < Jk
470
+ throw (ArgumentError (" column indices J[k] must satisfy 1 <= J[k] <= n" ))
471
+ end
472
+ csrk = csrrowptr[Ik+ 1 ]
473
+ csrrowptr[Ik+ 1 ] = csrk+ 1
474
+ csrcolval[csrk] = Jk
475
+ csrnzval[csrk] = V[k]
476
+ end
477
+ # This completes the unsorted-row, has-repeats CSR form's construction
478
+
479
+ # Sweep through the CSR form, simultaneously (1) caculating the CSC form's column
480
+ # counts and storing them shifted forward by one in csccolptr; (2) detecting repeated
481
+ # entries; and (3) repacking the CSR form with the repeated entries combined.
482
+ #
483
+ # Minimizing extraneous communication and nonlocality of reference, primarily by using
484
+ # only a single auxiliary array in this step, is the key to this method's performance.
485
+ fill! (csccolptr, 0 )
486
+ fill! (klasttouch, 0 )
487
+ writek = 1
488
+ newcsrrowptri = 1
489
+ origcsrrowptri = 1
490
+ origcsrrowptrip1 = csrrowptr[2 ]
491
+ @inbounds for i in 1 : m
492
+ for readk in origcsrrowptri: (origcsrrowptrip1- 1 )
493
+ j = csrcolval[readk]
494
+ if klasttouch[j] < newcsrrowptri
495
+ klasttouch[j] = writek
496
+ if writek != readk
497
+ csrcolval[writek] = j
498
+ csrnzval[writek] = csrnzval[readk]
499
+ end
500
+ writek += 1
501
+ csccolptr[j+ 1 ] += 1
502
+ else
503
+ klt = klasttouch[j]
504
+ csrnzval[klt] = combine (csrnzval[klt], csrnzval[readk])
505
+ end
506
+ end
507
+ newcsrrowptri = writek
508
+ origcsrrowptri = origcsrrowptrip1
509
+ origcsrrowptrip1 != writek && (csrrowptr[i+ 1 ] = writek)
510
+ i < m && (origcsrrowptrip1 = csrrowptr[i+ 2 ])
511
+ end
512
+
513
+ # Compute the CSC form's colptrs and store them shifted forward by one in csccolptr
514
+ countsum = 1
515
+ csccolptr[1 ] = 1
516
+ @inbounds for j in 2 : (n+ 1 )
517
+ overwritten = csccolptr[j]
518
+ csccolptr[j] = countsum
519
+ countsum += overwritten
520
+ end
521
+
522
+ # Now knowing the CSC form's entry count, resize cscrowval and cscnzval if necessary
523
+ cscnnz = countsum - 1
524
+ length (cscrowval) < cscnnz && resize! (cscrowval, cscnnz)
525
+ length (cscnzval) < cscnnz && resize! (cscnzval, cscnnz)
526
+
527
+ # Finally counting-sort the row and nonzero values from the CSR form into cscrowval and
528
+ # cscnzval. Tracking write positions in csccolptr corrects the column pointers.
529
+ @inbounds for i in 1 : m
530
+ for csrk in csrrowptr[i]: (csrrowptr[i+ 1 ]- 1 )
531
+ j = csrcolval[csrk]
532
+ x = csrnzval[csrk]
533
+ csck = csccolptr[j+ 1 ]
534
+ csccolptr[j+ 1 ] = csck+ 1
535
+ cscrowval[csck] = i
536
+ cscnzval[csck] = x
537
+ end
538
+ end
539
+
540
+ SparseMatrixCSC (m, n, csccolptr, cscrowval, cscnzval)
541
+ end
542
+ function sparse! {Tv,Ti<:Integer} (I:: AbstractVector{Ti} , J:: AbstractVector{Ti} ,
543
+ V:: AbstractVector{Tv} , m:: Integer , n:: Integer , combine, klasttouch:: Vector{Ti} ,
544
+ csrrowptr:: Vector{Ti} , csrcolval:: Vector{Ti} , csrnzval:: Vector{Tv} ,
545
+ csccolptr:: Vector{Ti} )
546
+ sparse! (I, J, V, m, n, combine, klasttouch,
547
+ csrrowptr, csrcolval, csrnzval,
548
+ csccolptr, Vector {Ti} (), Vector {Tv} () )
549
+ end
550
+ function sparse! {Tv,Ti<:Integer} (I:: AbstractVector{Ti} , J:: AbstractVector{Ti} ,
551
+ V:: AbstractVector{Tv} , m:: Integer , n:: Integer , combine, klasttouch:: Vector{Ti} ,
552
+ csrrowptr:: Vector{Ti} , csrcolval:: Vector{Ti} , csrnzval:: Vector{Tv} )
553
+ sparse! (I, J, V, m, n, combine, klasttouch,
554
+ csrrowptr, csrcolval, csrnzval,
555
+ Vector {Ti} (n+ 1 ), Vector {Ti} (), Vector {Tv} () )
556
+ end
339
557
340
558
dimlub (I) = isempty (I) ? 0 : Int (maximum (I)) # least upper bound on required sparse matrix dimension
341
559
0 commit comments