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

Equality checking for tensor objects #252

Open
ACE07-Sev opened this issue Jul 27, 2024 · 4 comments
Open

Equality checking for tensor objects #252

ACE07-Sev opened this issue Jul 27, 2024 · 4 comments

Comments

@ACE07-Sev
Copy link

Is your feature request related to a problem?

At the moment, as far as I can tell, the tensor network objects don't have any implementations for __eq__, which results in python using the standard __eq__ from the builtins module. The issue is that this will not work properly given it would require every attribute to be equal.

For tensors and tensor networks, we only need to ensure the underlying tensor data (the matrices, shapes, and number of sites) are equal. The index string generation may cause different str values for identical indices. i.e.,

data = np.array([1, 1, 0, 0])
statevector = data/np.linalg.norm(data)

mps = MatrixProductState.from_dense(statevector)
tensor_core.tensor_compress_bond(mps[0], mps[1], max_bond=64)

repr(mps)
MatrixProductState(tensors=2, indices=3, L=2, max_bond=2)
Tensor(shape=(2, 1), inds=[k0, _735f7fAAAAk], tags={I0}),
backend=numpy, dtype=float64, data=array([[1.], [0.]])
Tensor(shape=(1, 2), inds=[_735f7fAAAAk, k1], tags={I1}),
backend=numpy, dtype=float64, data=array([[0.70710678, 0.70710678]])

And if you re-run the exact same thing, you may get a different MPS instance

MatrixProductState(tensors=2, indices=3, L=2, max_bond=2)
Tensor(shape=(2, 1), inds=[k0, _735f7fAAAAn], tags={I0}),
backend=numpy, dtype=float64, data=array([[1.], [0.]])
Tensor(shape=(1, 2), inds=[_735f7fAAAAn, k1], tags={I1}),
backend=numpy, dtype=float64, data=array([[0.70710678, 0.70710678]])

Which will cause in the equality to fail.

data = np.array([1, 1, 0, 0])
statevector = data/np.linalg.norm(data)

mps1 = MatrixProductState.from_dense(statevector)
tensor_core.tensor_compress_bond(mps1[0], mps1[1], max_bond=64)

mps2 = MatrixProductState.from_dense(statevector)
tensor_core.tensor_compress_bond(mps2[0], mps2[1], max_bond=64)

mps1 == mps2
False

Describe the solution you'd like

I am still quite new to quimb, but I believe implementing the __eq__ such that the underlying tensor data (the matrices) are equal should suffice. Essentially, do not require the inds to be equal since the string generation is not consistent.

Describe alternatives you've considered

The issue given I haven't made any mistakes in understanding (hopefully) is quite easy to fix. I don't think any other alternatives would be needed besides a concrete implementation of __eq__ dunder in the relevant base class (I imagine it would be TensorNetwork, or even just Tensor since TensorNetwork's tensors property is just a container of Tensor instances iirc).

Additional context

No response

@jcmgray
Copy link
Owner

jcmgray commented Aug 3, 2024

Hi @ACE07-Sev, thanks for the issue.

I think the solution here is probably a special method for the specific type of equality checking you want e.g. tn.equals_with_structure(other), maybe you could elaborate more the purpose?

I actully didn;y realize that python has a default __eq__ implementation. This should maybe be disabled.

The problem with implementing __eq__ is that its quite ambiguous want the desired behaviour should be:

  1. matching overall (i.e. outer) object
  2. element-wise matching object, a la numpy
  3. check current data arrays and index structure (up to their names) and tags?

E.g. for 1. what tolerance should be used ? and for 3., an extra wrinkle is this becomes a NP-hard problem to check if the two graphs of a tensor network are equivalent.

Some already implemented methods that might be of interest:

@ACE07-Sev
Copy link
Author

ACE07-Sev commented Aug 3, 2024

Greetings there dear Johnnie,

Hope you are well. Thank you very much for the prompt response, sir!

So, my suggestion would be to check the equality of the tensor data (the underlying numpy arrays) and the structure. Basically, do not check the equality of inds strings. If you wish to have a tolerance parameter for this you could do something tiny, i.e, tol=1e-8. I can write a function for you and see if it's something you would approve of and then we can discuss whether a PR would be needed.

I mean, a tensor network is basically an NDArray correct? We only care about the structure (shape) and element-wise values. The purpose for me personally was to check if two MatrixProductState instances were equivalent for testing purposes, aka representing the same state.

I would say instead of disabling __eq__ we should have a concrete implementation of it within the relevant class(es). I will have a careful look at the codes you kindly linked and will experiment a bit to see if they do the job and will update back here.

@jcmgray
Copy link
Owner

jcmgray commented Aug 3, 2024

So, my suggestion would be to check the equality of the tensor data (the underlying numpy arrays) and the structure. Basically, do not check the equality of inds strings. If you wish to have a tolerance parameter for this you could do something tiny, i.e, tol=1e-8. I can write a function for you and see if it's something you would approve of and then we can discuss whether a PR would be needed.

I don't think this would be a good fit for __eq__ for various reasons, including that the intention of == is really to be identical objects (not up to some tolerance that would also have to depend on data type precision etc). But you could write a specific method that checks e.g.

tna.geometry_hash(strict_index_order=True) == tnb.geometry_hash(strict_index_order=True)

and

all(do('allclose', x, y, **allclose_opts) for x, y in zip(tna.arrays, tnb.arrays)

and call it allclose_structure or something similarly explicit. This would be sufficient but not necessary to show that two TNs were approximately equal, and might be useful for debugging.

I mean, a tensor network is basically an NDArray correct? We only care about the structure (shape) and element-wise values. The purpose for me personally was to check if two MatrixProductState instances were equivalent for testing purposes, aka representing the same state.

A TN represents a NDArray, but the element-wise values are not known until contracted. Moreover the actual values depend on the network geometry, which one can arbitrarily permute, and there is also a gauge freedom meaning you can transform all the arrays in the TN without changing the overall object represented. For these reasons if you implemented __eq__ as above then there would still be many TNs which were equivalent but would evaluate to False.

My preference here would be to disable __eq__ but raise a helpful error that points to methods that should be used:

  • distance
  • geometry_hash
  • allclose_structure if implemented.

@ACE07-Sev
Copy link
Author

Greetings there,

Hope you are well sir. Apologies for the delay. May I ask what allclose_ops is in the above code?

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

No branches or pull requests

2 participants