We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Using simulator instructions crashes the latex drawer
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Informations

- **Qiskit Terra version**: latest master
- **Python version**: 3.7
- **Operating system**: linux

### What is the current behavior?

Attempting to use the latex drawer to render a circuit with simulator instructions stack traces in the dagunroller. For example:

```
Traceback (most recent call last):
  File "test_qiskit.py", line 67, in <module>
    visualization.generate_latex_source(qc, filename='out.tex')
  File "/tmp/qiskit/qiskit-terra/qiskit/tools/visualization/_circuit_visualization.py", line 354, in generate_latex_source
    json_circuit = transpile(dag_circuit, basis_gates=basis, format='json')
  File "/tmp/qiskit/qiskit-terra/qiskit/transpiler/_transpiler.py", line 346, in transpile
    dag = dag_unroller.expand_gates()
  File "/tmp/qiskit/qiskit-terra/qiskit/unroll/_dagunroller.py", line 86, in expand_gates
    not self.dag_circuit.gates[current_node["name"]]["opaque"]:
KeyError: 'snapshot'
```
It looks like it's trying to treat the snapshot instruction as a gate (which it's not) and that's causing things to crash.

### Steps to reproduce the problem

I've been running:

```
import qiskit.extensions.simulator
from qiskit import *
from qiskit.tools import visualization

q = QuantumRegister(2)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)

qc.x(q[0])
qc.snapshot(slot=3)
qc.x(q[1])
qc.h(q[0])
qc.barrier()
qc.measure(q[0], c[0])

visualization.generate_latex_source(qc, filename='out.tex')
```
Also replacing snapshot() with save(), load(), and noise()

### What is the expected behavior?

This should draw a circuit (the barriers won't be drawn for the simulator instructions, that's what I was working on adding when I encountered this) and not stack trace.

### Suggested solutions

Fix the crash.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qiskit/unroll/_dagunroller.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 # Copyright 2017, IBM.
4 #
5 # This source code is licensed under the Apache License, Version 2.0 found in
6 # the LICENSE.txt file in the root directory of this source tree.
7 
8 """
9 DAG Unroller
10 """
11 
12 import networkx as nx
13 
14 from qiskit.unroll import Unroller
15 from qiskit.qasm._node import Real, Id, IdList, ExpressionList, Gate, \
16                               PrimaryList, Int, IndexedId, Qreg, If, Creg, \
17                               Program, CustomUnitary
18 from ._unrollererror import UnrollerError
19 from ._dagbackend import DAGBackend
20 
21 
22 class DagUnroller(object):
23     """An Unroller that takes Dag circuits as the input."""
24     def __init__(self, dag_circuit, backend=None):
25         if dag_circuit is None:
26             raise UnrollerError('Invalid dag circuit!!')
27 
28         self.dag_circuit = dag_circuit
29         self.backend = backend
30 
31     def set_backend(self, backend):
32         """Set the backend object."""
33         self.backend = backend
34 
35     def execute(self):
36         """Interpret OPENQASM and make appropriate backend calls."""
37         if self.backend is not None:
38             self._process()
39             return self.backend.get_output()
40         else:
41             raise UnrollerError("backend not attached")
42 
43     # TODO This method should merge with .execute(), so the output will depend
44     # on the backend associated with this DagUnroller instance
45     def expand_gates(self, basis=None):
46         """Expand all gate nodes to the given basis.
47 
48         If basis is empty, each custom gate node is replaced by its
49         implementation over U and CX. If basis contains names, then
50         those custom gates are not expanded. For example, if "u3"
51         is in basis, then the gate "u3" will not be expanded wherever
52         it occurs.
53 
54         This member function replicates the behavior of the unroller
55         module without using the OpenQASM parser.
56         """
57 
58         if basis is None:
59             basis = self.backend.basis
60 
61         if not isinstance(self.backend, DAGBackend):
62             raise UnrollerError("expand_gates only accepts a DAGBackend!!")
63 
64         # Build the Gate AST nodes for user-defined gates
65         gatedefs = []
66         for name, gate in self.dag_circuit.gates.items():
67             children = [Id(name, 0, "")]
68             if gate["n_args"] > 0:
69                 children.append(ExpressionList(list(
70                     map(lambda x: Id(x, 0, ""),
71                         gate["args"])
72                 )))
73             children.append(IdList(list(
74                 map(lambda x: Id(x, 0, ""),
75                     gate["bits"])
76             )))
77             children.append(gate["body"])
78             gatedefs.append(Gate(children))
79         # Walk through the DAG and examine each node
80         builtins = ["U", "CX", "measure", "reset", "barrier"]
81         topological_sorted_list = list(nx.topological_sort(self.dag_circuit.multi_graph))
82         for node in topological_sorted_list:
83             current_node = self.dag_circuit.multi_graph.node[node]
84             if current_node["type"] == "op" and \
85                current_node["name"] not in builtins + basis and \
86                not self.dag_circuit.gates[current_node["name"]]["opaque"]:
87                 subcircuit, wires = self._build_subcircuit(gatedefs,
88                                                            basis,
89                                                            current_node["name"],
90                                                            current_node["params"],
91                                                            current_node["qargs"],
92                                                            current_node["condition"])
93                 self.dag_circuit.substitute_circuit_one(node, subcircuit, wires)
94         return self.dag_circuit
95 
96     def _build_subcircuit(self, gatedefs, basis, gate_name, gate_params, gate_args,
97                           gate_condition):
98         """Build DAGCircuit for a given user-defined gate node.
99 
100         gatedefs = dictionary of Gate AST nodes for user-defined gates
101         gate_name = name of gate to expand to target_basis (nd["name"])
102         gate_params = list of gate parameters (nd["params"])
103         gate_args = list of gate arguments (nd["qargs"])
104         gate_condition = None or tuple (string, int) (nd["condition"])
105 
106         Returns (subcircuit, wires) where subcircuit is the DAGCircuit
107         corresponding to the user-defined gate node expanded to target_basis
108         and wires is the list of input wires to the subcircuit in order
109         corresponding to the gate's arguments.
110         """
111 
112         children = [Id(gate_name, 0, "")]
113         if gate_params:
114             children.append(
115                 ExpressionList(list(map(Real, gate_params)))
116             )
117         new_wires = [("q", j) for j in range(len(gate_args))]
118         children.append(
119             PrimaryList(
120                 list(map(lambda x: IndexedId(
121                     [Id(x[0], 0, ""), Int(x[1])]
122                 ), new_wires))
123             )
124         )
125         gate_node = CustomUnitary(children)
126         id_int = [Id("q", 0, ""), Int(len(gate_args))]
127         # Make a list of register declaration nodes
128         reg_nodes = [
129             Qreg(
130                 [
131                     IndexedId(id_int)
132                 ]
133             )
134         ]
135         # Add an If node when there is a condition present
136         if gate_condition:
137             gate_node = If([
138                 Id(gate_condition[0], 0, ""),
139                 Int(gate_condition[1]),
140                 gate_node
141             ])
142             new_wires += [(gate_condition[0], j)
143                           for j in range(self.dag_circuit.cregs[gate_condition[0]])]
144             reg_nodes.append(
145                 Creg([
146                     IndexedId([
147                         Id(gate_condition[0], 0, ""),
148                         Int(self.dag_circuit.cregs[gate_condition[0]])
149                     ])
150                 ])
151             )
152 
153         # Build the whole program's AST
154         sub_ast = Program(gatedefs + reg_nodes + [gate_node])
155         # Interpret the AST to give a new DAGCircuit over backend basis
156         sub_circuit = Unroller(sub_ast, DAGBackend(basis)).execute()
157         return sub_circuit, new_wires
158 
159     def _process(self):
160         for name, width in self.dag_circuit.qregs.items():
161             self.backend.new_qreg(name, width)
162         for name, width in self.dag_circuit.cregs.items():
163             self.backend.new_creg(name, width)
164         for name, data in self.dag_circuit.gates.items():
165             self.backend.define_gate(name, data)
166         for n in nx.topological_sort(self.dag_circuit.multi_graph):
167             current_node = self.dag_circuit.multi_graph.node[n]
168             if current_node["type"] == "op":
169                 params = map(Real, current_node["params"])
170                 params = list(params)
171                 if current_node["condition"] is not None:
172                     self.backend.set_condition(current_node["condition"][0],
173                                                current_node["condition"][1])
174                 if not current_node["cargs"]:
175                     if current_node["name"] == "U":
176                         self.backend.u(params, current_node["qargs"][0])
177                     elif current_node["name"] == "CX":
178                         self.backend.cx(current_node["qargs"][0], current_node["qargs"][1])
179                     elif current_node["name"] == "barrier":
180                         self.backend.barrier([current_node["qargs"]])
181                     elif current_node["name"] == "reset":
182                         self.backend.reset(current_node["qargs"][0])
183 
184                     # TODO: The schema of the snapshot gate is radically
185                     # different to other QASM instructions. The current model
186                     # of extensions does not support generating custom Qobj
187                     # instructions (only custom QASM strings) and the default
188                     # instruction generator is not enough to produce a valid
189                     # snapshot instruction for the new Qobj format.
190                     #
191                     # This is a hack since there would be mechanisms for the
192                     # extensions to provide their own Qobj instructions.
193                     # Extensions should not be hardcoded in the DAGUnroller.
194                     elif current_node["name"] == "snapshot":
195                         self.backend.start_gate(
196                             "snapshot", params, current_node["qargs"],
197                             extra_fields={'type': 'MISSING', 'label': 'MISSING', 'texparams': []})
198                         self.backend.end_gate("snapshot", params, current_node["qargs"])
199                     else:
200                         self.backend.start_gate(current_node["name"], params,
201                                                 current_node["qargs"])
202                         self.backend.end_gate(current_node["name"], params, current_node["qargs"])
203                 else:
204                     if current_node["name"] == "measure":
205                         if len(current_node["cargs"]) != 1 or len(current_node["qargs"]) != 1 \
206                            or current_node["params"]:
207                             raise UnrollerError("Bad node data!!")
208 
209                         self.backend.measure(current_node["qargs"][0], current_node["cargs"][0])
210                     else:
211                         raise UnrollerError("Bad node data!")
212 
213                 self.backend.drop_condition()
214         return self.backend.get_output()
215
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/qiskit/unroll/_dagunroller.py b/qiskit/unroll/_dagunroller.py
--- a/qiskit/unroll/_dagunroller.py
+++ b/qiskit/unroll/_dagunroller.py
@@ -78,11 +78,12 @@
             gatedefs.append(Gate(children))
         # Walk through the DAG and examine each node
         builtins = ["U", "CX", "measure", "reset", "barrier"]
+        simulator_builtins = ['snapshot', 'save', 'load', 'noise']
         topological_sorted_list = list(nx.topological_sort(self.dag_circuit.multi_graph))
         for node in topological_sorted_list:
             current_node = self.dag_circuit.multi_graph.node[node]
             if current_node["type"] == "op" and \
-               current_node["name"] not in builtins + basis and \
+               current_node["name"] not in builtins + basis + simulator_builtins and \
                not self.dag_circuit.gates[current_node["name"]]["opaque"]:
                 subcircuit, wires = self._build_subcircuit(gatedefs,
                                                            basis,

{"golden_diff": "diff --git a/qiskit/unroll/_dagunroller.py b/qiskit/unroll/_dagunroller.py\n--- a/qiskit/unroll/_dagunroller.py\n+++ b/qiskit/unroll/_dagunroller.py\n@@ -78,11 +78,12 @@\n             gatedefs.append(Gate(children))\n         # Walk through the DAG and examine each node\n         builtins = [\"U\", \"CX\", \"measure\", \"reset\", \"barrier\"]\n+        simulator_builtins = ['snapshot', 'save', 'load', 'noise']\n         topological_sorted_list = list(nx.topological_sort(self.dag_circuit.multi_graph))\n         for node in topological_sorted_list:\n             current_node = self.dag_circuit.multi_graph.node[node]\n             if current_node[\"type\"] == \"op\" and \\\n-               current_node[\"name\"] not in builtins + basis and \\\n+               current_node[\"name\"] not in builtins + basis + simulator_builtins and \\\n                not self.dag_circuit.gates[current_node[\"name\"]][\"opaque\"]:\n                 subcircuit, wires = self._build_subcircuit(gatedefs,\n                                                            basis,\n", "issue": "Using simulator instructions crashes the latex drawer\n<!-- \u26a0\ufe0f If you do not respect this template, your issue will be closed -->\r\n<!-- \u26a0\ufe0f Make sure to browse the opened and closed issues -->\r\n\r\n### Informations\r\n\r\n- **Qiskit Terra version**: latest master\r\n- **Python version**: 3.7\r\n- **Operating system**: linux\r\n\r\n### What is the current behavior?\r\n\r\nAttempting to use the latex drawer to render a circuit with simulator instructions stack traces in the dagunroller. For example:\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"test_qiskit.py\", line 67, in <module>\r\n    visualization.generate_latex_source(qc, filename='out.tex')\r\n  File \"/tmp/qiskit/qiskit-terra/qiskit/tools/visualization/_circuit_visualization.py\", line 354, in generate_latex_source\r\n    json_circuit = transpile(dag_circuit, basis_gates=basis, format='json')\r\n  File \"/tmp/qiskit/qiskit-terra/qiskit/transpiler/_transpiler.py\", line 346, in transpile\r\n    dag = dag_unroller.expand_gates()\r\n  File \"/tmp/qiskit/qiskit-terra/qiskit/unroll/_dagunroller.py\", line 86, in expand_gates\r\n    not self.dag_circuit.gates[current_node[\"name\"]][\"opaque\"]:\r\nKeyError: 'snapshot'\r\n```\r\nIt looks like it's trying to treat the snapshot instruction as a gate (which it's not) and that's causing things to crash.\r\n\r\n### Steps to reproduce the problem\r\n\r\nI've been running:\r\n\r\n```\r\nimport qiskit.extensions.simulator\r\nfrom qiskit import *\r\nfrom qiskit.tools import visualization\r\n\r\nq = QuantumRegister(2)\r\nc = ClassicalRegister(1)\r\nqc = QuantumCircuit(q, c)\r\n\r\nqc.x(q[0])\r\nqc.snapshot(slot=3)\r\nqc.x(q[1])\r\nqc.h(q[0])\r\nqc.barrier()\r\nqc.measure(q[0], c[0])\r\n\r\nvisualization.generate_latex_source(qc, filename='out.tex')\r\n```\r\nAlso replacing snapshot() with save(), load(), and noise()\r\n\r\n### What is the expected behavior?\r\n\r\nThis should draw a circuit (the barriers won't be drawn for the simulator instructions, that's what I was working on adding when I encountered this) and not stack trace.\r\n\r\n### Suggested solutions\r\n\r\nFix the crash.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Copyright 2017, IBM.\n#\n# This source code is licensed under the Apache License, Version 2.0 found in\n# the LICENSE.txt file in the root directory of this source tree.\n\n\"\"\"\nDAG Unroller\n\"\"\"\n\nimport networkx as nx\n\nfrom qiskit.unroll import Unroller\nfrom qiskit.qasm._node import Real, Id, IdList, ExpressionList, Gate, \\\n                              PrimaryList, Int, IndexedId, Qreg, If, Creg, \\\n                              Program, CustomUnitary\nfrom ._unrollererror import UnrollerError\nfrom ._dagbackend import DAGBackend\n\n\nclass DagUnroller(object):\n    \"\"\"An Unroller that takes Dag circuits as the input.\"\"\"\n    def __init__(self, dag_circuit, backend=None):\n        if dag_circuit is None:\n            raise UnrollerError('Invalid dag circuit!!')\n\n        self.dag_circuit = dag_circuit\n        self.backend = backend\n\n    def set_backend(self, backend):\n        \"\"\"Set the backend object.\"\"\"\n        self.backend = backend\n\n    def execute(self):\n        \"\"\"Interpret OPENQASM and make appropriate backend calls.\"\"\"\n        if self.backend is not None:\n            self._process()\n            return self.backend.get_output()\n        else:\n            raise UnrollerError(\"backend not attached\")\n\n    # TODO This method should merge with .execute(), so the output will depend\n    # on the backend associated with this DagUnroller instance\n    def expand_gates(self, basis=None):\n        \"\"\"Expand all gate nodes to the given basis.\n\n        If basis is empty, each custom gate node is replaced by its\n        implementation over U and CX. If basis contains names, then\n        those custom gates are not expanded. For example, if \"u3\"\n        is in basis, then the gate \"u3\" will not be expanded wherever\n        it occurs.\n\n        This member function replicates the behavior of the unroller\n        module without using the OpenQASM parser.\n        \"\"\"\n\n        if basis is None:\n            basis = self.backend.basis\n\n        if not isinstance(self.backend, DAGBackend):\n            raise UnrollerError(\"expand_gates only accepts a DAGBackend!!\")\n\n        # Build the Gate AST nodes for user-defined gates\n        gatedefs = []\n        for name, gate in self.dag_circuit.gates.items():\n            children = [Id(name, 0, \"\")]\n            if gate[\"n_args\"] > 0:\n                children.append(ExpressionList(list(\n                    map(lambda x: Id(x, 0, \"\"),\n                        gate[\"args\"])\n                )))\n            children.append(IdList(list(\n                map(lambda x: Id(x, 0, \"\"),\n                    gate[\"bits\"])\n            )))\n            children.append(gate[\"body\"])\n            gatedefs.append(Gate(children))\n        # Walk through the DAG and examine each node\n        builtins = [\"U\", \"CX\", \"measure\", \"reset\", \"barrier\"]\n        topological_sorted_list = list(nx.topological_sort(self.dag_circuit.multi_graph))\n        for node in topological_sorted_list:\n            current_node = self.dag_circuit.multi_graph.node[node]\n            if current_node[\"type\"] == \"op\" and \\\n               current_node[\"name\"] not in builtins + basis and \\\n               not self.dag_circuit.gates[current_node[\"name\"]][\"opaque\"]:\n                subcircuit, wires = self._build_subcircuit(gatedefs,\n                                                           basis,\n                                                           current_node[\"name\"],\n                                                           current_node[\"params\"],\n                                                           current_node[\"qargs\"],\n                                                           current_node[\"condition\"])\n                self.dag_circuit.substitute_circuit_one(node, subcircuit, wires)\n        return self.dag_circuit\n\n    def _build_subcircuit(self, gatedefs, basis, gate_name, gate_params, gate_args,\n                          gate_condition):\n        \"\"\"Build DAGCircuit for a given user-defined gate node.\n\n        gatedefs = dictionary of Gate AST nodes for user-defined gates\n        gate_name = name of gate to expand to target_basis (nd[\"name\"])\n        gate_params = list of gate parameters (nd[\"params\"])\n        gate_args = list of gate arguments (nd[\"qargs\"])\n        gate_condition = None or tuple (string, int) (nd[\"condition\"])\n\n        Returns (subcircuit, wires) where subcircuit is the DAGCircuit\n        corresponding to the user-defined gate node expanded to target_basis\n        and wires is the list of input wires to the subcircuit in order\n        corresponding to the gate's arguments.\n        \"\"\"\n\n        children = [Id(gate_name, 0, \"\")]\n        if gate_params:\n            children.append(\n                ExpressionList(list(map(Real, gate_params)))\n            )\n        new_wires = [(\"q\", j) for j in range(len(gate_args))]\n        children.append(\n            PrimaryList(\n                list(map(lambda x: IndexedId(\n                    [Id(x[0], 0, \"\"), Int(x[1])]\n                ), new_wires))\n            )\n        )\n        gate_node = CustomUnitary(children)\n        id_int = [Id(\"q\", 0, \"\"), Int(len(gate_args))]\n        # Make a list of register declaration nodes\n        reg_nodes = [\n            Qreg(\n                [\n                    IndexedId(id_int)\n                ]\n            )\n        ]\n        # Add an If node when there is a condition present\n        if gate_condition:\n            gate_node = If([\n                Id(gate_condition[0], 0, \"\"),\n                Int(gate_condition[1]),\n                gate_node\n            ])\n            new_wires += [(gate_condition[0], j)\n                          for j in range(self.dag_circuit.cregs[gate_condition[0]])]\n            reg_nodes.append(\n                Creg([\n                    IndexedId([\n                        Id(gate_condition[0], 0, \"\"),\n                        Int(self.dag_circuit.cregs[gate_condition[0]])\n                    ])\n                ])\n            )\n\n        # Build the whole program's AST\n        sub_ast = Program(gatedefs + reg_nodes + [gate_node])\n        # Interpret the AST to give a new DAGCircuit over backend basis\n        sub_circuit = Unroller(sub_ast, DAGBackend(basis)).execute()\n        return sub_circuit, new_wires\n\n    def _process(self):\n        for name, width in self.dag_circuit.qregs.items():\n            self.backend.new_qreg(name, width)\n        for name, width in self.dag_circuit.cregs.items():\n            self.backend.new_creg(name, width)\n        for name, data in self.dag_circuit.gates.items():\n            self.backend.define_gate(name, data)\n        for n in nx.topological_sort(self.dag_circuit.multi_graph):\n            current_node = self.dag_circuit.multi_graph.node[n]\n            if current_node[\"type\"] == \"op\":\n                params = map(Real, current_node[\"params\"])\n                params = list(params)\n                if current_node[\"condition\"] is not None:\n                    self.backend.set_condition(current_node[\"condition\"][0],\n                                               current_node[\"condition\"][1])\n                if not current_node[\"cargs\"]:\n                    if current_node[\"name\"] == \"U\":\n                        self.backend.u(params, current_node[\"qargs\"][0])\n                    elif current_node[\"name\"] == \"CX\":\n                        self.backend.cx(current_node[\"qargs\"][0], current_node[\"qargs\"][1])\n                    elif current_node[\"name\"] == \"barrier\":\n                        self.backend.barrier([current_node[\"qargs\"]])\n                    elif current_node[\"name\"] == \"reset\":\n                        self.backend.reset(current_node[\"qargs\"][0])\n\n                    # TODO: The schema of the snapshot gate is radically\n                    # different to other QASM instructions. The current model\n                    # of extensions does not support generating custom Qobj\n                    # instructions (only custom QASM strings) and the default\n                    # instruction generator is not enough to produce a valid\n                    # snapshot instruction for the new Qobj format.\n                    #\n                    # This is a hack since there would be mechanisms for the\n                    # extensions to provide their own Qobj instructions.\n                    # Extensions should not be hardcoded in the DAGUnroller.\n                    elif current_node[\"name\"] == \"snapshot\":\n                        self.backend.start_gate(\n                            \"snapshot\", params, current_node[\"qargs\"],\n                            extra_fields={'type': 'MISSING', 'label': 'MISSING', 'texparams': []})\n                        self.backend.end_gate(\"snapshot\", params, current_node[\"qargs\"])\n                    else:\n                        self.backend.start_gate(current_node[\"name\"], params,\n                                                current_node[\"qargs\"])\n                        self.backend.end_gate(current_node[\"name\"], params, current_node[\"qargs\"])\n                else:\n                    if current_node[\"name\"] == \"measure\":\n                        if len(current_node[\"cargs\"]) != 1 or len(current_node[\"qargs\"]) != 1 \\\n                           or current_node[\"params\"]:\n                            raise UnrollerError(\"Bad node data!!\")\n\n                        self.backend.measure(current_node[\"qargs\"][0], current_node[\"cargs\"][0])\n                    else:\n                        raise UnrollerError(\"Bad node data!\")\n\n                self.backend.drop_condition()\n        return self.backend.get_output()\n", "path": "qiskit/unroll/_dagunroller.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Copyright 2017, IBM.\n#\n# This source code is licensed under the Apache License, Version 2.0 found in\n# the LICENSE.txt file in the root directory of this source tree.\n\n\"\"\"\nDAG Unroller\n\"\"\"\n\nimport networkx as nx\n\nfrom qiskit.unroll import Unroller\nfrom qiskit.qasm._node import Real, Id, IdList, ExpressionList, Gate, \\\n                              PrimaryList, Int, IndexedId, Qreg, If, Creg, \\\n                              Program, CustomUnitary\nfrom ._unrollererror import UnrollerError\nfrom ._dagbackend import DAGBackend\n\n\nclass DagUnroller(object):\n    \"\"\"An Unroller that takes Dag circuits as the input.\"\"\"\n    def __init__(self, dag_circuit, backend=None):\n        if dag_circuit is None:\n            raise UnrollerError('Invalid dag circuit!!')\n\n        self.dag_circuit = dag_circuit\n        self.backend = backend\n\n    def set_backend(self, backend):\n        \"\"\"Set the backend object.\"\"\"\n        self.backend = backend\n\n    def execute(self):\n        \"\"\"Interpret OPENQASM and make appropriate backend calls.\"\"\"\n        if self.backend is not None:\n            self._process()\n            return self.backend.get_output()\n        else:\n            raise UnrollerError(\"backend not attached\")\n\n    # TODO This method should merge with .execute(), so the output will depend\n    # on the backend associated with this DagUnroller instance\n    def expand_gates(self, basis=None):\n        \"\"\"Expand all gate nodes to the given basis.\n\n        If basis is empty, each custom gate node is replaced by its\n        implementation over U and CX. If basis contains names, then\n        those custom gates are not expanded. For example, if \"u3\"\n        is in basis, then the gate \"u3\" will not be expanded wherever\n        it occurs.\n\n        This member function replicates the behavior of the unroller\n        module without using the OpenQASM parser.\n        \"\"\"\n\n        if basis is None:\n            basis = self.backend.basis\n\n        if not isinstance(self.backend, DAGBackend):\n            raise UnrollerError(\"expand_gates only accepts a DAGBackend!!\")\n\n        # Build the Gate AST nodes for user-defined gates\n        gatedefs = []\n        for name, gate in self.dag_circuit.gates.items():\n            children = [Id(name, 0, \"\")]\n            if gate[\"n_args\"] > 0:\n                children.append(ExpressionList(list(\n                    map(lambda x: Id(x, 0, \"\"),\n                        gate[\"args\"])\n                )))\n            children.append(IdList(list(\n                map(lambda x: Id(x, 0, \"\"),\n                    gate[\"bits\"])\n            )))\n            children.append(gate[\"body\"])\n            gatedefs.append(Gate(children))\n        # Walk through the DAG and examine each node\n        builtins = [\"U\", \"CX\", \"measure\", \"reset\", \"barrier\"]\n        simulator_builtins = ['snapshot', 'save', 'load', 'noise']\n        topological_sorted_list = list(nx.topological_sort(self.dag_circuit.multi_graph))\n        for node in topological_sorted_list:\n            current_node = self.dag_circuit.multi_graph.node[node]\n            if current_node[\"type\"] == \"op\" and \\\n               current_node[\"name\"] not in builtins + basis + simulator_builtins and \\\n               not self.dag_circuit.gates[current_node[\"name\"]][\"opaque\"]:\n                subcircuit, wires = self._build_subcircuit(gatedefs,\n                                                           basis,\n                                                           current_node[\"name\"],\n                                                           current_node[\"params\"],\n                                                           current_node[\"qargs\"],\n                                                           current_node[\"condition\"])\n                self.dag_circuit.substitute_circuit_one(node, subcircuit, wires)\n        return self.dag_circuit\n\n    def _build_subcircuit(self, gatedefs, basis, gate_name, gate_params, gate_args,\n                          gate_condition):\n        \"\"\"Build DAGCircuit for a given user-defined gate node.\n\n        gatedefs = dictionary of Gate AST nodes for user-defined gates\n        gate_name = name of gate to expand to target_basis (nd[\"name\"])\n        gate_params = list of gate parameters (nd[\"params\"])\n        gate_args = list of gate arguments (nd[\"qargs\"])\n        gate_condition = None or tuple (string, int) (nd[\"condition\"])\n\n        Returns (subcircuit, wires) where subcircuit is the DAGCircuit\n        corresponding to the user-defined gate node expanded to target_basis\n        and wires is the list of input wires to the subcircuit in order\n        corresponding to the gate's arguments.\n        \"\"\"\n\n        children = [Id(gate_name, 0, \"\")]\n        if gate_params:\n            children.append(\n                ExpressionList(list(map(Real, gate_params)))\n            )\n        new_wires = [(\"q\", j) for j in range(len(gate_args))]\n        children.append(\n            PrimaryList(\n                list(map(lambda x: IndexedId(\n                    [Id(x[0], 0, \"\"), Int(x[1])]\n                ), new_wires))\n            )\n        )\n        gate_node = CustomUnitary(children)\n        id_int = [Id(\"q\", 0, \"\"), Int(len(gate_args))]\n        # Make a list of register declaration nodes\n        reg_nodes = [\n            Qreg(\n                [\n                    IndexedId(id_int)\n                ]\n            )\n        ]\n        # Add an If node when there is a condition present\n        if gate_condition:\n            gate_node = If([\n                Id(gate_condition[0], 0, \"\"),\n                Int(gate_condition[1]),\n                gate_node\n            ])\n            new_wires += [(gate_condition[0], j)\n                          for j in range(self.dag_circuit.cregs[gate_condition[0]])]\n            reg_nodes.append(\n                Creg([\n                    IndexedId([\n                        Id(gate_condition[0], 0, \"\"),\n                        Int(self.dag_circuit.cregs[gate_condition[0]])\n                    ])\n                ])\n            )\n\n        # Build the whole program's AST\n        sub_ast = Program(gatedefs + reg_nodes + [gate_node])\n        # Interpret the AST to give a new DAGCircuit over backend basis\n        sub_circuit = Unroller(sub_ast, DAGBackend(basis)).execute()\n        return sub_circuit, new_wires\n\n    def _process(self):\n        for name, width in self.dag_circuit.qregs.items():\n            self.backend.new_qreg(name, width)\n        for name, width in self.dag_circuit.cregs.items():\n            self.backend.new_creg(name, width)\n        for name, data in self.dag_circuit.gates.items():\n            self.backend.define_gate(name, data)\n        for n in nx.topological_sort(self.dag_circuit.multi_graph):\n            current_node = self.dag_circuit.multi_graph.node[n]\n            if current_node[\"type\"] == \"op\":\n                params = map(Real, current_node[\"params\"])\n                params = list(params)\n                if current_node[\"condition\"] is not None:\n                    self.backend.set_condition(current_node[\"condition\"][0],\n                                               current_node[\"condition\"][1])\n                if not current_node[\"cargs\"]:\n                    if current_node[\"name\"] == \"U\":\n                        self.backend.u(params, current_node[\"qargs\"][0])\n                    elif current_node[\"name\"] == \"CX\":\n                        self.backend.cx(current_node[\"qargs\"][0], current_node[\"qargs\"][1])\n                    elif current_node[\"name\"] == \"barrier\":\n                        self.backend.barrier([current_node[\"qargs\"]])\n                    elif current_node[\"name\"] == \"reset\":\n                        self.backend.reset(current_node[\"qargs\"][0])\n\n                    # TODO: The schema of the snapshot gate is radically\n                    # different to other QASM instructions. The current model\n                    # of extensions does not support generating custom Qobj\n                    # instructions (only custom QASM strings) and the default\n                    # instruction generator is not enough to produce a valid\n                    # snapshot instruction for the new Qobj format.\n                    #\n                    # This is a hack since there would be mechanisms for the\n                    # extensions to provide their own Qobj instructions.\n                    # Extensions should not be hardcoded in the DAGUnroller.\n                    elif current_node[\"name\"] == \"snapshot\":\n                        self.backend.start_gate(\n                            \"snapshot\", params, current_node[\"qargs\"],\n                            extra_fields={'type': 'MISSING', 'label': 'MISSING', 'texparams': []})\n                        self.backend.end_gate(\"snapshot\", params, current_node[\"qargs\"])\n                    else:\n                        self.backend.start_gate(current_node[\"name\"], params,\n                                                current_node[\"qargs\"])\n                        self.backend.end_gate(current_node[\"name\"], params, current_node[\"qargs\"])\n                else:\n                    if current_node[\"name\"] == \"measure\":\n                        if len(current_node[\"cargs\"]) != 1 or len(current_node[\"qargs\"]) != 1 \\\n                           or current_node[\"params\"]:\n                            raise UnrollerError(\"Bad node data!!\")\n\n                        self.backend.measure(current_node[\"qargs\"][0], current_node[\"cargs\"][0])\n                    else:\n                        raise UnrollerError(\"Bad node data!\")\n\n                self.backend.drop_condition()\n        return self.backend.get_output()\n", "path": "qiskit/unroll/_dagunroller.py"}]}