QNGOptimizer LinAlgErr: Singlular Matrix

I am getting a LinAlgError: Singular matrix when trying to run QNGOptimizer on my circuit.
Here is the code for making the circuit and the hamiltonian:

dev = qml.device("default.qubit", wires=8)
connectivity = [(1, 2), (0, 6), (2, 3), (0, 7), (3, 4), (0, 1), (4, 5), (0, 2), (5, 6), (0, 3), (6, 7), (0, 4), (7, 1), (0, 5)]

def twoQubit(params, wires=8):
    for (index, (i, j)) in enumerate(connectivity):
        start = index * 15

        qml.U3(params[start], params[start+1], params[start+2], wires=i)
        qml.U3(params[start+3], params[start+4], params[start+5], wires=j)
        
        qml.CNOT(wires=[j, i])

        qml.RZ(params[start+6], wires=i)
        qml.RY(params[start+7], wires=j)

        qml.CNOT(wires=[i, j])

        qml.RY(params[start+8], wires=j)

        qml.CNOT(wires=[j, i])

        qml.U3(params[start+9], params[start+10], params[start+11], wires=i)
        qml.U3(params[start+12], params[start+13], params[start+14], wires=j)
op_list = [(qml.pauli.utils.string_to_pauli_word(singleEntry)) for singleEntry in returnStringObservable(1, 8)]
H = qml.Hamiltonian([-1]*len(op_list), op_list)

@qml.qnode(dev)
def cost_fn(params):
    twoQubit(params)
    return qml.expval(H)
init_params = np.array(np.random.normal(0, 0.1, 15*14), requires_grad=True)

opt = qml.QNGOptimizer(stepsize=step_size, approx="block-diag")

opt.step_and_cost(cost_fn, init_params)

The error stack is:

----> 3 opt.step_and_cost(cost_fn, init_params)

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pennylane\optimize\qng.py:204, in QNGOptimizer.step_and_cost(self, qnode, grad_fn, recompute_tensor, metric_tensor_fn, *args, **kwargs)
    199     self.metric_tensor = self.metric_tensor + self.lam * qml.math.eye(
    200         size, like=_metric_tensor
    201     )
    203 g, forward = self.compute_grad(qnode, args, kwargs, grad_fn=grad_fn)
--> 204 new_args = np.array(self.apply_grad(g, args), requires_grad=True)
    206 if forward is None:
    207     forward = qnode(*args, **kwargs)

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pennylane\optimize\qng.py:278, in QNGOptimizer.apply_grad(self, grad, args)
    276 grad_flat = np.array(list(_flatten(grad)))
    277 x_flat = np.array(list(_flatten(args)))
--> 278 x_new_flat = x_flat - self.stepsize * np.linalg.solve(self.metric_tensor, grad_flat)
    279 return unflatten(x_new_flat, args)

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pennylane\numpy\wrapper.py:117, in tensor_wrapper.<locals>._wrapped(*args, **kwargs)
    114         tensor_kwargs["requires_grad"] = _np.any([i.requires_grad for i in tensor_args])
    116 # evaluate the original object
...
File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\numpy\linalg\linalg.py:89, in _raise_linalgerror_singular(err, flag)
     88 def _raise_linalgerror_singular(err, flag):
---> 89     raise LinAlgError("Singular matrix")

LinAlgError: Singular matrix

The optimizer runs well with other circuits but not this. I usually use qiskit but switched to pennylane because of its quantum natural gradient optimizer and would really appreciate any help here

My hermitian looks as follows:

  (-1) [X1]
+ (-1) [X2]
+ (-1) [X3]
+ (-1) [X4]
+ (-1) [X5]
+ (-1) [X6]
+ (-1) [X7]
+ (-1) [Z1 Z2]
+ (-1) [Z2 Z3]
+ (-1) [Z3 Z4]
+ (-1) [Z4 Z5]
+ (-1) [Z5 Z6]
+ (-1) [Z6 Z7]
+ (-1) [Z1 Z7]

Hi @omersajid9,

If you run print(np.round(qml.metric_tensor(cost_fn, approx="block-diag")(init_params), 8)), you will see that the matrix corresponding to your qnode is in fact singular.

If you look at our demo here you will notice that the metric tensor is not singular.

I think this might be an issue with how you use your parameters in your circuit. If you are able to simplify this you might find the root of the problem.

For instance, in the following circuit you will get a singular matrix because only the first parameter is being used:

dev2 = qml.device('default.qubit',wires=2)

@qml.qnode(dev2)
def circuit2(params):
    qml.RX(params[0],wires=0)
    qml.RY(params[0],wires=1)
    return qml.expval(qml.PauliZ(0)@qml.PauliY(1))

i_param = np.array([0.1,0.2],requires_grad=True)
print(qml.metric_tensor(circuit2, approx="block-diag")(i_param))

However if you change qml.RY(params[0],wires=1) to qml.RY(params[1],wires=1) your matrix is no longer singular.

Please let me know if this helps!

Hi, thank you for your response. Yes, it fixes the issue sometimes but I have noticed that just applying a single qubit gate (for ex. H) on all qubits infront of my ansatz gets the circuit running. Is there any special reason for this?

Hi @omersajid9,
I’m not sure why this is working for you. If you’re not using all of your parameters then you probably still get a singular matrix. However, if you’re adding parametrized gates where you use all of your parameters then maybe that’s why this fixes your issue.

In any case I’m glad you managed to solve your problem!