Repository: Qiskit/qiskit

Issue:
Using simulator instructions breaks latex circuit drawer
### What is the current behavior?
Using circuit simulator instructions (such as [`snapshot`](https://qiskit.org/documentation/_autodoc/qiskit._quantumcircuit.html?highlight=snapshot#qiskit._quantumcircuit.QuantumCircuit.snapshot)) cause those instructions to be added to the basis of the DAG, which in turn cause the circuit drawer to raise:

```sh
circuit_drawer(qc).show()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/tools/visualization/_circuit_visualization.py", line 77, in circuit_drawer
    return latex_circuit_drawer(circuit, basis, scale, filename)
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/tools/visualization/_circuit_visualization.py", line 105, in latex_circuit_drawer
    generate_latex_source(circuit, filename=tmppath, basis=basis, scale=scale)
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/tools/visualization/_circuit_visualization.py", line 177, in generate_latex_source
    ast = Qasm(data=circuit.qasm()).parse()
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/qasm/_qasm.py", line 49, in parse
    return qasm_p.parse(self._data)
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/qasm/_qasmparser.py", line 1065, in parse
    self.parser.parse(data, lexer=self.lexer, debug=self.parse_deb)
  File "/Users/salva/workspace/qiskit-sdk-py/.venv/lib/python3.6/site-packages/ply/yacc.py", line 333, in parse
    return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
  File "/Users/salva/workspace/qiskit-sdk-py/.venv/lib/python3.6/site-packages/ply/yacc.py", line 1120, in parseopt_notrack
    p.callable(pslice)
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/qasm/_qasmparser.py", line 651, in p_unitary_op_4
    self.verify_as_gate(program[1], program[5], arglist=program[3])
  File "/Users/salva/workspace/qiskit-sdk-py/qiskit/qasm/_qasmparser.py", line 126, in verify_as_gate
    + "', line", str(obj.line), 'file', obj.file)
qiskit.qasm._qasmerror.QasmError: "Cannot find gate definition for 'snapshot', line 6 file "
```
### Steps to reproduce the problem

```python
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.tools.visualization import circuit_drawer
from qiskit.extensions import simulator
q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)
qc.h(q)
qc.snapshot('0')
qc.measure(q, c)
circuit_drawer(qc).show()
```

### Expected behavior


Discussion:
Also for the experiment.
@ajavadia can you provide the steps to reproduce and the expected behavior, please?
Since is in my bug I will. But it is pretty clear from the description. 

```python
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.tools.visualization import circuit_drawer
q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)
qc.h(q)
qc.snapshot('0')
qc.measure(q, c)
circuit_drawer(qc).show()
```
@jaygambetta this is the output I'm getting, since I and other contributors could not be used to quantum circuits representation, what's the expected behaviour?
![captura de pantalla 2018-08-07 a las 16 09 28](https://user-images.githubusercontent.com/757942/43781098-6bf81c1c-9a5c-11e8-9891-a5ea3ed569fe.png)

hmm no, it crashes. but I think I know why. In your corrections to my code could you add

```python
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.tools.visualization import circuit_drawer
from qiskit.extensions import simulator
q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)
qc.h(q)
qc.snapshot('0')
qc.measure(q, c)
circuit_drawer(qc).show()
```

and it should give you the error. I think your code is just ignoring the snapshot. 
QasmError: "Cannot find gate definition for 'snapshot', line 6 file "
Thank you, Jay, for fixing the steps to reproduce. With all the information in place, I'm completing the summary and assigning to myself.
@ajavadia what's the expected behaviour? It is "not crashing and displaying [the circuit](https://github.com/Qiskit/qiskit-terra/issues/732#issuecomment-411070422)" or should the snapshot be displayed in some way?
@delapuente the expected behavior is that it should not crash. The reason for the crash seems to be that these simulator instructions get added to the basis of DAGCircuit, but not recognized by the drawer or the device experiment. 
Displaying the snapshot/barrier/etc. is tracked in a separate PR #731. 
What happens is that the QASM DAG parser cannot verify that `snapshot` is a gate because it is not in the global symbol table. The QASM the parser is trying to parse is:

```
OPENQASM 2.0;
include "qelib1.inc";
qreg q0[1];
creg c0[1];
snapshot(0) q0[0];
```

Which is, indeed, incorrect since `snapshot` is not defined. The correct one would be:

```
OPENQASM 2.0;
include "qelib1.inc";
opaque snapshot(n) q;
qreg q0[1];
creg c0[1];
snapshot(0) q0[0];
```

Notice the `opaque` definition.

A quick workaround is to make the `qasm()` method for `snapshot` to emit a comment like `#snapshot(0) q0[0];` but this would cause the DAG not to include the `snapshot` node.

A more elaborated solution is adding support for the extensions to add their own definitions so the simulator extension can include the corresponding opaque.

Another solution could be to hardcode the opaque definition of `snapshot` in the `qelib1.inc` and in the basis.

For some reason, even if adding the opaque definition, the JSON unroller complains if the opaque is not in the basis.

@ajavadia @jaygambetta any preference?
@delapuente let's not change `qelib1.inc`. 

I don't quite understand how the first solution would work. Why is this a qasm problem. There should be no qasm text generated, just circuit and dag.

Any gate not in the standard `qelib1.inc` should be dynamically added to the dag basis when the dag is created. The source of error here is that it is not found in the dag basis. Then I think we should teach the visualizer to plot snapshot the same way as it does barrier. 
> The source of error here is that it is not found in the dag basis.

I think there is a misunderstanding on how circuit visualization run right now. DAGs are never used in the current implementation of visualizations. They use the JSON representation of the circuits but, instead of getting here through the DAG, they use the QASM AST:
https://github.com/Qiskit/qiskit-terra/blob/9fb88b035a47eee2ad8e57ddf184049c35e31933/qiskit/tools/visualization/_circuit_visualization.py#L349-L355

Anyway @ajavadia, you gave me a clue!

> Any gate not in the standard qelib1.inc should be dynamically added to the dag basis when the dag is created.

And I'm going to modify the JSON generation making it to use the `DagUnroller` instead of the QASM one.
Yeah that's a bad code, it's dumping qasm text, then reparsing it. Should use the DagUnroller.

Resolving patch:
diff --git a/qiskit/_compositegate.py b/qiskit/_compositegate.py
--- a/qiskit/_compositegate.py
+++ b/qiskit/_compositegate.py
@@ -15,7 +15,7 @@
 class CompositeGate(Gate):
     """Composite gate, a sequence of unitary gates."""
 
-    def __init__(self, name, param, args, circuit=None):
+    def __init__(self, name, param, args, circuit=None, inverse_name=None):
         """Create a new composite gate.
 
         name = instruction name string
@@ -26,6 +26,7 @@ def __init__(self, name, param, args, circuit=None):
         super().__init__(name, param, args, circuit)
         self.data = []  # gate sequence defining the composite unitary
         self.inverse_flag = False
+        self.inverse_name = inverse_name or (name + 'dg')
 
     def instruction_list(self):
         """Return a list of instructions for this CompositeGate.
diff --git a/qiskit/dagcircuit/_dagcircuit.py b/qiskit/dagcircuit/_dagcircuit.py
--- a/qiskit/dagcircuit/_dagcircuit.py
+++ b/qiskit/dagcircuit/_dagcircuit.py
@@ -1330,11 +1330,17 @@ def property_summary(self):
         return summary
 
     @staticmethod
-    def fromQuantumCircuit(circuit):
-        """Returns a DAGCircuit object from a QuantumCircuit
+    def fromQuantumCircuit(circuit, expand_gates=True):
+        """Build a ``DAGCircuit`` object from a ``QuantumCircuit``.
 
-        None of the gates are expanded, i.e. the gates that are defined in the
-        circuit are included in the gate basis.
+        Args:
+            circuit (QuantumCircuit): the input circuit.
+            expand_gates (bool): if ``False``, none of the gates are expanded,
+                i.e. the gates that are defined in the circuit are included in
+                the DAG basis.
+
+        Return:
+            DAGCircuit: the DAG representing the input circuit.
         """
         dagcircuit = DAGCircuit()
         dagcircuit.name = circuit.name
@@ -1367,10 +1373,12 @@ def fromQuantumCircuit(circuit):
             # TODO: generate definitions and nodes for CompositeGates,
             # for now simply drop their instructions into the DAG
             instruction_list = []
-            if isinstance(main_instruction, CompositeGate):
+            is_composite = isinstance(main_instruction, CompositeGate)
+            if is_composite and expand_gates:
                 instruction_list = main_instruction.instruction_list()
             else:
                 instruction_list.append(main_instruction)
+
             for instruction in instruction_list:
                 # Add OpenQASM built-in gates on demand
                 if instruction.name in builtins:
@@ -1390,7 +1398,15 @@ def fromQuantumCircuit(circuit):
                     control = None
                 else:
                     control = (instruction.control[0].name, instruction.control[1])
-                dagcircuit.apply_operation_back(instruction.name, qargs, cargs,
+
+                if is_composite and not expand_gates:
+                    is_inverse = instruction.inverse_flag
+                    name = instruction.name if not is_inverse else instruction.inverse_name
+
+                else:
+                    name = instruction.name
+
+                dagcircuit.apply_operation_back(name, qargs, cargs,
                                                 instruction.param,
                                                 control)
         return dagcircuit
diff --git a/qiskit/tools/visualization/_circuit_visualization.py b/qiskit/tools/visualization/_circuit_visualization.py
--- a/qiskit/tools/visualization/_circuit_visualization.py
+++ b/qiskit/tools/visualization/_circuit_visualization.py
@@ -31,11 +31,10 @@
 from matplotlib import get_backend as get_matplotlib_backend, \
     patches as patches, pyplot as plt
 
-from qiskit._quantumcircuit import QuantumCircuit
-from qiskit._qiskiterror import QISKitError
-from qiskit.wrapper import load_qasm_file
-from qiskit.qasm import Qasm
-from qiskit.unroll import Unroller, JsonBackend
+from qiskit import QuantumCircuit, QISKitError, load_qasm_file
+
+from qiskit.dagcircuit import DAGCircuit
+from qiskit.transpiler import transpile
 
 logger = logging.getLogger(__name__)
 
@@ -348,13 +347,8 @@ def generate_latex_source(circuit, filename=None,
     Returns:
         str: Latex string appropriate for writing to file.
     """
-    ast = Qasm(data=circuit.qasm()).parse()
-    if basis:
-        # Split basis only if it is not the empty string.
-        basis = basis.split(',')
-    u = Unroller(ast, JsonBackend(basis))
-    u.execute()
-    json_circuit = u.backend.circuit
+    dag_circuit = DAGCircuit.fromQuantumCircuit(circuit, expand_gates=False)
+    json_circuit = transpile(dag_circuit, basis_gates=basis, format='json')
     qcimg = QCircuitImage(json_circuit, scale, style=style)
     latex = qcimg.latex()
     if filename:
@@ -1391,7 +1385,7 @@ def __init__(self,
                  scale=1.0, style=None):
 
         self._ast = None
-        self._basis = basis.split(',')
+        self._basis = basis
         self._scale = DEFAULT_SCALE * scale
         self._creg = []
         self._qreg = []
@@ -1421,14 +1415,12 @@ def __init__(self,
         self.ax.tick_params(labelbottom='off', labeltop='off', labelleft='off', labelright='off')
 
     def load_qasm_file(self, filename):
-        circuit = load_qasm_file(filename, name='draw', basis_gates=','.join(self._basis))
+        circuit = load_qasm_file(filename, name='draw', basis_gates=self._basis)
         self.parse_circuit(circuit)
 
     def parse_circuit(self, circuit: QuantumCircuit):
-        ast = Qasm(data=circuit.qasm()).parse()
-        u = Unroller(ast, JsonBackend(self._basis))
-        u.execute()
-        self._ast = u.backend.circuit
+        dag_circuit = DAGCircuit.fromQuantumCircuit(circuit, expand_gates=False)
+        self._ast = transpile(dag_circuit, basis_gates=self._basis, format='json')
         self._registers()
         self._ops = self._ast['instructions']
 


Test patch:
diff --git a/test/python/current_matplot.png b/test/python/current_matplot.png
deleted file mode 100644
Binary files a/test/python/current_matplot.png and /dev/null differ
diff --git a/test/python/references/matplotlib_ref.png b/test/python/references/matplotlib_ref.png
Binary files a/test/python/references/matplotlib_ref.png and b/test/python/references/matplotlib_ref.png differ
diff --git a/test/python/test_visualization_output.py b/test/python/test_visualization_output.py
--- a/test/python/test_visualization_output.py
+++ b/test/python/test_visualization_output.py
@@ -102,7 +102,7 @@ def test_matplotlib_drawer(self):
         self.assertImagesAreEqual(filename, self.matplotlib_reference)
         os.remove(filename)
 
-    def assertImagesAreEqual(self, current, expected, diff_tolerance=0.01):
+    def assertImagesAreEqual(self, current, expected, diff_tolerance=0.001):
         """Checks if both images are similar enough to be considered equal.
         Similarity is controlled by the ```diff_tolerance``` argument."""
         from PIL import Image, ImageChops