Repository: PennyLaneAI/pennylane
Pull request #6980 (2025-02-19 17:13:32)

Fix certain `pylint=3.3.4` errors in source code
**Context:**

After running the latest version of `pylint` on our codebase, several important errors were highlighted. 

![pylint_heatmap](https://github.com/user-attachments/assets/f27059cf-b610-42a9-8e45-37ba8ee22cd1)

This PR updates the source code to follow best practices.

**Description of the Change:** 

Restructured some logic to fix certain errors.

**Benefits:** Improved code quality.

**Possible Drawbacks:** `too-many-arguments` and `too-many-positional-arguments` will not be addressed in this PR.

[sc-84776]

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6980?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.59%. Comparing base [(`b0b77fb`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/b0b77fbf791e0b332ea197cce05bbac529d3f4f1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`1a4303f`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/1a4303f9dbac1a2c63962da03cf186767aa604d9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 1 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #6980      +/-   ##
==========================================
- Coverage   99.59%   99.59%   -0.01%     
==========================================
  Files         481      481              
  Lines       45784    45781       -3     
==========================================
- Hits        45599    45596       -3     
  Misses        185      185              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6980?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 1651966ad..b7eeee2d5 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -316,6 +316,9 @@
 
 <h3>Internal changes ⚙️</h3>
 
+* Fix certain `pylint` errors in source code.
+  [(#6980)](https://github.com/PennyLaneAI/pennylane/pull/6980)
+
 * Remove `QNode.get_gradient_fn` from source code.
   [(#6898)](https://github.com/PennyLaneAI/pennylane/pull/6898)
   
diff --git a/pennylane/data/data_manager/__init__.py b/pennylane/data/data_manager/__init__.py
index 220a83e23..d38968b4e 100644
--- a/pennylane/data/data_manager/__init__.py
+++ b/pennylane/data/data_manager/__init__.py
@@ -185,7 +185,9 @@ def _download_datasets(  # pylint: disable=too-many-arguments
 
     if pbar is not None:
         if attributes is None:
-            file_sizes = [int(head(url).headers["Content-Length"]) for url in dataset_urls]
+            file_sizes = [
+                int(head(url, timeout=5).headers["Content-Length"]) for url in dataset_urls
+            ]
         else:
             # Can't get file sizes for partial downloads
             file_sizes = (None for _ in dataset_urls)
diff --git a/pennylane/devices/capabilities.py b/pennylane/devices/capabilities.py
index da1cc5aa5..2cfd30f91 100644
--- a/pennylane/devices/capabilities.py
+++ b/pennylane/devices/capabilities.py
@@ -34,7 +34,7 @@ class InvalidCapabilitiesError(Exception):
 
 def load_toml_file(file_path: str) -> dict:
     """Loads a TOML file and returns the parsed dict."""
-    with open(file_path, "r") as file:
+    with open(file_path, "r", encoding="utf-8") as file:
         return toml.load(file)
 
 
diff --git a/pennylane/ops/op_math/decompositions/two_qubit_unitary.py b/pennylane/ops/op_math/decompositions/two_qubit_unitary.py
index 666a592ba..5997ae2e5 100644
--- a/pennylane/ops/op_math/decompositions/two_qubit_unitary.py
+++ b/pennylane/ops/op_math/decompositions/two_qubit_unitary.py
@@ -202,15 +202,15 @@ def _su2su2_to_tensor_products(U):
     # case one of the elements of A is 0.
     # We use B1 unless division by 0 would cause all elements to be inf.
     use_B2 = math.allclose(A[0, 0], 0.0, atol=1e-6)
-    if not math.is_abstract(A):
-        B = C2 / math.cast_like(A[0, 1], 1j) if use_B2 else C1 / math.cast_like(A[0, 0], 1j)
-    elif qml.math.get_interface(A) == "jax":
+    if math.is_abstract(A) and qml.math.get_interface(A) == "jax":
         B = qml.math.cond(
             use_B2,
             lambda x: C2 / math.cast_like(A[0, 1], 1j),
             lambda x: C1 / math.cast_like(A[0, 0], 1j),
             [0],  # arbitrary value for x
         )
+    else:
+        B = C2 / math.cast_like(A[0, 1], 1j) if use_B2 else C1 / math.cast_like(A[0, 0], 1j)
 
     return math.convert_like(A, U), math.convert_like(B, U)
 
@@ -634,13 +634,14 @@ def two_qubit_decomposition(U, wires):
 
     # The next thing we will do is compute the number of CNOTs needed, as this affects
     # the form of the decomposition.
-    if not qml.math.is_abstract(U):
+    if qml.math.is_abstract(U):
+        # Currently we can only support 3 CNOT decomposition
+        num_cnots = 3
+    else:
         num_cnots = _compute_num_cnots(U)
 
     with qml.QueuingManager.stop_recording():
-        if qml.math.is_abstract(U):
-            decomp = _decomposition_3_cnots(U, wires)
-        elif num_cnots == 0:
+        if num_cnots == 0:
             decomp = _decomposition_0_cnots(U, wires)
         elif num_cnots == 1:
             decomp = _decomposition_1_cnot(U, wires)
diff --git a/pennylane/qchem/dipole.py b/pennylane/qchem/dipole.py
index f5d1c4c66..f8a10a186 100644
--- a/pennylane/qchem/dipole.py
+++ b/pennylane/qchem/dipole.py
@@ -447,6 +447,10 @@ def molecular_dipole(
     elif len(coordinates) == len(symbols):
         geometry_dhf = qml.numpy.array(coordinates)
         geometry_hf = coordinates.flatten()
+    else:
+        raise ValueError(
+            "The shape of the coordinates does not match the number of atoms in the molecule."
+        )
 
     if molecule.mult != 1:
         raise ValueError(
diff --git a/pennylane/qchem/vibrational/taylor_ham.py b/pennylane/qchem/vibrational/taylor_ham.py
index a56602464..88323055e 100644
--- a/pennylane/qchem/vibrational/taylor_ham.py
+++ b/pennylane/qchem/vibrational/taylor_ham.py
@@ -681,16 +681,16 @@ def taylor_hamiltonian(
         + (-0.013079509779221888+0j) * Z(0)
     )
     """
-    mapping.lower().strip()
-    if mapping not in ["binary", "unary"]:
-        raise ValueError(
-            f"Specified mapping {mapping}, is not found. Please use either 'binary' or 'unary' mapping."
-        )
     coeffs_arr = taylor_coeffs(pes, max_deg, min_deg)
     bose_op = taylor_bosonic(coeffs_arr, pes.freqs, is_local=pes.localized, uloc=pes.uloc)
+    mapping = mapping.lower().strip()
     if mapping == "binary":
         ham = binary_mapping(bose_operator=bose_op, n_states=n_states, wire_map=wire_map, tol=tol)
     elif mapping == "unary":
         ham = unary_mapping(bose_operator=bose_op, n_states=n_states, wire_map=wire_map, tol=tol)
+    else:
+        raise ValueError(
+            f"Specified mapping {mapping}, is not found. Please use either 'binary' or 'unary' mapping."
+        )
 
     return ham
diff --git a/pennylane/qcut/utils.py b/pennylane/qcut/utils.py
index 1e718332c..168531fcf 100644
--- a/pennylane/qcut/utils.py
+++ b/pennylane/qcut/utils.py
@@ -649,6 +649,7 @@ def fragment_graph(graph: MultiDiGraph) -> tuple[tuple[MultiDiGraph], MultiDiGra
     communication_graph = MultiDiGraph()
     communication_graph.add_nodes_from(range(len(subgraphs)))
 
+    start_fragment, end_fragment = 0, 0
     for node1, node2, _ in cut_edges:
         for i, subgraph in enumerate(subgraphs):
             if subgraph.has_node(node1):
diff --git a/pennylane/templates/subroutines/all_singles_doubles.py b/pennylane/templates/subroutines/all_singles_doubles.py
index 97d7c5305..7329753fc 100644
--- a/pennylane/templates/subroutines/all_singles_doubles.py
+++ b/pennylane/templates/subroutines/all_singles_doubles.py
@@ -223,15 +223,15 @@ class AllSinglesDoubles(Operation):
         Returns:
             tuple(int): shape of the tensor containing the circuit parameters
         """
-        if singles is None or not singles:
-            if doubles is None or not doubles:
-                raise ValueError(
-                    f"'singles' and 'doubles' lists can not be both empty;"
-                    f" got singles = {singles}, doubles = {doubles}"
-                )
-            if doubles is not None:
-                shape_ = (len(doubles),)
-        elif doubles is None:
+        if not singles and not doubles:
+            raise ValueError(
+                f"'singles' and 'doubles' lists can not be both empty;"
+                f" got singles = {singles}, doubles = {doubles}"
+            )
+
+        if not singles:
+            shape_ = (len(doubles),)
+        elif not doubles:
             shape_ = (len(singles),)
         else:
             shape_ = (len(singles) + len(doubles),)
diff --git a/pennylane/workflow/interfaces/jax_jit.py b/pennylane/workflow/interfaces/jax_jit.py
index 0eb8c0b4e..a54ced2ed 100644
--- a/pennylane/workflow/interfaces/jax_jit.py
+++ b/pennylane/workflow/interfaces/jax_jit.py
@@ -75,6 +75,7 @@ def _set_trainable_parameters_on_copy(tapes, params):
     return tuple(t.bind_new_parameters(a, t.trainable_params) for t, a in zip(tapes, params))
 
 
+# pylint: disable=no-member
 def _jax_dtype(m_type):
     if m_type == int:
         return jnp.int64 if jax.config.jax_enable_x64 else jnp.int32


Test patch:
diff --git a/tests/data/data_manager/test_dataset_access.py b/tests/data/data_manager/test_dataset_access.py
index b0b3fdcdc..a9ad394c6 100644
--- a/tests/data/data_manager/test_dataset_access.py
+++ b/tests/data/data_manager/test_dataset_access.py
@@ -181,7 +181,7 @@ def get_dataset_urls_mock(class_id, parameters):
     return []
 
 
-def head_mock(url):
+def head_mock(url, timeout=None):
     """Return a fake header stating content-length is 1."""
     return NamedTuple("Head", headers=dict)(headers={"Content-Length": 10000})
 
diff --git a/tests/qchem/test_dipole.py b/tests/qchem/test_dipole.py
index c49967039..55a50a05d 100644
--- a/tests/qchem/test_dipole.py
+++ b/tests/qchem/test_dipole.py
@@ -321,3 +321,14 @@ def test_gradient_expvalD():
     grad_finitediff = (d_2 - d_1) / 0.0002
 
     assert np.allclose(grad_qml[0][0], grad_finitediff)
+
+
+def test_molecular_dipole_error():
+    """Test that an error is raised if the shape of the coordinates does not match the number of atoms in the molecule."""
+
+    m = qml.qchem.Molecule(["H"], np.array([1.0, 2.0]))
+    with pytest.raises(
+        ValueError,
+        match="The shape of the coordinates does not match the number of atoms in the molecule.",
+    ):
+        qml.qchem.molecular_dipole(m)