Repository: PennyLaneAI/pennylane
Pull request #1174 (2021-03-26 21:53:22)

Drawing a ControlledQubitUnitary
**Context:**
We consider drawing a `ControlledQubitUnitary` using the circuit drawer, which raises an error due to having no special rule for drawing this operation.

**Description of the Change:**
Adds a rule for rendering the matrix and the operation.

**Benefits:**
Draws `ControlledQubitUnitary` instances.

**Possible Drawbacks:**
N/A

**Related GitHub Issues:**
Closes https://github.com/PennyLaneAI/pennylane/issues/1171

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [.github/CHANGELOG.md](/PennyLaneAI/pennylane/blob/master/.github/CHANGELOG.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://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=h1) Report
> Merging [#1174](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=desc) (8c906c2) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/0021c3feecf16745c70418ea5444197731c7943f?el=desc) (0021c3f) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #1174   +/-   ##
=======================================
  Coverage   98.12%   98.12%           
=======================================
  Files         144      144           
  Lines       10833    10844   +11     
=======================================
+ Hits        10630    10641   +11     
  Misses        203      203           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...ennylane/circuit\_drawer/representation\_resolver.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174/diff?src=pr&el=tree#diff-cGVubnlsYW5lL2NpcmN1aXRfZHJhd2VyL3JlcHJlc2VudGF0aW9uX3Jlc29sdmVyLnB5) | `99.33% <100.00%> (+0.04%)` | :arrow_up: |
| [pennylane/ops/qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174/diff?src=pr&el=tree#diff-cGVubnlsYW5lL29wcy9xdWJpdC5weQ==) | `98.30% <100.00%> (+<0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=footer). Last update [0021c3f...8c906c2](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1174?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 0ba6054d1..0d1493ef0 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -672,6 +672,10 @@
 
 <h3>Bug fixes</h3>
 
+* Fixes a bug where using the circuit drawer with a ``ControlledQubitUnitary``
+  operation raised an error.
+  [(#1174)](https://github.com/PennyLaneAI/pennylane/pull/1174)
+
 * Fixes a bug and a test where the ``QuantumTape.is_sampled`` attribute was not
   being updated.
   [(#1126)](https://github.com/PennyLaneAI/pennylane/pull/1126)
diff --git a/pennylane/circuit_drawer/representation_resolver.py b/pennylane/circuit_drawer/representation_resolver.py
index 0cc2c4641..cd04fe3ad 100644
--- a/pennylane/circuit_drawer/representation_resolver.py
+++ b/pennylane/circuit_drawer/representation_resolver.py
@@ -134,6 +134,23 @@ class RepresentationResolver:
 
         return "{}{}".format(symbol, idx)
 
+    @staticmethod
+    def _format_controlled_qubit_unitary(operation, symbol, cache):
+        """Format an operation that corresponds to a single matrix with controls.
+
+        Args:
+            operation (~.Operation): Operation that shall be formatted
+            symbol (str): The symbol that should be used to identify matrices
+            cache (List[numpy.ndarray]): The cache of already known matrices
+
+        Returns:
+            str: The formatted operation
+        """
+        mat = operation.U
+        idx = RepresentationResolver.index_of_array_or_append(mat, cache)
+
+        return "{}{}".format(symbol, idx)
+
     @staticmethod
     def _format_matrix_arguments(params, symbol, cache):
         """Format a sequence of matrix parameters.
@@ -351,6 +368,14 @@ class RepresentationResolver:
                 op, "U", self.unitary_matrix_cache
             )
 
+        elif base_name == "ControlledQubitUnitary":
+            if wire in op.control_wires:
+                return self.charset.CONTROL
+
+            representation = RepresentationResolver._format_controlled_qubit_unitary(
+                op, "U", self.unitary_matrix_cache
+            )
+
         elif base_name == "Hermitian":
             representation = RepresentationResolver._format_matrix_operation(
                 op, "H", self.hermitian_matrix_cache
diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py
index 5ed613ba2..92bbda28b 100644
--- a/pennylane/ops/qubit.py
+++ b/pennylane/ops/qubit.py
@@ -1907,6 +1907,11 @@ class ControlledQubitUnitary(QubitUnitary):
         target_dim = 2 ** len(wires)
         if len(U) != target_dim:
             raise ValueError(f"Input unitary must be of shape {(target_dim, target_dim)}")
+
+        # Saving for the circuit drawer
+        self.control_wires = control_wires
+        self.U = U
+
         wires = control_wires + wires
 
         # If control values unspecified, we control on the all-ones string


Test patch:
diff --git a/tests/circuit_drawer/test_circuit_drawer.py b/tests/circuit_drawer/test_circuit_drawer.py
index 8e173197b..efb7b92c2 100644
--- a/tests/circuit_drawer/test_circuit_drawer.py
+++ b/tests/circuit_drawer/test_circuit_drawer.py
@@ -274,6 +274,7 @@ def parameterized_qubit_qnode():
         qml.CRY(0.3589, wires=[3, 1]).inv()
         qml.CSWAP(wires=[4, 2, 1]).inv()
         qml.QubitUnitary(np.eye(2), wires=[2])
+        qml.ControlledQubitUnitary(np.eye(2), control_wires=[0, 1], wires=[2])
         qml.Toffoli(wires=[0, 2, 1])
         qml.CNOT(wires=[0, 2])
         qml.PauliZ(wires=[1])
@@ -326,11 +327,11 @@ def drawn_parameterized_qubit_circuit_with_variable_names():
 def drawn_parameterized_qubit_circuit_with_values():
     """The rendered circuit representation of the above qubit circuit with variable values."""
     return (
-        " 0: ──RX(0.1)───────────────╭C─────RX(0.4)──────────────────────────────────────╭C─────╭C───────╭C──╭C────────────╭C──╭SWAP⁻¹───╭SWAP───┤ ⟨Y⟩       \n"
-        + " 1: ──RX(0.2)────Z──────────╰X⁻¹──╭RY(0.2)──RX(2)────╭RY(0.359)⁻¹──╭SWAP⁻¹──────├X──Z──│───Z⁻¹──╰Z──│─────╭X──╭C──│───│─────────├SWAP───┤ Var[H]    \n"
-        + " 2: ──Rϕ(0.567)──RX(0.6)⁻¹────────│──────────────────│─────────────├SWAP⁻¹──U0──╰C─────╰X───────────╰Z⁻¹──╰C──│───╰X──╰SWAP⁻¹───│───────┤ Sample[X] \n"
-        + " 3: ──────────────────────────────╰C────────RZ(0.2)──╰C────────────│──────────────────────────────────────────╰X───────RZ(0.2)──│──────╭┤ ⟨H0⟩      \n"
-        + " 4: ───────────────────────────────────────────────────────────────╰C───────────────────────────────────────────────────────────╰C─────╰┤ ⟨H0⟩      \n"
+        " 0: ──RX(0.1)───────────────╭C─────RX(0.4)──────────────────────────────────────╭C───╭C─────╭C───────╭C──╭C────────────╭C──╭SWAP⁻¹───╭SWAP───┤ ⟨Y⟩       \n"
+        + " 1: ──RX(0.2)────Z──────────╰X⁻¹──╭RY(0.2)──RX(2)────╭RY(0.359)⁻¹──╭SWAP⁻¹──────├C───├X──Z──│───Z⁻¹──╰Z──│─────╭X──╭C──│───│─────────├SWAP───┤ Var[H]    \n"
+        + " 2: ──Rϕ(0.567)──RX(0.6)⁻¹────────│──────────────────│─────────────├SWAP⁻¹──U0──╰U0──╰C─────╰X───────────╰Z⁻¹──╰C──│───╰X──╰SWAP⁻¹───│───────┤ Sample[X] \n"
+        + " 3: ──────────────────────────────╰C────────RZ(0.2)──╰C────────────│───────────────────────────────────────────────╰X───────RZ(0.2)──│──────╭┤ ⟨H0⟩      \n"
+        + " 4: ───────────────────────────────────────────────────────────────╰C────────────────────────────────────────────────────────────────╰C─────╰┤ ⟨H0⟩      \n"
         + "U0 =\n"
         + "[[1. 0.]\n"
         + " [0. 1.]]\n"