Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "ITensorBase"
uuid = "4795dd04-0d67-49bb-8f44-b89c448a1dc7"
version = "0.13.10"
version = "0.13.11"
authors = ["ITensor developers <support@itensor.org> and contributors"]

[workspace]
Expand Down
14 changes: 14 additions & 0 deletions src/namedtensoroperator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,20 @@ function operator(a::AbstractNamedTensor, output, input)
return NamedTensorOperator(a, name.(output), name.(input))
end

# A plain tensor is a trivial (empty-pairing) operator, so an operator is the promotion of a
# non-operator tensor. `convert` wraps a plain tensor as a trivial operator and `promote_rule`
# makes the operator the common type, so `promote(state, operator)` returns two operators and a
# mixed collection such as `[state, operator]` builds a homogeneous `Vector{<:NamedTensorOperator}`
# instead of falling back to the abstract `typejoin`.
function Base.convert(::Type{O}, a::NamedTensor) where {O <: NamedTensorOperator}
return operator(a, (), ())::O
end
function Base.promote_rule(
::Type{<:NamedTensor{D}}, ::Type{O}
) where {D, O <: NamedTensorOperator{D}}
return O
end

# Operator-preserving contraction. Contracting two named arrays sums over their
# shared names, so the result keeps each operand's surviving output/input
# structure. A non-operator tensor contributes no pairs (all its names are
Expand Down
25 changes: 25 additions & 0 deletions test/test_operator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,28 @@ end
@test Pmat * Pmat' ≈ A
@test Pmat * unnamed(state(Pinv)) ≈ I(n)
end

@testset "operator/state promotion" begin
t = NamedTensor(randn(2, 2), ("i", "j"))
o = operator(randn(2, 2), ("i",), ("j",))
O = typeof(o)

# A plain tensor promotes to the operator type (either argument order); the operator is
# the common type, so `promote` returns two operators.
@test promote_type(typeof(t), O) == O
@test promote_type(O, typeof(t)) == O
@test typeof.(promote(t, o)) == (O, O)

# `convert` wraps a plain tensor as a trivial empty-pairing operator, losslessly, and is a
# no-op on an operator.
to = convert(O, t)
@test to isa NamedTensorOperator
@test isempty(outputnames(to)) && isempty(inputnames(to))
@test state(to) === t
@test convert(O, o) === o

# A heterogeneous collection therefore homogenizes to the operator type.
v = [t, o]
@test eltype(v) == O
@test all(x -> x isa NamedTensorOperator, v)
end