Repository: Qiskit/qiskit

Issue:
crash when set initial_state with complex vector for simulator
<!-- ⚠️ 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**: the master branch (Dec. 4th)
- **Python version**: 3.7.1
- **Operating system**: macOS 10.13

### What is the current behavior?
Encounter JSON encoding error


### Steps to reproduce the problem
running with the following qasm and setting
```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
u1(3.14159265358979) q[0];

{'shots': 1, 'config': {'initial_state': array([0.93130364-0.02274014j, 0.2641254 +0.2497883j ])}}
```
error message:
```
Traceback (most recent call last):
  File "/Users/rchen/Developer/Quantum/temp/aqua/test/test_operator.py", line 136, in test_create_from_matrix
    non_matrix_mode = op.eval('paulis', circuit, backend, run_config=run_config)[0]
  File "/Users/rchen/Developer/Quantum/temp/aqua/qiskit_aqua/operator.py", line 779, in eval
    has_shared_circuits=has_shared_circuits)
  File "/Users/rchen/Developer/Quantum/temp/aqua/qiskit_aqua/utils/run_circuits.py", line 151, in compile_and_run_circuits
    return _reuse_shared_circuits(circuits, backend, backend_config, compile_config, run_config, qjob_config)
  File "/Users/rchen/Developer/Quantum/temp/aqua/qiskit_aqua/utils/run_circuits.py", line 110, in _reuse_shared_circuits
    show_circuit_summary=show_circuit_summary)
  File "/Users/rchen/Developer/Quantum/temp/aqua/qiskit_aqua/utils/run_circuits.py", line 239, in compile_and_run_circuits
    results.append(job.result(**qjob_config))
  File "/Users/rchen/Developer/Quantum/qiskit-terra-chenrich/qiskit/backends/aer/aerjob.py", line 37, in _wrapper
    return func(self, *args, **kwargs)
  File "/Users/rchen/Developer/Quantum/qiskit-terra-chenrich/qiskit/backends/aer/aerjob.py", line 92, in result
    return self._future.result(timeout=timeout)
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/concurrent/futures/_base.py", line 432, in result
    return self.__get_result()
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/Users/rchen/Developer/Quantum/qiskit-terra-chenrich/qiskit/backends/aer/statevector_simulator.py", line 71, in _run_job
    result = super()._run_job(job_id, qobj)
  File "/Users/rchen/Developer/Quantum/qiskit-terra-chenrich/qiskit/backends/aer/qasm_simulator.py", line 97, in _run_job
    result = run(qobj_dict, self._configuration.exe)
  File "/Users/rchen/Developer/Quantum/qiskit-terra-chenrich/qiskit/backends/aer/qasm_simulator.py", line 195, in run
    cin = json.dumps(qobj).encode()
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type complex is not JSON serializable
```

### What is the expected behavior?
Without crash.


### Suggested solutions
Terra should parse the complex vector to [ [real, imag] [real, imag]].
(I tried with above format, it will work)




Discussion:
@diego-plan9 can you please look into this? passing a `config` kwarg to `execute()` seems to not serialize correctly...
I think I need more info - but if you are modifying a `Qobj` instance directly, the data (in this case, I assume is appending the `config`) should be as close to the specs as possible, which would mean that the proper way for that information to be stored would be indeed as a bare list of pairs.

@chunfuchen , can you provide more information about how you are using the custom configuration? 
@diego-plan9 
here is the example script
```python
from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister
import qiskit as qk
import numpy as np

np.random.seed(0)

def generate_circuits(num_qubits, parameters, depth=10):
    q = QuantumRegister(num_qubits, name='q')
    c = ClassicalRegister(num_qubits, name='c')
    circuit = QuantumCircuit(q, c)
    param_idx = 0
    for qubit in range(num_qubits):
        circuit.u3(parameters[param_idx], 0.0, 0.0, q[qubit])
        circuit.u1(parameters[param_idx+1], q[qubit])
        param_idx += 2

    for block in range(depth):
        circuit.barrier(q)
        for qubit in range(num_qubits):
            circuit.u3(parameters[param_idx], 0.0, 0.0, q[qubit])
            circuit.u1(parameters[param_idx+1], q[qubit])
            param_idx += 2
    # circuit.barrier(q)
    # circuit.measure(q, c)

    return circuit

num_circuits = 10
num_qubits = 5
depth = 2

#work
config = {'config': {'initial_state': [[0.93130364, -0.02274014], [0.2641254, 0.2497883]]}}
# does not work
# config = {'config': {'initial_state': np.asarray([0.93130364-0.02274014j, 0.2641254 +0.2497883j ])}}

num_parameters = num_qubits * (depth + 1) * 2
circuits = [generate_circuits(num_qubits, np.random.rand(num_parameters), depth) for _ in range(num_circuits)]

my_backend = qk.Aer.get_backend('statevector_simulator')
qobj = qk.compile(circuits=circuits, backend=my_backend, seed=0, config=config)

qjob = my_backend.run(qobj)
result = qjob.result()

print(result.get_statevector(circuits[0]))
```
Thanks @chunfuchen ! I think there are several forces at play here:
* the `config` is indeed appended to the `Qobj` "directly" in `circuits_to_qobj`. If we still consider `Qobj` to be a rather dummy and bare container, it would make sense that it is stored in the right format (ie. complex as tuple) one it reaches that point.
* whether we can limit all the possible inputs for `config` and reach some kind of universal conversion - which I think we realistically can't . Since it is a field that is loosely defined, and per-backend type, we might be able to perform a "preprocessing" of sorts in the backends, where they might know what configurations are valid for their needs.
* backwards-compatibility: I'm not sure what was the expected behaviour pre-0.7! If possible, it would be nice to preserve it.

So I'm kind of rebounding the question and the decision to @ajavadia , and in general, the compilers project - we have several options, from specifying that the `config` should be passed in qobj-like format (the second option mentioned by @chunfuchen ), to only perform the conversion on a subset of configurations that we know are supported, to fully delegating on backends. Any ideas?
I would say that it should be possible to take a state vector returned by `results.get_statevector()` and feed it into the circuit in `initial_state` with no conversion by the user.  Since the former is a NumPy array, the latter should accept that as an input.
@diego-plan9 I found a related bug to this in `QobItem`:

The line

```python
if isinstance(obj, numpy.ndarray):
    return obj.tolist()
```

should be

```python
if isinstance(obj, numpy.ndarray):
     return cls._expand_item(obj.tolist())
```

to recursively parse arse a complex array, otherwise it will only convert the array to a list, but not serialize the inner complex numbers correctly.

Resolving patch:
diff --git a/qiskit/qobj/_qobj.py b/qiskit/qobj/_qobj.py
--- a/qiskit/qobj/_qobj.py
+++ b/qiskit/qobj/_qobj.py
@@ -50,8 +50,6 @@ def _expand_item(cls, obj):
             return [cls._expand_item(item) for item in obj]
         if isinstance(obj, dict):
             return {key: cls._expand_item(value) for key, value in obj.items()}
-        if isinstance(obj, QobjItem):
-            return obj.as_dict()
         if isinstance(obj, numpy.integer):
             return int(obj)
         if isinstance(obj, numpy.float):
@@ -61,9 +59,11 @@ def _expand_item(cls, obj):
         if isinstance(obj, sympy.Basic):
             return float(obj.evalf())
         if isinstance(obj, numpy.ndarray):
-            return obj.tolist()
+            return cls._expand_item(obj.tolist())
         if isinstance(obj, complex):
             return [obj.real, obj.imag]
+        if hasattr(obj, 'as_dict'):
+            return obj.as_dict()
         return obj
 
     @classmethod


Test patch: