Skip to content

Commit 7c68aec

Browse files
borisdevoslkdvosJutho
authored
Generalising functions to support GenericUnit (#291)
* change TensorKitSectors compat * add `unitspace` * one -> unit + left/rightone -> left/rightunit + isone -> isunit when concerning sectors + some more unitspace * change conj to dual for sectors * export new functions from TensorKitSectors * introduce `zerospace` to replace `zero` of a space * add `oneunit` for type of space * format * minor changes from #263 * use the const `TK` in tests where appropriate * format * `otimes` between tensormaps to account for `sectorscalartype` * generalise `unitspace` and `zerospace` * have `dim` of graded space depend on sectorscalartype * introduce `left/rightunitspace` * generalise `blocksectors` of homspace * generalise `scalar` * some exports * rename `insertleft/rightunit` and `removeunit` to `insertleft/rightunitspace` and `removeunitspace` * undo the renaming in the changelog * update `insertleft/rightunitspace` * add fixme * fix bad merge conflict choices * remove TensorKit shortcuts where they don't exist * another merge fix * more merge changes + add `IsingBimodule` to sectorlist * make spaces tests multifusion-friendly * apply tensors test changes from #263 * avoid `one` call in `rank` of tensormap * use `sum` in `dim` of `GradedSpace` * change one more `oneunit` to `unitspace` * changes to `(left/right)unitspace` and `zerospace` * remove module specification * Revert "remove module specification" This reverts commit 9ffa2dc. * keep `init` in `dim` and deal with repercussions in src * change `removeunitspace` to look for any unit instead of all * add `IsingBimodule` spaces and help functions for fusiontree tests * rewrite and reorganise fusiontree tests * rewrite and reorganise factorisation tests * rewrite and reorganise tensor tests * another float dim thingie correction * remove comment * bring back `insertleft/rightunit` and `removeunit` * remove dupe exports * fix `dim` and revert unnecessary Int converts * change blocksectors of empty productspace * fix gradedspace tests for product sectors including multifusion * bring back `Int` for truncrank dimensions * bump TensorKitSectors compat * suggestions to dim and pinv * fix doc error * add docstring to `unitspace` * fix `allequal` version dep * format * move code around + docstring extension * introduce `isunitspace` + use in `removeunit` * `isunit` change * clean up some tests + reduce git diff * have `blocksectors` always return a vector * rename + add todo * rewrite spaces tests to not specialise to fusion or multifusion * potential fix to isometry test * keep `@tensor` for symmetric braiding test * keep `@tensor` tests + add todo * fix return type of rank + test * more isunits * clean up `random_fusion` + remove redundant setup function * assert spaces are suitable for factorization tests + deal with float dim * get `insertleft/rightunit` working without explicit indices + edit some homspaces * reduce git diff * variable renames * tests for `is/left/rightunitspace` * format * get some trace tests working for multifusion + remove some todos * revert `isunit` change to `isone` * fix full trace test for fermions * return error for `fusiontrees` without coupled sector for `GenericUnit` * get double fusion tree tests working for `GenericUnit` * suggested source changes * factorisations test changes * let full and partial trace tests run for every sector * accidently removed part of test * Update test/tensors/factorizations.jl Co-authored-by: Lukas Devos <[email protected]> * code suggestions * get rid of try-catch block * code suggestions * format * deal with different error type thrown * make spaces compatible in isometric projections * Update test/symmetries/spaces.jl Co-authored-by: Jutho <[email protected]> * help `random_fusion` in while loop * `isa`'s, CI friendliness, braiding conditions and todos * import `HasBraiding` manually * Revert "import `HasBraiding` manually" This reverts commit 9fc7aa8. * export `HasBraiding` in TensorKit --------- Co-authored-by: Lukas Devos <[email protected]> Co-authored-by: Jutho <[email protected]>
1 parent 318f921 commit 7c68aec

File tree

18 files changed

+505
-279
lines changed

18 files changed

+505
-279
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Random = "1"
4343
SafeTestsets = "0.1"
4444
ScopedValues = "1.3.0"
4545
Strided = "2"
46-
TensorKitSectors = "=0.3.0, 0.3.2"
46+
TensorKitSectors = "0.3.3"
4747
TensorOperations = "5.1"
4848
Test = "1"
4949
TestExtras = "0.2,0.3"

src/TensorKit.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ module TensorKit
1111
export Sector, AbstractIrrep, Irrep
1212
export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, SimpleFusion, GenericFusion
1313
export UnitStyle, SimpleUnit, GenericUnit
14-
export BraidingStyle, SymmetricBraiding, Bosonic, Fermionic, Anyonic, NoBraiding
14+
export BraidingStyle, SymmetricBraiding, Bosonic, Fermionic, Anyonic, NoBraiding, HasBraiding
1515
export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep
1616
export ProductSector
1717
export FermionParity, FermionNumber, FermionSpin
18-
export FibonacciAnyon, IsingAnyon
18+
export FibonacciAnyon, IsingAnyon, IsingBimodule
1919

2020
# Export common vector space, fusion tree and tensor types
2121
export VectorSpace, Field, ElementarySpace # abstract vector spaces
@@ -34,7 +34,10 @@ export SpaceMismatch, SectorMismatch, IndexError # error types
3434
# Export general vector space methods
3535
export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual
3636
export unitspace, zerospace, oplus, ominus
37+
export leftunitspace, rightunitspace, isunitspace
3738
export insertleftunit, insertrightunit, removeunit
39+
40+
# partial order for vector spaces
3841
export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic
3942

4043
# Reexport methods for sectors and properties thereof

src/auxiliary/auxiliary.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,9 @@ function _copyto!(A::StridedView{<:Any, 1}, B::StridedView{<:Any, 2})
8080

8181
return A
8282
end
83+
84+
@static if VERSION < v"1.11" # TODO: remove once support for v1.10 is dropped
85+
_allequal(f, xs) = allequal(Base.Generator(f, xs))
86+
else
87+
_allequal(f, xs) = allequal(f, xs)
88+
end

src/fusiontrees/fusiontrees.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ function FusionTree{I}(
9292
uncoupled::NTuple{N}, coupled = unit(I), isdual = ntuple(Returns(false), N)
9393
) where {I <: Sector, N}
9494
FusionStyle(I) isa UniqueFusion ||
95-
error("fusion tree requires inner lines if `FusionStyle(I) <: MultipleFusion`")
95+
throw(ArgumentError("fusion tree requires inner lines if `FusionStyle(I) <: MultipleFusion`"))
9696
return FusionTree{I}(
9797
map(s -> convert(I, s), uncoupled), convert(I, coupled), isdual,
9898
_abelianinner(map(s -> convert(I, s), (uncoupled..., dual(coupled))))

src/fusiontrees/iterator.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ function fusiontrees(uncoupled::Tuple{Vararg{I}}, coupled::I) where {I <: Sector
1717
return fusiontrees(uncoupled, coupled, isdual)
1818
end
1919
function fusiontrees(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector}
20+
UnitStyle(I) isa GenericUnit && throw(
21+
ArgumentError("Must specify coupled sector when UnitStyle is GenericUnit.")
22+
)
2023
coupled = unit(I)
2124
isdual = ntuple(n -> false, length(uncoupled))
2225
return fusiontrees(uncoupled, coupled, isdual)

src/spaces/gradedspace.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,8 @@ field(::Type{<:GradedSpace}) = ℂ
9090
InnerProductStyle(::Type{<:GradedSpace}) = EuclideanInnerProduct()
9191

9292
function dim(V::GradedSpace)
93-
return reduce(
94-
+, dim(V, c) * dim(c) for c in sectors(V);
95-
init = zero(dim(unit(sectortype(V))))
96-
)
93+
init = 0 * dim(first(allunits(sectortype(V))))
94+
return sum(c -> dim(c) * dim(V, c), sectors(V); init = init)
9795
end
9896
function dim(V::GradedSpace{I, <:AbstractDict}, c::I) where {I <: Sector}
9997
return get(V.dims, isdual(V) ? dual(c) : c, 0)
@@ -123,8 +121,11 @@ function flip(V::GradedSpace{I}) where {I <: Sector}
123121
end
124122
end
125123

126-
unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 1)
127-
zerospace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 0)
124+
function unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector}
125+
return S(unit => 1 for unit in allunits(I))
126+
end
127+
zerospace(S::Type{<:GradedSpace}) = S()
128+
128129
# TODO: the following methods can probably be implemented more efficiently for
129130
# `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so
130131
# these generic definitions (which are still quite efficient) are good for now.

src/spaces/homspace.jl

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,16 @@ function blocksectors(W::HomSpace)
9393
N₁ = length(codom)
9494
N₂ = length(dom)
9595
I = sectortype(W)
96-
# TODO: is sort! still necessary now that blocksectors of ProductSpace is sorted?
97-
if N₂ <= N₁
98-
return sort!(filter!(c -> hasblock(codom, c), blocksectors(dom)))
96+
if N₁ == N₂ == 0
97+
return allunits(I)
98+
elseif N₁ == 0
99+
return filter!(isunit, collect(blocksectors(dom))) # module space cannot end in empty space
100+
elseif N₂ == 0
101+
return filter!(isunit, collect(blocksectors(codom)))
102+
elseif N₂ <= N₁
103+
return filter!(c -> hasblock(codom, c), collect(blocksectors(dom)))
99104
else
100-
return sort!(filter!(c -> hasblock(dom, c), blocksectors(codom)))
105+
return filter!(c -> hasblock(dom, c), collect(blocksectors(codom)))
101106
end
102107
end
103108

@@ -227,7 +232,7 @@ function TensorOperations.tensorcontract(
227232
end
228233

229234
"""
230-
insertleftunit(W::HomSpace, i=numind(W) + 1; conj=false, dual=false)
235+
insertleftunit(W::HomSpace, i = numind(W) + 1; conj = false, dual = false)
231236
232237
Insert a trivial vector space, isomorphic to the underlying field, at position `i`,
233238
which can be specified as an `Int` or as `Val(i)` for improved type stability.
@@ -237,7 +242,8 @@ See also [`insertrightunit`](@ref insertrightunit(::HomSpace, ::Val{i}) where {i
237242
[`removeunit`](@ref removeunit(::HomSpace, ::Val{i}) where {i}).
238243
"""
239244
function insertleftunit(
240-
W::HomSpace, ::Val{i} = Val(numind(W) + 1); conj::Bool = false, dual::Bool = false
245+
W::HomSpace, ::Val{i} = Val(numind(W) + 1);
246+
conj::Bool = false, dual::Bool = false
241247
) where {i}
242248
if i numout(W)
243249
return insertleftunit(codomain(W), Val(i); conj, dual) domain(W)
@@ -247,7 +253,7 @@ function insertleftunit(
247253
end
248254

249255
"""
250-
insertrightunit(W::HomSpace, i=numind(W); conj=false, dual=false)
256+
insertrightunit(W::HomSpace, i = numind(W); conj = false, dual = false)
251257
252258
Insert a trivial vector space, isomorphic to the underlying field, after position `i`,
253259
which can be specified as an `Int` or as `Val(i)` for improved type stability.
@@ -257,7 +263,8 @@ See also [`insertleftunit`](@ref insertleftunit(::HomSpace, ::Val{i}) where {i})
257263
[`removeunit`](@ref removeunit(::HomSpace, ::Val{i}) where {i}).
258264
"""
259265
function insertrightunit(
260-
W::HomSpace, ::Val{i} = Val(numind(W)); conj::Bool = false, dual::Bool = false
266+
W::HomSpace, ::Val{i} = Val(numind(W));
267+
conj::Bool = false, dual::Bool = false
261268
) where {i}
262269
if i numout(W)
263270
return insertrightunit(codomain(W), Val(i); conj, dual) domain(W)

src/spaces/productspace.jl

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
struct ProductSpace{S<:ElementarySpace, N} <: CompositeSpace{S}
3-
ProductSpace(spaces::NTuple{N, S}) where {S<:ElementarySpace, N}
2+
struct ProductSpace{S <: ElementarySpace, N} <: CompositeSpace{S}
3+
ProductSpace(spaces::NTuple{N, S}) where {S <: ElementarySpace, N}
44
55
A `ProductSpace` is a tensor product space of `N` vector spaces of type `S <: ElementarySpace`.
66
Only tensor products between [`ElementarySpace`](@ref) objects of the same type are allowed.
@@ -87,7 +87,7 @@ end
8787

8888
# more specific methods
8989
"""
90-
sectors(P::ProductSpace{S, N}) where {S<:ElementarySpace}
90+
sectors(P::ProductSpace{S, N}) where {S <: ElementarySpace}
9191
9292
Return an iterator over all possible combinations of sectors (represented as an
9393
`NTuple{N, sectortype(S)}`) that can appear within the tensor product space `P`.
@@ -151,7 +151,7 @@ function blocksectors(P::ProductSpace{S, N}) where {S, N}
151151
end
152152
bs = Vector{I}()
153153
if N == 0
154-
push!(bs, unit(I))
154+
append!(bs, allunits(I))
155155
elseif N == 1
156156
for s in sectors(P)
157157
push!(bs, first(s))
@@ -196,7 +196,7 @@ hasblock(P::ProductSpace, c::Sector) = !isempty(fusiontrees(P, c))
196196
blockdim(P::ProductSpace, c::Sector)
197197
198198
Return the total dimension of a coupled sector `c` in the product space, by summing over
199-
all `dim(P, s)` for all tuples of sectors `s::NTuple{N, <:Sector}` that can fuse to `c`,
199+
all `dim(P, s)` for all tuples of sectors `s::NTuple{N, Sector}` that can fuse to `c`,
200200
counted with the correct multiplicity (i.e. number of ways in which `s` can fuse to `c`).
201201
202202
See also [`hasblock`](@ref) and [`blocksectors`](@ref).
@@ -228,8 +228,8 @@ end
228228

229229
# unit element with respect to the monoidal structure of taking tensor products
230230
"""
231-
one(::S) where {S<:ElementarySpace} -> ProductSpace{S, 0}
232-
one(::ProductSpace{S}) where {S<:ElementarySpace} -> ProductSpace{S, 0}
231+
one(::S) where {S <: ElementarySpace} -> ProductSpace{S, 0}
232+
one(::ProductSpace{S}) where {S <: ElementarySpace} -> ProductSpace{S, 0}
233233
234234
Return a tensor product of zero spaces of type `S`, i.e. this is the unit object under the
235235
tensor product operation, such that `V ⊗ one(V) == V`.
@@ -248,7 +248,7 @@ fuse(P::ProductSpace{S, 0}) where {S <: ElementarySpace} = unitspace(S)
248248
fuse(P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...)
249249

250250
"""
251-
insertleftunit(P::ProductSpace, i::Int=length(P) + 1; conj=false, dual=false)
251+
insertleftunit(P::ProductSpace, i::Int = length(P) + 1; conj = false, dual = false)
252252
253253
Insert a trivial vector space, isomorphic to the underlying field, at position `i`,
254254
which can be specified as an `Int` or as `Val(i)` for improved type stability.
@@ -260,7 +260,18 @@ function insertleftunit(
260260
P::ProductSpace, ::Val{i} = Val(length(P) + 1);
261261
conj::Bool = false, dual::Bool = false
262262
) where {i}
263-
u = unitspace(spacetype(P))
263+
N = length(P)
264+
I = sectortype(P)
265+
if UnitStyle(I) isa SimpleUnit
266+
u = unitspace(spacetype(P))
267+
else
268+
N > 0 || throw(ArgumentError("cannot insert a sensible unit space in the empty product space"))
269+
if i == N + 1
270+
u = rightunitspace(P[N])
271+
else
272+
u = leftunitspace(P[i])
273+
end
274+
end
264275
if dual
265276
u = TensorKit.dual(u)
266277
end
@@ -271,7 +282,7 @@ function insertleftunit(
271282
end
272283

273284
"""
274-
insertrightunit(P::ProductSpace, i=lenght(P); conj=false, dual=false)
285+
insertrightunit(P::ProductSpace, i = length(P); conj = false, dual = false)
275286
276287
Insert a trivial vector space, isomorphic to the underlying field, after position `i`,
277288
which can be specified as an `Int` or as `Val(i)` for improved type stability.
@@ -283,7 +294,14 @@ function insertrightunit(
283294
P::ProductSpace, ::Val{i} = Val(length(P));
284295
conj::Bool = false, dual::Bool = false
285296
) where {i}
286-
u = unitspace(spacetype(P))
297+
N = length(P)
298+
I = sectortype(P)
299+
if UnitStyle(I) isa SimpleUnit
300+
u = unitspace(spacetype(P))
301+
else
302+
N > 0 || throw(ArgumentError("cannot insert a sensible unit space in the empty product space"))
303+
u = rightunitspace(P[i])
304+
end
287305
if dual
288306
u = TensorKit.dual(u)
289307
end
@@ -305,7 +323,8 @@ and [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i}
305323
"""
306324
function removeunit(P::ProductSpace, ::Val{i}) where {i}
307325
1 i length(P) || _boundserror(P, i)
308-
isisomorphic(P[i], unitspace(P[i])) || _nontrivialspaceerror(P, i)
326+
I = sectortype(P)
327+
isunitspace(P[i]) || _nontrivialspaceerror(P, i)
309328
return ProductSpace{spacetype(P)}(TupleTools.deleteat(P.spaces, i))
310329
end
311330

src/spaces/vectorspaces.jl

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ generally, objects in linear monoidal categories.
4141
abstract type VectorSpace end
4242

4343
"""
44-
field(a) -> Type{𝔽<:Field}
45-
field(::Type{T}) -> Type{𝔽<:Field}
44+
field(a) -> Type{𝔽 <: Field}
45+
field(::Type{T}) -> Type{𝔽 <: Field}
4646
4747
Return the type of field over which object `a` (e.g. a vector space or a tensor) is defined.
4848
This also works in type domain.
@@ -130,10 +130,13 @@ Always returns `false` for spaces where `V == conj(V)`, i.e. vector spaces over
130130
""" isconj(::ElementarySpace)
131131

132132
"""
133-
unitspace(V::S) where {S<:ElementarySpace} -> S
133+
unitspace(V::S) where {S <: ElementarySpace} -> S
134134
135135
Return the corresponding vector space of type `S` that represents the trivial
136136
one-dimensional space, i.e. the space that is isomorphic to the corresponding field.
137+
For vector spaces where `I = sectortype(S)` has a semi-simple unit structure
138+
(`UnitStyle(I) == GenericUnit()`), this returns a multi-dimensional space corresponding to all unit sectors:
139+
`dim(unitspace(V), s) == 1` for all `s in allunits(I)`.
137140
138141
!!! note
139142
`unitspace(V)`is different from `one(V)`. The latter returns the empty product space
@@ -144,7 +147,7 @@ Base.oneunit(V::ElementarySpace) = unitspace(V)
144147
Base.oneunit(::Type{V}) where {V <: ElementarySpace} = unitspace(V)
145148

146149
"""
147-
zerospace(V::S) where {S<:ElementarySpace} -> S
150+
zerospace(V::S) where {S <: ElementarySpace} -> S
148151
149152
Return the corresponding vector space of type `S` that represents the zero-dimensional or empty space.
150153
This is the zero element of the direct sum of vector spaces.
@@ -154,6 +157,68 @@ zerospace(V::ElementarySpace) = zerospace(typeof(V))
154157
Base.zero(V::ElementarySpace) = zerospace(V)
155158
Base.zero(::Type{V}) where {V <: ElementarySpace} = zerospace(V)
156159

160+
"""
161+
leftunitspace(V::S) where {S <: ElementarySpace} -> S
162+
163+
Return the corresponding vector space of type `S` that represents the trivial
164+
one-dimensional space, i.e. the space that is isomorphic to the corresponding field. For vector spaces
165+
of type `GradedSpace{I}`, this one-dimensional space contains the unique left unit of the objects in `Sector` `I` present
166+
in the vector space.
167+
"""
168+
function leftunitspace(V::ElementarySpace)
169+
I = sectortype(V)
170+
if UnitStyle(I) isa SimpleUnit
171+
return unitspace(typeof(V))
172+
else
173+
!isempty(sectors(V)) || throw(ArgumentError("Cannot determine the left unit of an empty space"))
174+
_allequal(leftunit, sectors(V)) ||
175+
throw(ArgumentError("sectors of $V do not have the same left unit"))
176+
177+
sector = leftunit(first(sectors(V)))
178+
return spacetype(V)(sector => 1)
179+
end
180+
end
181+
182+
"""
183+
rightunitspace(V::S) where {S <: ElementarySpace} -> S
184+
185+
Return the corresponding vector space of type `ElementarySpace` that represents the trivial
186+
one-dimensional space, i.e. the space that is isomorphic to the corresponding field. For vector spaces
187+
of type `GradedSpace{I}`, this corresponds to the right unit of the objects in `Sector` `I` present
188+
in the vector space.
189+
"""
190+
function rightunitspace(V::ElementarySpace)
191+
I = sectortype(V)
192+
if UnitStyle(I) isa SimpleUnit
193+
return unitspace(typeof(V))
194+
else
195+
!isempty(sectors(V)) || throw(ArgumentError("Cannot determine the right unit of an empty space"))
196+
_allequal(rightunit, sectors(V)) ||
197+
throw(ArgumentError("sectors of $V do not have the same right unit"))
198+
199+
sector = rightunit(first(sectors(V)))
200+
return spacetype(V)(sector => 1)
201+
end
202+
end
203+
204+
"""
205+
isunitspace(V::S) where {S <: ElementarySpace} -> Bool
206+
207+
Return whether the elementary space `V` is a unit space, i.e. is isomorphic to the
208+
trivial one-dimensional space. For vector spaces of type `GradedSpace{I}` where `Sector` `I` has a
209+
semi-simple unit structure, this returns `true` if `V` is isomorphic to either the left, right or
210+
semi-simple unit space.
211+
"""
212+
function isunitspace(V::ElementarySpace)
213+
I = sectortype(V)
214+
return if isa(UnitStyle(I), SimpleUnit)
215+
isisomorphic(V, unitspace(V))
216+
else
217+
(dim(V) == 0 || !all(isunit, sectors(V))) && return false
218+
return true
219+
end
220+
end
221+
157222
"""
158223
⊕(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} -> S
159224
oplus(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} -> S

src/tensors/diagonal.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ function LinearAlgebra.pinv(d::DiagonalTensorMap; kwargs...)
306306
if iszero(atol)
307307
rtol = get(kwargs, :rtol, zero(real(T)))
308308
else
309-
rtol = sqrt(eps(real(float(oneunit(T))))) * length(d.data)
309+
rtol = sqrt(eps(real(float(one(T))))) * length(d.data)
310310
end
311311
pdata = let tol = max(atol, rtol * maximum(abs, d.data))
312312
map(x -> abs(x) < tol ? zero(x) : pinv(x), d.data)

0 commit comments

Comments
 (0)