Repository: Qiskit/qiskit

Issue:
Move output_names into TranspileConfig
Follow up items from https://github.com/Qiskit/qiskit-terra/pull/2789#pullrequestreview-269057207
- `output_names` should move into `TranspileConfig`, and its parsing should be split out similar to the other arguments to `transpile`
- `transpile` should raise if passed an `output_names` that's not a `str` or a `list`
- Update docstring to include that `output_names` is strictly 1-to-1 or n-to-n (unlike other transpile args that will do 1-to-n broadcasting.)
- Rename `output_names` to `output_name`



Discussion:

Resolving patch:
diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py
--- a/qiskit/compiler/transpile.py
+++ b/qiskit/compiler/transpile.py
@@ -31,7 +31,7 @@ def transpile(circuits,
               basis_gates=None, coupling_map=None, backend_properties=None,
               initial_layout=None, seed_transpiler=None,
               optimization_level=None,
-              pass_manager=None, callback=None, output_names=None):
+              pass_manager=None, callback=None, output_name=None):
     """Transpile one or more circuits, according to some desired transpilation targets.
 
     All arguments may be given as either singleton or list. In case of list,
@@ -158,8 +158,9 @@ def callback_func(**kwargs):
                     ...
 
                 transpile(circ, callback=callback_func)
-        output_names (str or list[str]) :
-            A list with strings to identify the output circuits
+        output_name (str or list[str]) :
+            A list with strings to identify the output circuits. The length of
+            `list[str]` should be exactly the length of `circuits` parameter.
     Returns:
         QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s).
 
@@ -181,7 +182,7 @@ def callback_func(**kwargs):
     transpile_configs = _parse_transpile_args(circuits, backend, basis_gates, coupling_map,
                                               backend_properties, initial_layout,
                                               seed_transpiler, optimization_level,
-                                              pass_manager, callback)
+                                              pass_manager, callback, output_name)
     # Check circuit width against number of qubits in coupling_map(s)
     coupling_maps_list = list(config.coupling_map for config in transpile_configs)
     for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list):
@@ -196,36 +197,7 @@ def callback_func(**kwargs):
                                       'in the coupling_map')
     # Transpile circuits in parallel
     circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_configs)))
-    # naming and returning circuits
-    # output_names could be either a string or a list
-    if output_names is not None:
-        if isinstance(output_names, str):
-            # single circuit
-            if len(circuits) == 1:
-                circuits[0].name = output_names
-            # multiple circuits
-            else:
-                raise TranspilerError("Expected a list object of length equal " +
-                                      "to that of the number of circuits " +
-                                      "being transpiled")
-        elif isinstance(output_names, list):
-            # single circuit
-            if len(circuits) == 1:
-                if len(output_names) == 1:
-                    circuits[0].name = output_names[0]
-                else:
-                    raise TranspilerError("the length of output_names list "
-                                          + "must be equal to the number of "
-                                          + "transpiled circuits")
-            # multiple circuits
-            else:
-                if len(circuits) == len(output_names):
-                    for index, circuit in enumerate(circuits):
-                        circuit.name = output_names[index]
-                else:
-                    raise TranspilerError("the length of output_names list "
-                                          + "must be equal to the number of "
-                                          + "transpiled circuits")
+
     if len(circuits) == 1:
         return circuits[0]
     return circuits
@@ -251,7 +223,7 @@ def _transpile_circuit(circuit_config_tuple):
 def _parse_transpile_args(circuits, backend,
                           basis_gates, coupling_map, backend_properties,
                           initial_layout, seed_transpiler, optimization_level,
-                          pass_manager, callback):
+                          pass_manager, callback, output_name):
     """Resolve the various types of args allowed to the transpile() function through
     duck typing, overriding args, etc. Refer to the transpile() docstring for details on
     what types of inputs are allowed.
@@ -282,10 +254,12 @@ def _parse_transpile_args(circuits, backend,
 
     pass_manager = _parse_pass_manager(pass_manager, num_circuits)
 
+    output_name = _parse_output_name(output_name, circuits)
+
     transpile_configs = []
     for args in zip(basis_gates, coupling_map, backend_properties,
                     initial_layout, seed_transpiler, optimization_level,
-                    pass_manager):
+                    pass_manager, output_name):
         transpile_config = TranspileConfig(basis_gates=args[0],
                                            coupling_map=args[1],
                                            backend_properties=args[2],
@@ -293,7 +267,8 @@ def _parse_transpile_args(circuits, backend,
                                            seed_transpiler=args[4],
                                            optimization_level=args[5],
                                            pass_manager=args[6],
-                                           callback=callback)
+                                           callback=callback,
+                                           output_name=args[7])
         transpile_configs.append(transpile_config)
 
     return transpile_configs
@@ -392,3 +367,32 @@ def _parse_pass_manager(pass_manager, num_circuits):
     if not isinstance(pass_manager, list):
         pass_manager = [pass_manager] * num_circuits
     return pass_manager
+
+
+def _parse_output_name(output_name, circuits):
+    # naming and returning circuits
+    # output_name could be either a string or a list
+    if output_name is not None:
+        if isinstance(output_name, str):
+            # single circuit
+            if len(circuits) == 1:
+                return [output_name]
+            # multiple circuits
+            else:
+                raise TranspilerError("Expected a list object of length equal " +
+                                      "to that of the number of circuits " +
+                                      "being transpiled")
+        elif isinstance(output_name, list):
+            if len(circuits) == len(output_name) and \
+                    all(isinstance(name, str) for name in output_name):
+                return output_name
+            else:
+                raise TranspilerError("The length of output_name list "
+                                      "must be equal to the number of "
+                                      "transpiled circuits and the output_name "
+                                      "list should be strings.")
+        else:
+            raise TranspilerError("The parameter output_name should be a string or a"
+                                  "list of strings: %s was used." % type(output_name))
+    else:
+        return [circuit.name for circuit in circuits]
diff --git a/qiskit/transpiler/transpile_circuit.py b/qiskit/transpiler/transpile_circuit.py
--- a/qiskit/transpiler/transpile_circuit.py
+++ b/qiskit/transpiler/transpile_circuit.py
@@ -59,4 +59,7 @@ def transpile_circuit(circuit, transpile_config):
     if getattr(transpile_config, 'callback', None):
         pass_manager.callback = transpile_config.callback
 
-    return pass_manager.run(circuit)
+    out_circuit = pass_manager.run(circuit)
+    out_circuit.name = transpile_config.output_name
+
+    return out_circuit


Test patch:
diff --git a/test/python/transpiler/test_naming_transpiled_circuits.py b/test/python/transpiler/test_naming_transpiled_circuits.py
--- a/test/python/transpiler/test_naming_transpiled_circuits.py
+++ b/test/python/transpiler/test_naming_transpiled_circuits.py
@@ -41,7 +41,7 @@ def test_single_circuit_name_singleton(self):
         """
 
         trans_cirkie = transpile(self.circuit0, basis_gates=self.basis_gates,
-                                 output_names='transpiled-cirkie')
+                                 output_name='transpiled-cirkie')
         self.assertEqual(trans_cirkie.name, 'transpiled-cirkie')
 
     def test_single_circuit_name_list(self):
@@ -53,7 +53,7 @@ def test_single_circuit_name_list(self):
         Transpile function raises an error.
         """
         trans_cirkie = transpile(self.circuit0, basis_gates=self.basis_gates,
-                                 output_names=['transpiled-cirkie'])
+                                 output_name=['transpiled-cirkie'])
         self.assertEqual(trans_cirkie.name, 'transpiled-cirkie')
 
     def test_single_circuit_and_multiple_name_list(self):
@@ -62,7 +62,7 @@ def test_single_circuit_and_multiple_name_list(self):
         # If List has multiple elements, transpile function must raise error
         with self.assertRaises(TranspilerError):
             transpile(self.circuit0, basis_gates=self.basis_gates,
-                      output_names=["cool-cirkie", "new-cirkie", "dope-cirkie", "awesome-cirkie"])
+                      output_name=["cool-cirkie", "new-cirkie", "dope-cirkie", "awesome-cirkie"])
 
     def test_multiple_circuits_name_singleton(self):
         """Test output_name raise error if a single name is provided to a list of circuits
@@ -71,7 +71,7 @@ def test_multiple_circuits_name_singleton(self):
         """
         # Raise Error if single name given to multiple circuits
         with self.assertRaises(TranspilerError):
-            transpile([self.circuit1, self.circuit2], self.backend, output_names='circ')
+            transpile([self.circuit1, self.circuit2], self.backend, output_name='circ')
 
     def test_multiple_circuits_name_list(self):
         """Test output_name with a list of circuits
@@ -85,7 +85,7 @@ def test_multiple_circuits_name_list(self):
         circuits = [self.circuit1, self.circuit2, self.circuit3]
         # equal lengths
         names = ['awesome-circ1', 'awesome-circ2', 'awesome-circ3']
-        trans_circuits = transpile(circuits, self.backend, output_names=names)
+        trans_circuits = transpile(circuits, self.backend, output_name=names)
         self.assertEqual(trans_circuits[0].name, 'awesome-circ1')
         self.assertEqual(trans_circuits[1].name, 'awesome-circ2')
         self.assertEqual(trans_circuits[2].name, 'awesome-circ3')
@@ -98,7 +98,7 @@ def test_greater_circuits_name_list(self):
         # names list greater than circuits list
         names = ['awesome-circ1', 'awesome-circ2', 'awesome-circ3', 'awesome-circ4']
         with self.assertRaises(TranspilerError):
-            transpile(circuits, self.backend, output_names=names)
+            transpile(circuits, self.backend, output_name=names)
 
     def test_smaller_circuits_name_list(self):
         """ Test output_names list smaller than circuits list
@@ -108,7 +108,7 @@ def test_smaller_circuits_name_list(self):
         # names list smaller than circuits list
         names = ['awesome-circ1', 'awesome-circ2']
         with self.assertRaises(TranspilerError):
-            transpile(circuits, self.backend, output_names=names)
+            transpile(circuits, self.backend, output_name=names)
 
 
 if __name__ == '__main__':