@@ -489,14 +489,6 @@ their component parts. A typical definition for an array that wraps a parent is
489489dataids (A:: AbstractQuasiArray ) = (UInt (objectid (A)),)
490490
491491
492- # # structured matrix methods ##
493- replace_in_print_matrix (A:: AbstractQuasiMatrix ,i,j,s:: AbstractString ) = s
494- replace_in_print_matrix (A:: AbstractQuasiVector ,i,j,s:: AbstractString ) = s
495-
496- # # Concatenation ##
497- eltypeof (x:: AbstractQuasiArray ) = eltype (x)
498-
499-
500492# # Reductions and accumulates ##
501493
502494function isequal (A:: AbstractQuasiArray , B:: AbstractQuasiArray )
@@ -512,17 +504,6 @@ function isequal(A::AbstractQuasiArray, B::AbstractQuasiArray)
512504 return true
513505end
514506
515- function cmp (A:: AbstractQuasiVector , B:: AbstractQuasiVector )
516- for (a, b) in zip (A, B)
517- if ! isequal (a, b)
518- return isless (a, b) ? - 1 : 1
519- end
520- end
521- return cmp (length (A), length (B))
522- end
523-
524- isless (A:: AbstractQuasiVector , B:: AbstractQuasiVector ) = cmp (A, B) < 0
525-
526507function (== )(A:: AbstractQuasiArray , B:: AbstractQuasiArray )
527508 if axes (A) != axes (B)
528509 return false
@@ -539,192 +520,6 @@ function (==)(A::AbstractQuasiArray, B::AbstractQuasiArray)
539520 return anymissing ? missing : true
540521end
541522
542- _lookup (ind, r:: Inclusion ) = ind
543-
544- _ind2sub (dims:: NTuple{N,Any} , ind) where N = (@_inline_meta ; _ind2sub_recurse (dims, ind- 1 ))
545- _ind2sub (inds:: QuasiIndices , ind) = (@_inline_meta ; _ind2sub_recurse (inds, ind- 1 ))
546- _ind2sub (inds:: Tuple{Inclusion{<:Any},AbstractUnitRange{<:Integer}} , ind) = (@_inline_meta ; _ind2sub_recurse (inds, ind- 1 ))
547- _ind2sub (inds:: Tuple{AbstractUnitRange{<:Integer},Inclusion{<:Any}} , ind) = (@_inline_meta ; _ind2sub_recurse (inds, ind- 1 ))
548-
549- function _ind2sub (inds:: Union{NTuple{N,Any},QuasiIndices{N}} , ind:: AbstractQuasiVector ) where N
550- M = length (ind)
551- t = ntuple (n-> similar (ind),Val (N))
552- for (i,idx) in pairs (IndexLinear (), ind)
553- sub = _ind2sub (inds, idx)
554- for j = 1 : N
555- t[j][i] = sub[j]
556- end
557- end
558- t
559- end
560-
561-
562- # # map over arrays ##
563-
564- # # transform any set of dimensions
565- # # dims specifies which dimensions will be transformed. for example
566- # # dims==1:2 will call f on all slices A[:,:,...]
567-
568- function mapslices (f, A:: AbstractQuasiArray ; dims)
569- if isempty (dims)
570- return map (f,A)
571- end
572- if ! isa (dims, AbstractQuasiVector)
573- dims = [dims... ]
574- end
575-
576- dimsA = [axes (A)... ]
577- ndimsA = ndims (A)
578- alldims = [1 : ndimsA;]
579-
580- otherdims = setdiff (alldims, dims)
581-
582- idx = Any[first (ind) for ind in axes (A)]
583- itershape = tuple (dimsA[otherdims]. .. )
584- for d in dims
585- idx[d] = Slice (axes (A, d))
586- end
587-
588- # Apply the function to the first slice in order to determine the next steps
589- Aslice = A[idx... ]
590- r1 = f (Aslice)
591- # In some cases, we can re-use the first slice for a dramatic performance
592- # increase. The slice itself must be mutable and the result cannot contain
593- # any mutable containers. The following errs on the side of being overly
594- # strict (#18570 & #21123).
595- safe_for_reuse = isa (Aslice, StridedArray) &&
596- (isa (r1, Number) || (isa (r1, AbstractQuasiArray) && eltype (r1) <: Number ))
597-
598- # determine result size and allocate
599- Rsize = copy (dimsA)
600- # TODO : maybe support removing dimensions
601- if ! isa (r1, AbstractQuasiArray) || ndims (r1) == 0
602- # If the result of f on a single slice is a scalar then we add singleton
603- # dimensions. When adding the dimensions, we have to respect the
604- # index type of the input array (e.g. in the case of OffsetArrays)
605- tmp = similar (Aslice, typeof (r1), reduced_indices (Aslice, 1 : ndims (Aslice)))
606- tmp[firstindex (tmp)] = r1
607- r1 = tmp
608- end
609- nextra = max (0 , length (dims)- ndims (r1))
610- if eltype (Rsize) == Int
611- Rsize[dims] = [size (r1)... , ntuple (d-> 1 , nextra)... ]
612- else
613- Rsize[dims] = [axes (r1)... , ntuple (d-> OneTo (1 ), nextra)... ]
614- end
615- R = similar (r1, tuple (Rsize... ,))
616-
617- ridx = Any[map (first, axes (R))... ]
618- for d in dims
619- ridx[d] = axes (R,d)
620- end
621-
622- concatenate_setindex! (R, r1, ridx... )
623-
624- nidx = length (otherdims)
625- indices = Iterators. drop (CartesianIndices (itershape), 1 ) # skip the first element, we already handled it
626- inner_mapslices! (safe_for_reuse, indices, nidx, idx, otherdims, ridx, Aslice, A, f, R)
627- end
628-
629- concatenate_setindex! (R, X:: AbstractQuasiArray , I... ) = (R[I... ] = X)
630-
631- # # 1 argument
632-
633- function map! (f:: F , dest:: AbstractQuasiArray , A:: AbstractQuasiArray ) where F
634- for (i,j) in zip (eachindex (dest),eachindex (A))
635- dest[i] = f (A[j])
636- end
637- return dest
638- end
639-
640- # map on collections
641- map (f, A:: AbstractQuasiArray ) = collect_similar (A, Generator (f,A))
642-
643- # # 2 argument
644- function map! (f:: F , dest:: AbstractQuasiArray , A:: AbstractQuasiArray , B:: AbstractQuasiArray ) where F
645- for (i, j, k) in zip (eachindex (dest), eachindex (A), eachindex (B))
646- dest[i] = f (A[j], B[k])
647- end
648- return dest
649- end
650-
651-
652- function map_n! (f:: F , dest:: AbstractQuasiArray , As) where F
653- for i = LinearIndices (As[1 ])
654- dest[i] = f (ith_all (i, As)... )
655- end
656- return dest
657- end
658-
659- map! (f:: F , dest:: AbstractQuasiArray , As:: AbstractQuasiArray... ) where {F} = map_n! (f, dest, As)
660-
661-
662- # # hashing AbstractQuasiArray ##
663-
664- function hash (A:: AbstractQuasiArray , h:: UInt )
665- h = hash (AbstractQuasiArray, h)
666- # Axes are themselves AbstractQuasiArrays, so hashing them directly would stack overflow
667- # Instead hash the tuple of firsts and lasts along each dimension
668- h = hash (map (first, axes (A)), h)
669- h = hash (map (last, axes (A)), h)
670- isempty (A) && return h
671-
672- # Goal: Hash approximately log(N) entries with a higher density of hashed elements
673- # weighted towards the end and special consideration for repeated values. Colliding
674- # hashes will often subsequently be compared by equality -- and equality between arrays
675- # works elementwise forwards and is short-circuiting. This means that a collision
676- # between arrays that differ by elements at the beginning is cheaper than one where the
677- # difference is towards the end. Furthermore, blindly choosing log(N) entries from a
678- # sparse array will likely only choose the same element repeatedly (zero in this case).
679-
680- # To achieve this, we work backwards, starting by hashing the last element of the
681- # array. After hashing each element, we skip `fibskip` elements, where `fibskip`
682- # is pulled from the Fibonacci sequence -- Fibonacci was chosen as a simple
683- # ~O(log(N)) algorithm that ensures we don't hit a common divisor of a dimension
684- # and only end up hashing one slice of the array (as might happen with powers of
685- # two). Finally, we find the next distinct value from the one we just hashed.
686-
687- # This is a little tricky since skipping an integer number of values inherently works
688- # with linear indices, but `findprev` uses `keys`. Hoist out the conversion "maps":
689- ks = keys (A)
690- key_to_linear = LinearIndices (ks) # Index into this map to compute the linear index
691- linear_to_key = vec (ks) # And vice-versa
692-
693- # Start at the last index
694- keyidx = last (ks)
695- linidx = key_to_linear[keyidx]
696- fibskip = prevfibskip = oneunit (linidx)
697- n = 0
698- while true
699- n += 1
700- # Hash the current key-index and its element
701- elt = A[keyidx]
702- h = hash (keyidx=> elt, h)
703-
704- # Skip backwards a Fibonacci number of indices -- this is a linear index operation
705- linidx = key_to_linear[keyidx]
706- linidx <= fibskip && break
707- linidx -= fibskip
708- keyidx = linear_to_key[linidx]
709-
710- # Only increase the Fibonacci skip once every N iterations. This was chosen
711- # to be big enough that all elements of small arrays get hashed while
712- # obscenely large arrays are still tractable. With a choice of N=4096, an
713- # entirely-distinct 8000-element array will have ~75% of its elements hashed,
714- # with every other element hashed in the first half of the array. At the same
715- # time, hashing a `typemax(Int64)`-length Float64 range takes about a second.
716- if rem (n, 4096 ) == 0
717- fibskip, prevfibskip = fibskip + prevfibskip, fibskip
718- end
719-
720- # Find a key index with a value distinct from `elt` -- might be `keyidx` itself
721- keyidx = findprev (! isequal (elt), A, keyidx)
722- keyidx === nothing && break
723- end
724-
725- return h
726- end
727-
728523
729524# #
730525# show
0 commit comments