pythonscipy-optimizedifferential-evolution

How to pass shared variable in scipy differential evolution with worker > 2


I want to pass a shared variable in python scipy "differential_evolution" to keep track of experiment number. I can do this with worker=1 , but i am unable to keep track of experiment number with worker > 2. Here is my code:

from scipy.optimize import differential_evolution

def objectiveFunction(x,experiment_no ):
    y=x[0]+x[1]
    # Keep track of experiment number
    experiment_no["exp"]=experiment_no["exp"]+1
    print("This is expriment no :"+ str(experiment_no["exp"]))
    return y

if __name__ == "__main__":
    minRanges=[1.2,10]
    maxRanges=[1.5,20]
    experiment_no={}
    experiment_no["exp"]=0
    try:
        bounds = list(zip(minRanges,maxRanges))
        
        result = differential_evolution(objectiveFunction,bounds, args=(experiment_no,),strategy="best1bin", workers=1,  maxiter=int("2"), updating="deferred", polish=False)        
   
        print('Global minimum [x]:')
        print(result.x)
        print('Function value at global minimum [f(x)]:')
        print(result.fun)
    except :
        exit( sys.exc_info()[:2])


with workers=1 my output is : 
This is expriment no :1
This is expriment no :2
This is expriment no :3
This is expriment no :4
This is expriment no :5
This is expriment no :6
....
but with workers=2 or 3 , my output is :
This is expriment no :1
This is expriment no :1
This is expriment no :11
This is expriment no :1
This is expriment no :1
This is expriment no :1


How can i achieve my below output with workers=2 ?
This is expriment no :1
This is expriment no :2
This is expriment no :3
This is expriment no :4
This is expriment no :5
This is expriment no :6

Solution

  • I'm not sure if it is the best way, but it is working through a thread-safe counter:

    from scipy.optimize import differential_evolution
    from multiprocessing import Process, RawValue, Lock
    
    
    class Counter(object):
        def __init__(self, value=0):
            # RawValue because we don't need it to create a Lock:
            self.val = RawValue('i', value)
            self.lock = Lock()
    
        def increment(self):
            with self.lock:
                self.val.value += 1
                return self.val.value
    
    
    class ExpermientInfo:
        no = Counter()
    
        def increment():
            return ExpermientInfo.no.increment()
    
    
    def objectiveFunction(x):
        y = x[0]+x[1]
        # Keep track of experiment number
        no = ExpermientInfo.increment()
        print("This is expriment no :" + str(no))
        return y
    
    
    if __name__ == "__main__":
        minRanges = [1.2, 10]
        maxRanges = [1.5, 20]
        try:
            bounds = list(zip(minRanges, maxRanges))
            counter = 0
    
            result = differential_evolution(objectiveFunction, bounds, args=(
            ), strategy="best1bin", workers=2,  maxiter=int("2"), updating="deferred", polish=False)
    
            print('Global minimum [x]:')
            print(result.x)
            print('Function value at global minimum [f(x)]:')
            print(result.fun)
        except:
            exit(sys.exc_info()[:2])