Skip to content

Conversation

alexanderivrii
Copy link
Member

@alexanderivrii alexanderivrii commented Aug 29, 2025

Summary

After we have bumped faer to the latest version (see #14873) and saw that it significantly improves the numerical accuracy of the SVD (see #14797), I have decided to reimplement the sine-cosine decomposition using faer instead of nalgebra, thus essentially making a full circle, as the original implementation by @mtreinish was also written using faer.

Details and comments

This is all a bit unnecessary since we are no longer using CSD for the Quantum Shannon Decomposition (and currently anywhere else in the code), but I believe that now we have significantly lower numerical errors when using CSD, and in addition I could remove using closest_unitary inside the algorithm. We do have a large number of python tests checking that CSD is correct.

@alexanderivrii alexanderivrii requested a review from a team as a code owner August 29, 2025 17:04
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

if s.diagonal().iter().any(|x| x.im != 0.) {
for j in 0..n {
let z = s[(j, j)];
let mut s: Vec<Complex64> = s.diagonal().column_vector().iter().copied().collect();
Copy link
Member Author

@alexanderivrii alexanderivrii Aug 29, 2025

Choose a reason for hiding this comment

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

This collection of diagonal elements of a matrix seems unnecessary complex, but I was not able to make it better, any ideas? More generally, I don't fully understand how to use DiagRef in faer (see https://docs.rs/faer/latest/faer/diag/type.DiagRef.html), in particular how to iterate over its elements (e.g., mat.diag().iter() does not work as there is no support for iteration). Am I missing something obvious?

Copy link
Member

Choose a reason for hiding this comment

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

I think the obvious thing is the faer api here isn't very clear. I looked at the source: https://docs.rs/faer/0.21.9/src/faer/diag/diagref.rs.html#4-6 and diagonal().column_vector().iter() is the correct way to get an iterator over the diagonal elements from a matrix (if using the .diagonal() method). I guess you could do something like (0..n).map(|i| mat[(i, i)]) instead.

@coveralls
Copy link

coveralls commented Aug 29, 2025

Pull Request Test Coverage Report for Build 17340333031

Warning: 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

  • 32 of 32 (100.0%) changed or added relevant lines in 1 file are covered.
  • 220 unchanged lines in 9 files lost coverage.
  • Overall coverage decreased (-0.03%) to 88.37%

Files with Coverage Reduction New Missed Lines %
crates/circuit/src/parameter/parameter_expression.rs 1 82.79%
crates/circuit/src/parameter/symbol_expr.rs 2 72.78%
crates/qasm2/src/lex.rs 5 91.75%
crates/qasm2/src/parse.rs 6 97.09%
crates/transpiler/src/passes/gate_direction.rs 10 96.3%
crates/transpiler/src/passes/gates_in_basis.rs 15 82.95%
qiskit/transpiler/preset_passmanagers/common.py 15 84.47%
qiskit/transpiler/target.py 17 94.52%
crates/transpiler/src/target/mod.rs 149 84.53%
Totals Coverage Status
Change from base Build 17325698704: -0.03%
Covered Lines: 91250
Relevant Lines: 103259

💛 - Coveralls

@ShellyGarion ShellyGarion added the Changelog: None Do not include in changelog label Sep 1, 2025
@ShellyGarion
Copy link
Member

ShellyGarion commented Sep 1, 2025

LGTM. Nice that it's working better now, but it's better to merge it after #14797

Copy link
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

I like this re implementation, and only have some minor comments.

let mut c: DVector<f64> = svd.singular_values.column(0).into_owned();
let svd = u00.svd().expect("Problem with SVD decomposition");
let l0 = svd.U().to_owned();
let r0 = svd.V().adjoint().to_owned();
Copy link
Member

Choose a reason for hiding this comment

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

adjoint here is the conjugate transpose and not the inverse?

let mut l0 = svd.u.unwrap();
let mut r0 = svd.v_t.unwrap();
let mut c: DVector<f64> = svd.singular_values.column(0).into_owned();
let svd = u00.svd().expect("Problem with SVD decomposition");
Copy link
Member

Choose a reason for hiding this comment

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

do you want to add verify_svd_decomp (or a similar code in faer):

fn verify_svd_decomp(

similar to what we did in:
debug_assert!(verify_svd_decomp(

let arr2 = res.2.as_ref().into_ndarray().to_pyarray(py);
let arr3 = res.3.as_ref().into_ndarray().to_pyarray(py);

Ok(((arr0, arr1), res.4, (arr2, arr3))
Copy link
Member

Choose a reason for hiding this comment

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

looking at this commit, it seems that instead of 0,1,2,3,4 it's better to call them l0,l1,r0,r1,thetas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants