Search code examples
pythonmultiprocessingpython-multiprocessing

Python: ValueError: not enough values to unpack (expected 3, got 1)


I am kinda new to multiprocessing library in python. I have watched couple of lectures on youtube and have implemented basic instances of process from multiprocessing. My problem is, I want to speed up my code by utilizing all the 8 available cores. I asked a similar question in this regard a couple of days back but couldnt elicit a reasonable answer. I went back and did thorough homework and now after having understood the basics of pool(among other things in multiprocessing), I am kinda stuck in a peculier situation. I went through similar problems that were posed here at SO for instance this one-(Python multiprocessing pool map with multiple arguments) and (Python multiprocessing pool.map for multiple arguments). I even implemented the solution mentioned in former. However my code is still throwing this error-

File "minimal_example_so.py", line 84, in <module>
    X,u,t=pool.starmap(solver,[args])
ValueError: not enough values to unpack (expected 3, got 1)

My code looks something like this-

import time 
ti=time.time()
import matplotlib.pyplot as plt
from scipy.integrate import ode
import numpy as np
from numpy import sin,cos,tan,zeros,exp,tanh,dot,array
from matplotlib import rc
import itertools
from const_for_drdo_mod4 import *
plt.style.use('bmh')
import mpltex
from functools import reduce
from multiprocessing import Pool
import multiprocessing
linestyles=mpltex.linestyle_generator()

def zetta(x,spr,c): 
    num=len(x)*len(c)
    Mu=[[] for i in range(len(x))]
    for i in range(len(x)):
        Mu[i]=np.zeros(len(c))
    m=[]
    for i in range(len(x)):
        for j in range(len(c)):
            Mu[i][j]=exp(-.5*((x[i]-c[j])/spr)**2)

    b=list(itertools.product(*Mu))
    for i in range(len(b)):
        m.append(reduce(lambda x,y:x*y,b[i]))

    m=np.array(m)
    S=np.sum(m)
    return m/S

def f(t,Y,a,b,spr,tim,so,k,K,C):
    x1,x2=Y[0],Y[1]
    e=x1-2
    de=-2*x1+a*x2+b*sin(x1)
    s=de+2*e
    xx=[e,de]
    sold,ti=so,tim
    #import pdb;pdb.set_trace()
    theta=Y[2:2+len(C)**len(xx)]
    Z=zetta(xx,spr,C)
    u=dot(Z,theta)
    Z1=list(Z)
    dt=time.time()-ti
    ti=time.time()
    sodt=(s-sold)/dt
    x1dot=de
    x2dot=-x2*cos(x1)+cos(2*x1)*u
    xdot=[x1dot,x2dot]
    thetadot=[-20*number*(sodt+k*s+K*tanh(s))-20*.1*number2 for number,number2 in zip(Z1,theta)]
    sold=s
    ydot=xdot+thetadot
    return [ydot,u]

def solver(t0,y0,t1,dt,a,b,spr,tim,so,k,K,C):
    num=2
    x,t=[[] for i in range(2+len(C)**num)],[]
    u=[]
    r=ode(lambda t,y,a,b,spr,tim,so,k,K,C: f(t,y,a,b,spr,tim,so,k,K,C)[0]).set_integrator('dopri5',method='bdf')
    r.set_initial_value(y0,t0).set_f_params(a,b,spr,tim,so,k,K,C)
    while r.successful() and r.t<t1:
        r.integrate(r.t+dt)
        for i in range(2+len(C)**num):
            x[i].append(r.y[i])

        u.append(f(r.t,r.y,a,b,spr,tim,so,k,K,C)[1])
        t.append(r.t)
    return x,u,t

if __name__=='__main__':
    spr,C=1.5,[-3,-1.5,0,1.5,3]
    num=2
    k,K=2,5
    tim,so=0,0
    a,b=1,2
    y0,T=[0.1,0],100
    x1=[0 for i in range(len(C)**num)]
    x0=y0+x1
    args=(0,x0,T,1e-2,a,b,spr,tim,so,k,K,C)
    pool=multiprocessing.Pool(3)
    X,u,t=pool.starmap(solver,[args])
    #X,u,t=solver(0,x0,T,1e-2,a,b,spr,tim,so,k,K,C)
    nam=["x1","x2"]
    pool.close()
    pool.join()
    plt.figure(1)
    for i in range(len(X[0:2])): 
        plt.plot(t,X[i],label=nam[i])
        plt.legend(loc='upper right')
    plt.figure(2)
    for i in range(len(X[2:])):
        plt.plot(t,X[i])
    plt.figure(3)
    plt.plot(t,u)
    plt.show()

Here I wish to tell that all my arguments are either plain numbers or lists/array of numbers which needs to be passed to the solver method. I tried couple of ways to pass my arguments to the pool using map and even starmap, but all of them were in vain. Kindly help, thanks in advance. PS- my code is working absolutely fine without pool.


Solution

  • You're calling starmap with a list of just one tuple of arguments. The return value will therefore also be a list containing one element - the tuple returned by one call to solver. So you're effectively saying

    X, u, t = [(x1, u1, t1)]
    

    which is why you get the exception you're getting: you can't unpack one value (the returned tuple) into three variables. If you want to use starmap here you'll need to do something like:

     [(X,u,t)] = pool.starmap(solver,[args])
    

    instead. But for only one set of arguments it makes more sense to use apply, since it's designed for a single invocation:

     X,u,t = pool.apply(solver, args)