Sequential
One worker visits every node, in order. Each iteration touches all 15 — wait for the previous one before starting the next. O(n) per iter.
Data parallelism
Nodes are split into 4 chunks. Each chunk goes to its own thread. Threads run the same code ((1-d)/n + d·A[chunk]@ranks) on different rows, then sync.
The fix
Pin BLAS to 1 thread, and create the ThreadPoolExecutor once outside the iteration loop. Without these two changes, NumPy's own threads and ours competed for the same cores and the parallel version ran slower than sequential.