-
Notifications
You must be signed in to change notification settings - Fork 2.7k
quantum_info: add operator_schmidt_decomposition #15299
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
base: main
Are you sure you want to change the base?
Conversation
|
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 19238258836Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
ShellyGarion
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NatPath - thanks for your contribution to Qiskit. I think this will be a great addition to the qiskit quantum info module.
I have some preliminary comments on the documnetation.
qiskit/quantum_info/operators/operator_schmidt_decomposition.py
Outdated
Show resolved
Hide resolved
releasenotes/notes/add-operator-schmidt-decomposition-ea5cec089113124c.yaml
Outdated
Show resolved
Hide resolved
associate github account with the commit
associate github account with the commit
associate github account with the commit
associate github account with the commit
associate github account with the commit
…tors.predicates associate github account with the commit
…ition to __init__.py under quantum_info and its docs associate github account with the commit
associate github account with the commit
…function - minor fix
…function - minor fix again
…s PR, will publish later
| # Determine number of terms to keep | ||
| total_terms = len(sing_vals) # = min(dim_a*dim_a, dim_b*dim_b) | ||
| if k is None: | ||
| num = total_terms | ||
| else: | ||
| if not (isinstance(k, int) and k > 0): | ||
| raise QiskitError("`k` must be a positive integer if provided.") | ||
| num = min(k, total_terms) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When users request k Schmidt terms, the code (line 221) clips to available terms without checking if singular values represent real signal versus numerical noise. Can SVD return values like [5.2, 2.8, 0.02, 1.3e-14, 8.7e-15] where the last two are floating-point rounding errors, not actual operator features? If that's the case then will code treats all as equally valid, building Schmidt factors from pure noise? Is it possible that high-fidelity gates can incorrectly show Schmidt rank 4 instead of 1? Should we add numerical rank check at line 215 using threshold max_singular_value × machine_epsilon × matrix_dimension, then warn users if requested k exceeds numerically significant terms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you Debasmita for this review.
Q >
Can SVD return values like [5.2, 2.8, 0.02, 1.3e-14, 8.7e-15] where the last two are floating-point rounding errors, not actual operator features?
A >
It can and often (almost always) does.
Q >
If that's the case then will code treats all as equally valid, building Schmidt factors from pure noise?
A >
The different terms "importance" is dictated by the singular values. Notice how the actual matrices are constructed - absorbing the weight of the singular values into A and B. For instance, in your example, the last two terms contribution to the reconstruction is negligible.
Q >
Is it possible that high-fidelity gates can incorrectly show Schmidt rank 4 instead of 1?
A >
Notice the schmidt rank is not part of the output. It can be interpreted elsewhere, given some threshold for the singular values which determines what is considered as non-zero.
Q>
Should we add numerical rank check at line 215 using threshold max_singular_value × machine_epsilon × matrix_dimension, then warn users if requested k exceeds numerically significant terms.
A>
Maybe! I thought that this kind of check could be done by the user according to its needs, but I guess it can also fit here.
ShellyGarion
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NatPath - thanks for your contribution to Qiskit.
After trying to use this function, I think that perhaps the API should be simplified.
Perhaps you can provide a simple example of using your code?
| (sum of kept terms) mapped back to the **original** qubit order. | ||
|
|
||
| Returns: | ||
| dict: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this API should be simplified, currently it's not so easy to decompose a tensor-product unitary and obtain back the same matrix. See this example.
from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import Operator
from qiskit.circuit.library import UnitaryGate
num_qubits = 4
qc = QuantumCircuit(num_qubits)
mat1 = random_unitary(4, seed=1234).to_matrix()
mat2 = random_unitary(4, seed=5678).to_matrix()
mat = np.kron(mat1, mat2)
out_full = operator_schmidt_decomposition(mat, (0,1))
perm_matrix = out_full["permutation"]["matrix"]
a_factors = out_full["A_factors"][0]
b_factors = out_full["B_factors"][0]
up_from_factors = np.kron(a_factors, b_factors)
up_direct = perm_matrix @ up_from_factors @ perm_matrix.T
qc.append(UnitaryGate(a_factors), [0, 1])
qc.append(UnitaryGate(b_factors), [2, 3])
print (Operator(qc).equiv(Operator(mat))) # should be True
print(Operator(qc) == Operator(up_direct)) # should be True
| self, seed: int, n_qubits: int, subset_a: tuple[int, ...] | ||
| ): | ||
| """Exact reconstruction (full sum) for random unitaries.""" | ||
| unitary = np.array(random_unitary(2**n_qubits, seed=seed), dtype=complex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use here: random_unitary(2 ** n_qubits).to_matrix() to get the np.array
Summary
This PR adds a new utility function,
operator_schmidt_decomposition, toqiskit.quantum_info. It computes the operator Schmidt decomposition of ann‑qubit operator across an arbitrary bipartition.Highlights
API
in a permuted basis where qubits are ordered as
[Sc, S](complement first, then selected subset).partition:{"S": tuple, "Sc": tuple}permutation:{"new_order": tuple(Sc)+tuple(S), "matrix": P}singular_values: full spectrum (descending)A_factors,B_factors: Schmidt factors for kept terms in the permuted basistruncation: kept/discarded terms, Frobenius tail error, relative errorkgives the best rank‑kapproximation (Frobenius‑optimal) via SVD of the realigned matrix.Tests
U_perm == Σ kron(A_r, B_r)U == P^T U_perm P.Notes
[Sc, S]basis by design.Scope
qiskit/quantum_info/operators/operator_schmidt_decomposition.pytest/python/quantum_info/operators/test_operator_schmidt_decomposition.pyDetails and comments
AI assistance: Microsoft Copilot Chat (GPT‑5)