Repository: Qiskit/qiskit

Issue:
`transpile` is always serial on windows
`qiskit.tools.parallel_map` defaults to running jobs serially whenever the user is running on windows (since the default behavior of `multiprocess.Pool` under windows is considerably different from than that of mac/linux.) However, there are still some cases (like transpiling for RB) where there may be potential benefit to being able to parallelize jobs under windows.


Discussion:
I tried removing the windows condition on: https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/tools/parallel.py#L110 and running the unittests locally on my windows vm. It doesn't seem to behave reliably. When I ran the whole test suite I had some hangs which I'm still debugging.

The test_parallel tests failed reliably because `test.python.tools.test_parallel.TestParallel.test_parallel_circuit_names` the circuit names are split by pid like on *nix: https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/quantumcircuit.py#L81 (this is probably fixable somehow though). Without that there are duplicated circuit instance numbers which causes the test to fail. Honestly though this test is kind of broken though because all it should really be testing is that 10 circuits got made no that their names are unique if we don't guarantee unique names across processes on all platforms.
Ok got the stack trace from the hang it's:

```
Process SpawnPoolWorker-10:
Traceback (most recent call last):
  File "C:\Program Files\python37\Lib\multiprocessing\process.py", line 297, in _bootstrap
    self.run()
  File "C:\Program Files\python37\Lib\multiprocessing\process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Program Files\python37\Lib\multiprocessing\pool.py", line 110, in worker
    task = get()
  File "C:\Program Files\python37\Lib\multiprocessing\queues.py", line 354, in get
    return _ForkingPickler.loads(res)
AttributeError: Can't get attribute 'func' on <module '__main__' (built-in)>
```
I assume this is because we don't have `fork()` and however python is implementing parallel processes on windows something we need to run is not defined. I'll dig some more into what could be causing this traceback.
That being said I'm not sure which test the hang is actually coming from, I just ran all the tests that have `transpile` in their test id (which was 445 tests) and it worked fine. So I expect it's an edge condition in one specific use of parallel_map that's causing an issue (and is likely why this was disabled). I'll try to track down which test is causing the issue.
It's `test.python.tools.jupyter.test_notebooks.TestJupyter.test_jupyter_jobs_pbars` every other test passes (except for the parallel circuit name one which we can come up for an independent fix for probably) if I enable parallel processes in `parallel_map` on windows.

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -17,7 +17,6 @@
 import copy
 import itertools
 import functools
-import sys
 import warnings
 import numbers
 import multiprocessing as mp
@@ -304,7 +303,7 @@ def cls_prefix(cls):
 
     def _name_update(self):
         """update name of instance using instance number"""
-        if sys.platform != "win32" and not is_main_process():
+        if not is_main_process():
             pid_name = f'-{mp.current_process().pid}'
         else:
             pid_name = ''
diff --git a/qiskit/utils/multiprocessing.py b/qiskit/utils/multiprocessing.py
--- a/qiskit/utils/multiprocessing.py
+++ b/qiskit/utils/multiprocessing.py
@@ -38,15 +38,17 @@ def local_hardware_info():
 
 def is_main_process():
     """Checks whether the current process is the main one"""
-
-    return not (
-        isinstance(mp.current_process(),
-                   (mp.context.ForkProcess, mp.context.SpawnProcess))
-
-        # In python 3.5 and 3.6, processes created by "ProcessPoolExecutor" are not
-        # mp.context.ForkProcess or mp.context.SpawnProcess. As a workaround,
-        # "name" of the process is checked instead.
-        or (sys.version_info[0] == 3
-            and (sys.version_info[1] == 5 or sys.version_info[1] == 6)
-            and mp.current_process().name != 'MainProcess')
-    )
+    if platform.system() == 'Windows':
+        return not isinstance(mp.current_process(), mp.context.SpawnProcess)
+    else:
+        return not (
+            isinstance(mp.current_process(),
+                       (mp.context.ForkProcess, mp.context.SpawnProcess))
+
+            # In python 3.5 and 3.6, processes created by "ProcessPoolExecutor" are not
+            # mp.context.ForkProcess or mp.context.SpawnProcess. As a workaround,
+            # "name" of the process is checked instead.
+            or (sys.version_info[0] == 3
+                and (sys.version_info[1] == 5 or sys.version_info[1] == 6)
+                and mp.current_process().name != 'MainProcess')
+        )


Test patch: