Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement inner(x,A,y) for TTN via BP. #146

Closed
wants to merge 3 commits into from

Conversation

b-kloss
Copy link
Contributor

@b-kloss b-kloss commented Mar 19, 2024

This implements inner(x,A,y) for x,A,y all AbstractTTN with the same underlying graph, passing through the BP-backend. It addresses a performance issue of the previous implementation for certain TTN, leading to higher than necessary scaling in some situations (due to the traversal order). Thanks @JoeyT1994 for providing the contract_with_BP code.
Tests have been added to test_forms.jl to verify the correctness of the implementation.

This is not intended as a complete overhaul of the current implementations of inner, expect etc, which could be addressed in future PRs.

@b-kloss b-kloss changed the title Implement inner(x,A,y) for TTN via BP. Implement inner(x,A,y) for TTN via BP. Mar 19, 2024
O = O * ydag[v] * A[v] * x[v]
end
return O[]
blf = BilinearFormNetwork(A, ydag, x)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be using a form here, that is mostly meant for optimization.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is your goal to make a tensor network out of the network y, A, and x? If so, you can use disjoint_union.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as far as I understand the BilinearFormNetwork just stacks the network here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's do that without going through BilinearFormNetwork.

)
end

function contract_with_BP(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contract_with_BP isn't a good function name, what about overloading ITensors.contract(::Algorithm"bp", kwargs...) and running with contract(itn; alg="bp", kwargs...).

end

function contract_with_BP(
T::Type,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this part of the interface, why can you set the element type in this way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the right way to do it. Basically, due to the log in this implementation, we need to promote the scalars of which the element type is taken to complex. Then the idea was that contract_with_BP returns a complex scalar, unless you specify a type, in which case it converts the scalar to the specified type. This should of course be handled differently for a proper implementation as contract with BP backend.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I think contract_with_BP as it stands is too far away from being a library function, so we should think about how to deal with that before continuing forward with this PR.

Comment on lines +745 to +747
if !is_tree(partitioned_graph(pg)) && outputlevel > 0
println("Partitioned graph is not a tree, result will be approximate!!")
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this, I think this is inherent if someone selects that they are using BP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it may be good to allow to pass a flag like assert_exact=true, to check that the BP will indeed be exact for the contraction (for the case of contracting TTNs)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's think about the design of that, but I don't think we have to address that in this PR. I agree with the sentiment that we don't want to secretly perform approximate contractions but give users the impression that we are doing exact contractions.

for pv in partitionvertices(pg)
incoming_mts = incoming_messages(bp_cache, [pv])
local_state = ITensor[itn[v] for v in vertices(pg, pv)]
log_numerator += log(complex(ITensors.contract(vcat(incoming_mts, local_state))[]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log_numerator += log(complex(ITensors.contract(vcat(incoming_mts, local_state))[]))
log_numerator += log(complex(contract(vcat(incoming_mts, local_state))[]))

for pe in partitionedges(pg)
log_denominator += log(
complex(
ITensors.contract(vcat(message(bp_cache, pe), message(bp_cache, reverse(pe))))[]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ITensors.contract(vcat(message(bp_cache, pe), message(bp_cache, reverse(pe))))[]
contract(vcat(message(bp_cache, pe), message(bp_cache, reverse(pe))))[]

log_numerator, log_denominator = 0, 0
for pv in partitionvertices(pg)
incoming_mts = incoming_messages(bp_cache, [pv])
local_state = ITensor[itn[v] for v in vertices(pg, pv)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you use pg[pv] here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or factor(bp_cache, pv)?

end
return O[]
blf = BilinearFormNetwork(A, ydag, x)
blf_scalar_bp = contract_with_BP(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we just use contract here, then we can allow an alg keyword argument to get passed from inner, and users can control what algorithm is used to contract the network.

To handle partitioning in the more general case, something we could do is that instead of passing an ITensorNetwork and a partitioning, we pass a PartitionedGraph to contract, then it is up to the contract backends to contract the partitioned tensor network (either using or ignoring the partitioning).

@mtfishman mtfishman closed this Mar 19, 2024
@mtfishman
Copy link
Member

mtfishman commented Mar 19, 2024

Closing in favor of first writing a better designed contract(::AbstractITensorNetwork) function (and logcontract(::AbstractITensorNetwork) function) based on BP.

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.

2 participants