question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

QiskitError: 'TwoQubitWeylDecomposition: failed to diagonalize M2' for matrix that passes is_unitary_matrix

See original GitHub issue

Information

  • Qiskit Terra version: 0.18.3
  • Python version: 3.8.10
  • Operating system: IBM Quantum Lab

What is the current behavior?

When transpiling a certain two-qubit unitary U, for which is_unitary_matrix(U) returns True the following error is given:

Traceback (most recent call last):
  File "/tmp/ipykernel_59/2962236792.py", line 16, in <module>
    transpile(qc, basis_gates=['u1', 'u2', 'u3', 'cx'])
  File "/opt/conda/lib/python3.8/site-packages/qiskit/compiler/transpiler.py", line 293, in transpile
    circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args)))
  File "/opt/conda/lib/python3.8/site-packages/qiskit/tools/parallel.py", line 132, in parallel_map
    return [task(values[0], *task_args, **task_kwargs)]
  File "/opt/conda/lib/python3.8/site-packages/qiskit/compiler/transpiler.py", line 380, in _transpile_circuit
    result = pass_manager.run(
  File "/opt/conda/lib/python3.8/site-packages/qiskit/transpiler/passmanager.py", line 216, in run
    return self._run_single_circuit(circuits, output_name, callback)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/transpiler/passmanager.py", line 272, in _run_single_circuit
    result = running_passmanager.run(circuit, output_name=output_name, callback=callback)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/transpiler/runningpassmanager.py", line 123, in run
    dag = self._do_pass(pass_, dag, passset.options)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/transpiler/runningpassmanager.py", line 154, in _do_pass
    dag = self._run_this_pass(pass_, dag)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/transpiler/runningpassmanager.py", line 166, in _run_this_pass
    new_dag = pass_.run(dag)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/transpiler/passes/basis/unroll_custom_definitions.py", line 73, in run
    rule = node.op.definition.data
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/instruction.py", line 221, in definition
    self._define()
  File "/opt/conda/lib/python3.8/site-packages/qiskit/extensions/unitary.py", line 114, in _define
    self.definition = two_qubit_cnot_decompose(self.to_matrix())
  File "/opt/conda/lib/python3.8/site-packages/qiskit/quantum_info/synthesis/two_qubit_decompose.py", line 917, in __call__
    target_decomposed = TwoQubitWeylDecomposition(target)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/quantum_info/synthesis/two_qubit_decompose.py", line 176, in __new__
    raise QiskitError("TwoQubitWeylDecomposition: failed to diagonalize M2")
QiskitError: 'TwoQubitWeylDecomposition: failed to diagonalize M2'

Steps to reproduce the problem

import numpy as np
from qiskit import QuantumCircuit, transpile

U = np.array([[ 7.06170298e-01+0.00000000e+00j,  1.32212929e-01+6.29474509e-02j,
   6.92734343e-01+0.00000000e+00j, -2.82359525e-15-4.90767745e-15j],
 [ 4.07469350e-02+0.00000000e+00j,  5.16761273e-01-5.62190168e-01j,
  -8.90793989e-02+1.54254824e-01j,  6.19281841e-01+0.00000000e+00j],
 [-7.06868520e-01+0.00000000e+00j,  1.61702735e-01+3.06787860e-02j,
   6.86928856e-01+8.83837054e-03j,  3.60355709e-02-1.91994210e-05j],
 [ 3.04662537e-04+0.00000000e+00j, -3.89640632e-01+4.65347802e-01j,
   3.17696502e-02-1.24220607e-01j,  7.83075412e-01-4.45458978e-02j]])

qc = QuantumCircuit(2)
qc.unitary(U, [0, 1])

transpile(qc, basis_gates=['u1', 'u2', 'u3', 'cx'])

What is the expected behavior?

Either an error is thrown about U not being unitary when qc.unitary is called, or the decomposition should not error.

Suggested solutions

The tolerances in TwoQubitWeylDecomposition should be adjusted to match the default tolerances in is_unitary_matrix or viceversa. Another possibility is (provided that U is close enough to be unitary) to find the closest unitary to U, so that the decomposition goes through.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
levbishopcommented, Oct 12, 2021

One way to get a nearby unitary might be something like:

T, Z = scipy.linalg.schur(U, output='complex')
U = Z @ np.diag(np.diag(T)/abs(np.diag(T))) @ Z.T.conj()

For the given example U the update is of the order 3.E-10:

In [136]: np.abs(U - Z @ np.diag(np.diag(T)/abs(np.diag(T))) @ Z.T.conj())
Out[136]:
array([[3.98988636e-11, 1.07464975e-10, 1.12342394e-10, 1.24830829e-10],
       [3.07231030e-10, 1.04934953e-10, 3.28459841e-10, 3.40176682e-10],
       [7.75431909e-11, 9.01091112e-11, 1.74218352e-10, 2.08428305e-10],
       [1.80731083e-10, 1.70747937e-10, 1.62545168e-10, 2.41630737e-10]])

I think (but I’m not schur) that changing the exception to a warning and normalizing D = D/abs(D) in a similar way after line 177 https://github.com/Qiskit/qiskit-terra/blob/06795bfa88f5bcb2c89d9d7a7621daa115a5cb18/qiskit/quantum_info/synthesis/two_qubit_decompose.py#L177 would similarly give the Weyl decomposition of a nearby unitary.

If so, the loop termination condition would need some thought, or we could dust off your slightly slower randomization-free diagonalization routine @jakelishman

1reaction
jakelishmancommented, Oct 11, 2021

That particular bit of code changed (albeit only slightly) in #6896, but I just tested it, and it produces the same effect on main.

Changing the tolerances is pretty difficult to implement correctly, because the floating-point operations being considered in the two cases are quite drastically different, and unfortunately the matrix being unitary isn’t even an absolute guarantee that this code will succeed. I’m not sure what’s meant by “the closest unitary”, though - the concept of distance is pretty hard to nail down.

In this case, the matrix actually relatively far from unitary - in this case I find:

In [1]: import numpy as np
   ...: np.set_printoptions(linewidth=np.inf)

In [2]: U = np.array([[ 7.06170298e-01+0.00000000e+00j,  1.32212929e-01+6.29474509e-02j,
   ...:    6.92734343e-01+0.00000000e+00j, -2.82359525e-15-4.90767745e-15j],
   ...:  [ 4.07469350e-02+0.00000000e+00j,  5.16761273e-01-5.62190168e-01j,
   ...:   -8.90793989e-02+1.54254824e-01j,  6.19281841e-01+0.00000000e+00j],
   ...:  [-7.06868520e-01+0.00000000e+00j,  1.61702735e-01+3.06787860e-02j,
   ...:    6.86928856e-01+8.83837054e-03j,  3.60355709e-02-1.91994210e-05j],
   ...:  [ 3.04662537e-04+0.00000000e+00j, -3.89640632e-01+4.65347802e-01j,
   ...:    3.17696502e-02-1.24220607e-01j,  7.83075412e-01-4.45458978e-02j]])
   ...: U @ U.conj().T
Out[2]:
array([[ 1.00000000e+00-7.35425978e-19j, -1.39835678e-10-5.32752620e-10j, -6.92500259e-11+7.36638354e-12j,  9.68643395e-11+1.19605201e-10j],
       [-1.39835678e-10+5.32752620e-10j,  1.00000000e+00+8.53060339e-19j,  9.38072536e-11+1.07378981e-10j, -3.52907044e-10+2.10605527e-10j],
       [-6.92500259e-11-7.36638268e-12j,  9.38072536e-11-1.07378981e-10j,  1.00000000e+00-4.19915915e-19j, -8.63739358e-11-2.13175519e-10j],
       [ 9.68643395e-11-1.19605201e-10j, -3.52907044e-10-2.10605544e-10j, -8.63739358e-11+2.13175519e-10j,  1.00000000e+00-1.52318951e-17j]])

In [3]: np.abs(_)
Out[3]:
array([[1.00000000e+00, 5.50798848e-10, 6.96407186e-11, 1.53909403e-10],
       [5.50798848e-10, 1.00000000e+00, 1.42583471e-10, 4.10972103e-10],
       [6.96407185e-11, 1.42583471e-10, 1.00000000e+00, 2.30009258e-10],
       [1.53909403e-10, 4.10972112e-10, 2.30009258e-10, 1.00000000e+00]])

While it’s not a perfect fix, as a temporary measure, are you able to use a more precise form of U? Right now, the magnitudes of the errors makes it seem like immediate problem may be coming from only having the elements of U specified to 9 significant figures.

The error should be more descriptive, though. Also see #4159.

Read more comments on GitHub >

github_iconTop Results From Across the Web

qiskit.quantum_info.synthesis.two_qubit_decompose
(2, 2): raise QiskitError("euler_angles_1q: expected 2x2 matrix") phase ... QiskitError("TwoQubitWeylDecomposition: failed to diagonalize ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found