The deeper I dig into the basis system as part of #32, the more I seem to see reasons for an overhaul 😅. This is of course very related to #27 and #33 but different enough that I thought it helpful to have a dedicated issue. Currently it seems there's two methods at tension for checking whether bases are compatible: the samebases machinery versus the typesystem. This is easily illustrated with an example
using QuantumOpticsBase
k1 = basisstate(SpinBasis(1), 1)
k2 = basisstate(SpinBasis(3//2), 2)
k1 + k2
throws ERROR: QuantumInterface.IncompatibleBases()
meanwhile
using QuantumOpticsBase
k1 = basisstate(NLevelBasis(3), 1)
k2 = basisstate(NLevelBasis(4), 2)
k1 + k2
throws ERROR: DimensionMismatch: a has size (2,), b has size (3,), mismatch at dim 1
The relevant part isn't so much the error messages themselves, we can easily (and probably should) make the latter example throw IncompatibleBases, but in how these two situations are caught due to using the type system
+(a::Ket{B}, b::Ket{B}) where {B} = Ket(a.basis, a.data+b.data)
+(a::Ket, b::Ket) = throw(IncompatibleBases())
When differences in bases are encoded within their types as currently happens with SpinBasis when called created with an isbits type, then this pattern catches incompatible bases. However, if incompatible bases are not evident at the type level, for example when using BigInt which has isbits false, then a dynamic samebases check is required.
I think making Basis isbits could potentially to unlock performance benefits since this might allow StaticArrays to be used! Then downside is that this means one has to carefully subtype Basis so that equality of bases is fully encoded in type parameters for which isbits is true. I think this is certainly doable for all the bases collected or proposed in #33. Of course this would also be a major breaking change.
In either case, I think it makes sense to pick only one pattern, document it here, and try to stick to it in dependent packages.
The deeper I dig into the basis system as part of #32, the more I seem to see reasons for an overhaul 😅. This is of course very related to #27 and #33 but different enough that I thought it helpful to have a dedicated issue. Currently it seems there's two methods at tension for checking whether bases are compatible: the
samebasesmachinery versus the typesystem. This is easily illustrated with an examplethrows
ERROR: QuantumInterface.IncompatibleBases()meanwhile
throws
ERROR: DimensionMismatch: a has size (2,), b has size (3,), mismatch at dim 1The relevant part isn't so much the error messages themselves, we can easily (and probably should) make the latter example throw
IncompatibleBases, but in how these two situations are caught due to using the type systemWhen differences in bases are encoded within their types as currently happens with
SpinBasiswhen called created with anisbitstype, then this pattern catches incompatible bases. However, if incompatible bases are not evident at the type level, for example when usingBigIntwhich hasisbitsfalse, then a dynamicsamebasescheck is required.I think making
Basisisbitscould potentially to unlock performance benefits since this might allowStaticArraysto be used! Then downside is that this means one has to carefully subtypeBasisso that equality of bases is fully encoded in type parameters for whichisbitsis true. I think this is certainly doable for all the bases collected or proposed in #33. Of course this would also be a major breaking change.In either case, I think it makes sense to pick only one pattern, document it here, and try to stick to it in dependent packages.