Repository: Qiskit/qiskit

Issue:
shouldn't single-qubit initialization decomposition result in single-qubit instructions?
Not sure if an issue, so tagging as `question` for now. If I'm saying nonsense, an explanation would be enough to close this issue (even if I don't understand it).

When creating an initializer with single-qubit states, I would expect that the decomposition turns into single qubits gates/instructions. In this case<sup>1</sup> I want to initialize two qubits in `|1>`:
```python
from qiskit.extensions.quantum_initializer.initializer import Initialize
initialize = Initialize([ 0, 0, 0, 1])
```
The result of decomposing it, I think, should be something along these lines:
```python
initialize.definition.draw()
```
```
          ┌───┐
q_0: ─|0>─┤ X ├
          ├───┤
q_1: ─|0>─┤ X ├
          └───┘
```

However, it results in a circuit with CNOTs:
```python
from qiskit import transpile
transpile(initialize.definition, basis_gates=['u3', 'cx', 'reset'], optimization_level=0).draw()
```
```
          ┌──────────────┐┌───┐┌───────────────┐┌───┐
q_0: ─|0>─┤ U3(pi/2,0,0) ├┤ X ├┤ U3(-pi/2,0,0) ├┤ X ├
          └┬────────────┬┘└─┬─┘└───────────────┘└─┬─┘
q_1: ─|0>──┤ U3(pi,0,0) ├───■─────────────────────■──
           └────────────┘                            
```

I understand that synthesis is something hard you want to keep general and probably we dont want to handle too many "special cases" (or do we?). To this end, would it help to support something like`Initialize('11')` (a la  `Statevector.from_label`)?  

In summary,
 - should we "aid" `Initialize` to decompose into single-qubit instructions when possible?
    - If No,
        - should `Initialize` synthesis be smarter and minimize/avoid multi-qubit gates when possible?
        - should the transpiler do the leg work here?
    - If Yes,
       - should we support something like `Initialize('11')`, `Initialize('+l')`, and the other labels from `Statevector.from_label`?
       - should we keep the labels when a `Statevector` is created from labels and allow `Initialize(Statevector.from_label('11'))`<sup>2</sup>? 

Footnotes:
<sup>1</sup> <small>This issue is inspired by [a question in stack exchange](https://quantumcomputing.stackexchange.com/q/13202/1859)</small>
<sup>2</sup> <small> Supporting  `Statevector` as  `Initialize`parameter seems intuitive to me in any case. Currently an unexpected `.data` needs to be added `Initialize(Statevector.from_label('11').data)`</small>


Discussion:

Resolving patch:
diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py
--- a/qiskit/extensions/quantum_initializer/initializer.py
+++ b/qiskit/extensions/quantum_initializer/initializer.py
@@ -22,7 +22,9 @@
 from qiskit.circuit import QuantumRegister
 from qiskit.circuit import Instruction
 from qiskit.circuit.exceptions import CircuitError
-from qiskit.circuit.library.standard_gates.x import CXGate
+from qiskit.circuit.library.standard_gates.x import CXGate, XGate
+from qiskit.circuit.library.standard_gates.h import HGate
+from qiskit.circuit.library.standard_gates.s import SGate, SdgGate
 from qiskit.circuit.library.standard_gates.ry import RYGate
 from qiskit.circuit.library.standard_gates.u1 import U1Gate
 from qiskit.circuit.reset import Reset
@@ -43,24 +45,61 @@ class Initialize(Instruction):
     def __init__(self, params):
         """Create new initialize composite.
 
-        params (list): vector of complex amplitudes to initialize to
+        params (str or list):
+          * list: vector of complex amplitudes to initialize to.
+          * string: labels of basis states of the Pauli eigenstates Z, X, Y. See
+               :meth:`~qiskit.quantum_info.states.statevector.Statevector.from_label`.
+               Notice the order of the labels is reversed with respect to the qubit index to
+               be applied to. Example label '01' initializes the qubit zero to `|1>` and the
+               qubit one to `|0>`
         """
-        num_qubits = math.log2(len(params))
+        if isinstance(params, str):
+            self._fromlabel = True
+            num_qubits = len(params)
+        else:
+            self._fromlabel = False
+            num_qubits = math.log2(len(params))
 
-        # Check if param is a power of 2
-        if num_qubits == 0 or not num_qubits.is_integer():
-            raise QiskitError("Desired statevector length not a positive power of 2.")
+            # Check if param is a power of 2
+            if num_qubits == 0 or not num_qubits.is_integer():
+                raise QiskitError("Desired statevector length not a positive power of 2.")
 
-        # Check if probabilities (amplitudes squared) sum to 1
-        if not math.isclose(sum(np.absolute(params) ** 2), 1.0,
-                            abs_tol=_EPS):
-            raise QiskitError("Sum of amplitudes-squared does not equal one.")
+            # Check if probabilities (amplitudes squared) sum to 1
+            if not math.isclose(sum(np.absolute(params) ** 2), 1.0,
+                                abs_tol=_EPS):
+                raise QiskitError("Sum of amplitudes-squared does not equal one.")
 
-        num_qubits = int(num_qubits)
+            num_qubits = int(num_qubits)
 
         super().__init__("initialize", num_qubits, 0, params)
 
     def _define(self):
+        self.definition = self._define_fromlabel() if self._fromlabel else self._define_synthesis()
+
+    def _define_fromlabel(self):
+        q = QuantumRegister(self.num_qubits, 'q')
+        initialize_circuit = QuantumCircuit(q, name='init_def')
+
+        for qubit, param in enumerate(reversed(self.params)):
+            initialize_circuit.append(Reset(), [q[qubit]])
+
+            if param == '1':
+                initialize_circuit.append(XGate(), [q[qubit]])
+            elif param == '+':
+                initialize_circuit.append(HGate(), [q[qubit]])
+            elif param == '-':
+                initialize_circuit.append(XGate(), [q[qubit]])
+                initialize_circuit.append(HGate(), [q[qubit]])
+            elif param == 'r':  # |+i>
+                initialize_circuit.append(HGate(), [q[qubit]])
+                initialize_circuit.append(SGate(), [q[qubit]])
+            elif param == 'l':  # |-i>
+                initialize_circuit.append(HGate(), [q[qubit]])
+                initialize_circuit.append(SdgGate(), [q[qubit]])
+
+        return initialize_circuit
+
+    def _define_synthesis(self):
         """Calculate a subcircuit that implements this initialization
 
         Implements a recursive initialization algorithm, including optimizations,
@@ -83,7 +122,7 @@ def _define(self):
             initialize_circuit.append(Reset(), [qubit])
         initialize_circuit.append(initialize_instr, q[:])
 
-        self.definition = initialize_circuit
+        return initialize_circuit
 
     def gates_to_uncompute(self):
         """Call to create a circuit with gates that take the desired vector to zero.
@@ -257,7 +296,16 @@ def broadcast_arguments(self, qargs, cargs):
         yield flat_qargs, []
 
     def validate_parameter(self, parameter):
-        """Initialize instruction parameter can be int, float, and complex."""
+        """Initialize instruction parameter can be str, int, float, and complex."""
+
+        # Initialize instruction parameter can be str
+        if self._fromlabel:
+            if parameter in ['0', '1', '+', '-', 'l', 'r']:
+                return parameter
+            raise CircuitError("invalid param label {0} for instruction {1}. Label should be "
+                               "0, 1, +, -, l, or r ".format(type(parameter), self.name))
+
+        # Initialize instruction parameter can be int, float, and complex.
         if isinstance(parameter, (int, float, complex)):
             return complex(parameter)
         elif isinstance(parameter, np.number):


Test patch:
diff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py
--- a/test/python/circuit/test_initializer.py
+++ b/test/python/circuit/test_initializer.py
@@ -26,6 +26,7 @@
 from qiskit.quantum_info import state_fidelity
 from qiskit.exceptions import QiskitError
 from qiskit.test import QiskitTestCase
+from qiskit.quantum_info.states.statevector import Statevector
 
 
 class TestInitialize(QiskitTestCase):
@@ -342,6 +343,20 @@ def test_max_number_cnots(self):
 
         self.assertLessEqual(number_cnots, max_cnots)
 
+    def test_from_labels(self):
+        """Initialize from labels."""
+        desired_sv = Statevector.from_label('01+-lr')
+
+        qc = QuantumCircuit(6)
+        qc.initialize('01+-lr', range(6))
+        job = execute(qc, BasicAer.get_backend('statevector_simulator'))
+        result = job.result()
+        statevector = result.get_statevector()
+        fidelity = state_fidelity(statevector, desired_sv)
+        self.assertGreater(
+            fidelity, self._desired_fidelity,
+            "Initializer has low fidelity {:.2g}.".format(fidelity))
+
 
 class TestInstructionParam(QiskitTestCase):
     """Test conversion of numpy type parameters."""