Repository: Qiskit/qiskit

Issue:
qc.qasm() does not include params of composited gates
### Information

- **Qiskit Terra version**: 0.15.2
- **Python version**:  3.7.7
- **Operating system**: macOS 10.15.6

### What is the current behavior?
When you create a `QuantumCircuit` from a qasm string including composited gates,

```python
from qiskit.circuit import QuantumCircuit
qasm_str = (
    'OPENQASM 2.0;'
    'include "qelib1.inc";'
    'gate nG0 (param0) q0 { h q0; }'
    'qreg q[3];'
    'creg c[3];'
    'nG0 (pi) q[0];'
)
qc = QuantumCircuit.from_qasm_str(qasm_str)
```

the circuit is correctly created. But if you get qasm from the already created circuit

```python
qc.qasm(formatted=True)
```
```qasm
OPENQASM 2.0;
include "qelib1.inc";
gate nG0 q0 {h q0; }   // it should be: gate nG0 (param0) q0 { h q0; }
qreg q[3];
creg c[3];
nG0(pi) q[0];
```

the composited gates params are not present, so the qasm is incorrect. Any attempt of creating a new quantum circuit from this qasm will fail:

```python
qc2 = QuantumCircuit.from_qasm_str(qc.qasm())
```
```python
QasmError: "Gate or opaque call to 'nG0' uses 1 qubits but is declared for 0 qubits line 6 file "
```

### What is the expected behavior?
The qasm of the created quantum circuit should include the params of the composited gate.

```python
qc.qasm(formatted=True)
```

```qasm
OPENQASM 2.0;
include "qelib1.inc";
gate nG0 (param0) q0 {h q0; }
qreg q[3];
creg c[3];
nG0(pi) q[0];
```


### Suggested solutions
Take into account `Instruction` params when creating qasm in `qc.qasm()`.




Discussion:

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -1006,6 +1006,7 @@ def _get_composite_circuit_qasm_from_instruction(instruction):
         """Returns OpenQASM string composite circuit given an instruction.
         The given instruction should be the result of composite_circuit.to_instruction()."""
 
+        gate_parameters = ",".join(["param%i" % num for num in range(len(instruction.params))])
         qubit_parameters = ",".join(["q%i" % num for num in range(instruction.num_qubits)])
         composite_circuit_gates = ""
 
@@ -1013,8 +1014,15 @@ def _get_composite_circuit_qasm_from_instruction(instruction):
             gate_qargs = ",".join(["q%i" % index for index in [qubit.index for qubit in qargs]])
             composite_circuit_gates += "%s %s; " % (data.qasm(), gate_qargs)
 
-        qasm_string = "gate %s %s {%s}" % (instruction.name, qubit_parameters,
-                                           composite_circuit_gates)
+        if composite_circuit_gates:
+            composite_circuit_gates = composite_circuit_gates.rstrip(' ')
+
+        if gate_parameters:
+            qasm_string = "gate %s(%s) %s { %s }" % (instruction.name, gate_parameters,
+                                                     qubit_parameters, composite_circuit_gates)
+        else:
+            qasm_string = "gate %s %s { %s }" % (instruction.name, qubit_parameters,
+                                                 composite_circuit_gates)
 
         return qasm_string
 
@@ -1053,9 +1061,8 @@ def qasm(self, formatted=False, filename=None):
                 string_temp += "%s %s[%d] -> %s[%d];\n" % (instruction.qasm(),
                                                            qubit.register.name, qubit.index,
                                                            clbit.register.name, clbit.index)
-            # If instruction is a composite circuit
-            elif not isinstance(instruction, Gate) and (instruction.name not in ['barrier',
-                                                                                 'reset']):
+            # If instruction is a root gate or a root instruction (in that case, compositive)
+            elif type(instruction) in [Gate, Instruction]:  # pylint: disable=unidiomatic-typecheck
                 if instruction not in existing_composite_circuits:
                     if instruction.name in existing_gate_names:
                         old_name = instruction.name


Test patch:
diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py
--- a/test/python/circuit/test_circuit_qasm.py
+++ b/test/python/circuit/test_circuit_qasm.py
@@ -91,7 +91,7 @@ def test_circuit_qasm_with_composite_circuit(self):
 
         expected_qasm = """OPENQASM 2.0;
 include "qelib1.inc";
-gate composite_circ q0,q1 {h q0; x q1; cx q0,q1; }
+gate composite_circ q0,q1 { h q0; x q1; cx q0,q1; }
 qreg qr[2];
 creg cr[2];
 h qr[0];
@@ -126,7 +126,7 @@ def test_circuit_qasm_with_multiple_same_composite_circuits(self):
 
         expected_qasm = """OPENQASM 2.0;
 include "qelib1.inc";
-gate composite_circ q0,q1 {h q0; x q1; cx q0,q1; }
+gate composite_circ q0,q1 { h q0; x q1; cx q0,q1; }
 qreg qr[2];
 creg cr[2];
 h qr[0];
@@ -165,9 +165,9 @@ def test_circuit_qasm_with_multiple_composite_circuits_with_same_name(self):
 
         expected_qasm = """OPENQASM 2.0;
 include "qelib1.inc";
-gate my_gate_{0} q0 {{x q0; }}
-gate my_gate_{1} q0 {{x q0; }}
-gate my_gate q0 {{h q0; }}
+gate my_gate_{0} q0 {{ x q0; }}
+gate my_gate_{1} q0 {{ x q0; }}
+gate my_gate q0 {{ h q0; }}
 qreg qr[1];
 my_gate qr[0];
 my_gate_{1} qr[0];
@@ -184,3 +184,33 @@ def test_circuit_qasm_pi(self):
         qasm_str = circuit.qasm()
         circuit2 = QuantumCircuit.from_qasm_str(qasm_str)
         self.assertEqual(circuit, circuit2)
+
+    def test_circuit_qasm_with_composite_circuit_with_one_param(self):
+        """Test circuit qasm() method when a composite circuit instruction
+        has one param
+        """
+        original_str = """OPENQASM 2.0;
+include "qelib1.inc";
+gate nG0(param0) q0 { h q0; }
+qreg q[3];
+creg c[3];
+nG0(pi) q[0];\n"""
+        qc = QuantumCircuit.from_qasm_str(original_str)
+
+        self.assertEqual(original_str, qc.qasm())
+
+    def test_circuit_qasm_with_composite_circuit_with_many_params_and_qubits(self):
+        """Test circuit qasm() method when a composite circuit instruction
+        has many params and qubits
+        """
+        original_str = """OPENQASM 2.0;
+include "qelib1.inc";
+gate nG0(param0,param1) q0,q1 { h q0; h q1; }
+qreg q[3];
+qreg r[3];
+creg c[3];
+creg d[3];
+nG0(pi,pi/2) q[0],r[0];\n"""
+        qc = QuantumCircuit.from_qasm_str(original_str)
+
+        self.assertEqual(original_str, qc.qasm())