Skip to content

isdual for AbstractTensorMaps#429

Closed
Yue-Zhengyuan wants to merge 1 commit into
mainfrom
isdual-tensor
Closed

isdual for AbstractTensorMaps#429
Yue-Zhengyuan wants to merge 1 commit into
mainfrom
isdual-tensor

Conversation

@Yue-Zhengyuan
Copy link
Copy Markdown
Member

This PR adds a utility function isdual(x::AbstractTensorMap) that returns a Boolean vector indicating which spaces of a tensor map are dual spaces. For example:

x = randn(ℂ^2  (ℂ^3)'  (ℂ^1)' ^4)
isdual(x) # returns [0, 1, 0, 1] as a Boolean vector

It is mainly used to assert that arrows in a network are preserved, which I frequently need to do.

The name of the function can be changed if you feel that isdual must return a single Bool. I don't know how to meaningfully test it, either.

@Jutho
Copy link
Copy Markdown
Member

Jutho commented May 13, 2026

I am not a great fan of calling isdual on a tensor, since that might have other connotations as well. One way forward that I could see is the following, but I would certainly like the opinion of @lkdvos .

Firstly, we could have a method:

spaces(t::AbstractTensorMap) = (codomain(t)..., map(dual, domain(t))...)

and then you can simply use broadcasting to write isdual.(spaces(t)). Would that be a sufficient shorthand?

@Jutho
Copy link
Copy Markdown
Member

Jutho commented May 13, 2026

Also, in relation to this and #430 , while map( f, ::ProductSpace) now indeed returns a tuple, broadcasting f.(::ProductSpace) still returns a vector, so we probably should also fix the broadcasting behavior of ProductSpace objects.

@Yue-Zhengyuan
Copy link
Copy Markdown
Member Author

Yue-Zhengyuan commented May 13, 2026

you can simply use broadcasting to write isdual.(spaces(t)). Would that be a sufficient shorthand?

This will be convenient enough for me 👍. Or maybe even better, instead of write a new spaces, we can define how to iterate over TensorMapSpace.

broadcasting f.(::ProductSpace) still returns a vector

It appears to have a simple solution: explicitly define

Base.Broadcast.broadcastable(P::ProductSpace) = P.spaces

Otherwise it defaults to collect(x), resulting in a vector.

@lkdvos
Copy link
Copy Markdown
Member

lkdvos commented May 13, 2026

Summarizing my earlier comment, I would like something like this: isdual.(codomain(t)), isdual.(domain(t)) (with appropriate negations), but I don't have strong opinions on adding a utility function to replace that if this is really desired (if we can come up with a good name).

The following also would make sense to me: in Base we often have the pattern size(A, i) = size(A)[i], so if we have space(t, i) = space(t)[i] (which I think we already do?), I would be fine making isdual.(space(t)) broadcast over the codomain, dual(domain) spaces in a linear way.

@Yue-Zhengyuan
Copy link
Copy Markdown
Member Author

if we have space(t, i) = space(t)[i] (which I think we already do?), I would be fine making isdual.(space(t)) broadcast over the codomain, dual(domain) spaces in a linear way.

We do have space(t, i) = space(t)[i]. The only thing preventing isdual.(space(t)) is the lack of Base.iterate for HomSpace. Also be careful that dual(domain) will reverse the leg order in the domain, which I don't want in isdual.(space(t)).

@Jutho
Copy link
Copy Markdown
Member

Jutho commented May 13, 2026

Given that we already support getindex on HomSpace, I would be supportive of both broadcasting and iteration support for ProductSpace and HomSpace.

Can you implement the necessary functionality for this in the current PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants