Genetic algorithm to find the minimum of a three-variable function

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
4
down vote

favorite
1












I've implemented a simple genetic algorithm for continuous floating point parameter spaces and without recombination. However, the way I'm passing in parameters and acquiring results from multiprocessing feels wrong. Should I be using concurrent.futures?



The code below uses the genetic algorithm to find the minimum of the equation x^2+y^2+z/10 over the parameter space -2 < x < 0, 0 < y < 2 and 10 < z < 11, but I'd like to keep the code easy to modify for various parameter spaces and evaluation functions.



import numpy as np

import multiprocessing
from collections import OrderedDict
import os
import time


def eval_iter(arg_lst, l_lst):
for c_i, args in enumerate(arg_lst):
yield c_i, args, l_lst


def eval_func(c_i, args, l_lst):
assert len(args) == 3
x = args[0]
y = args[1]
z = args[2]
res = x**2 + y**2 + z/10
print(f"Eval x, y, z: res")
l_lst[c_i] = res


if __name__ == '__main__':

generation_num = 10
child_num = 5

space = OrderedDict((
('x', (-2., 0.)),
('y', (0., 2.)),
('z', (10., 11.))
))

params = OrderedDict([(nm, ) for nm in space.keys()])
for nm, v_range in space.items():
params[nm] = np.random.uniform(v_range[0], v_range[1], size=child_num)

arg_list =
for c_n in range(child_num):
arg_list.append([val[c_n] for val in params.values()])

manager = multiprocessing.Manager()
loss_lst = manager.list([np.inf for i in range(child_num)])

for r_n in range(generation_num):
with multiprocessing.Pool(os.cpu_count()) as pool:
pool.starmap(eval_func, eval_iter(arg_list, loss_lst))

fittest_idx = int(np.argmin(loss_lst))
base_args = arg_list[fittest_idx]
print(f"Best base_argsn")

# mutate offspring from fittest individual
params = OrderedDict([(nm, ) for nm in space.keys()])
for s_i, (nm, v_range) in enumerate(space.items()):
std = (v_range[1] - v_range[0]) / 2
noise = np.random.normal(0, std, size=child_num)
new_param = base_args[s_i] + noise
params[nm] = np.clip(new_param, v_range[0], v_range[1])

arg_list =
for c_n in range(child_num):
arg_list.append([val[c_n] for val in params.values()])

loss_lst = manager.list([np.inf for i in range(child_num)])






share|improve this question



























    up vote
    4
    down vote

    favorite
    1












    I've implemented a simple genetic algorithm for continuous floating point parameter spaces and without recombination. However, the way I'm passing in parameters and acquiring results from multiprocessing feels wrong. Should I be using concurrent.futures?



    The code below uses the genetic algorithm to find the minimum of the equation x^2+y^2+z/10 over the parameter space -2 < x < 0, 0 < y < 2 and 10 < z < 11, but I'd like to keep the code easy to modify for various parameter spaces and evaluation functions.



    import numpy as np

    import multiprocessing
    from collections import OrderedDict
    import os
    import time


    def eval_iter(arg_lst, l_lst):
    for c_i, args in enumerate(arg_lst):
    yield c_i, args, l_lst


    def eval_func(c_i, args, l_lst):
    assert len(args) == 3
    x = args[0]
    y = args[1]
    z = args[2]
    res = x**2 + y**2 + z/10
    print(f"Eval x, y, z: res")
    l_lst[c_i] = res


    if __name__ == '__main__':

    generation_num = 10
    child_num = 5

    space = OrderedDict((
    ('x', (-2., 0.)),
    ('y', (0., 2.)),
    ('z', (10., 11.))
    ))

    params = OrderedDict([(nm, ) for nm in space.keys()])
    for nm, v_range in space.items():
    params[nm] = np.random.uniform(v_range[0], v_range[1], size=child_num)

    arg_list =
    for c_n in range(child_num):
    arg_list.append([val[c_n] for val in params.values()])

    manager = multiprocessing.Manager()
    loss_lst = manager.list([np.inf for i in range(child_num)])

    for r_n in range(generation_num):
    with multiprocessing.Pool(os.cpu_count()) as pool:
    pool.starmap(eval_func, eval_iter(arg_list, loss_lst))

    fittest_idx = int(np.argmin(loss_lst))
    base_args = arg_list[fittest_idx]
    print(f"Best base_argsn")

    # mutate offspring from fittest individual
    params = OrderedDict([(nm, ) for nm in space.keys()])
    for s_i, (nm, v_range) in enumerate(space.items()):
    std = (v_range[1] - v_range[0]) / 2
    noise = np.random.normal(0, std, size=child_num)
    new_param = base_args[s_i] + noise
    params[nm] = np.clip(new_param, v_range[0], v_range[1])

    arg_list =
    for c_n in range(child_num):
    arg_list.append([val[c_n] for val in params.values()])

    loss_lst = manager.list([np.inf for i in range(child_num)])






    share|improve this question























      up vote
      4
      down vote

      favorite
      1









      up vote
      4
      down vote

      favorite
      1






      1





      I've implemented a simple genetic algorithm for continuous floating point parameter spaces and without recombination. However, the way I'm passing in parameters and acquiring results from multiprocessing feels wrong. Should I be using concurrent.futures?



      The code below uses the genetic algorithm to find the minimum of the equation x^2+y^2+z/10 over the parameter space -2 < x < 0, 0 < y < 2 and 10 < z < 11, but I'd like to keep the code easy to modify for various parameter spaces and evaluation functions.



      import numpy as np

      import multiprocessing
      from collections import OrderedDict
      import os
      import time


      def eval_iter(arg_lst, l_lst):
      for c_i, args in enumerate(arg_lst):
      yield c_i, args, l_lst


      def eval_func(c_i, args, l_lst):
      assert len(args) == 3
      x = args[0]
      y = args[1]
      z = args[2]
      res = x**2 + y**2 + z/10
      print(f"Eval x, y, z: res")
      l_lst[c_i] = res


      if __name__ == '__main__':

      generation_num = 10
      child_num = 5

      space = OrderedDict((
      ('x', (-2., 0.)),
      ('y', (0., 2.)),
      ('z', (10., 11.))
      ))

      params = OrderedDict([(nm, ) for nm in space.keys()])
      for nm, v_range in space.items():
      params[nm] = np.random.uniform(v_range[0], v_range[1], size=child_num)

      arg_list =
      for c_n in range(child_num):
      arg_list.append([val[c_n] for val in params.values()])

      manager = multiprocessing.Manager()
      loss_lst = manager.list([np.inf for i in range(child_num)])

      for r_n in range(generation_num):
      with multiprocessing.Pool(os.cpu_count()) as pool:
      pool.starmap(eval_func, eval_iter(arg_list, loss_lst))

      fittest_idx = int(np.argmin(loss_lst))
      base_args = arg_list[fittest_idx]
      print(f"Best base_argsn")

      # mutate offspring from fittest individual
      params = OrderedDict([(nm, ) for nm in space.keys()])
      for s_i, (nm, v_range) in enumerate(space.items()):
      std = (v_range[1] - v_range[0]) / 2
      noise = np.random.normal(0, std, size=child_num)
      new_param = base_args[s_i] + noise
      params[nm] = np.clip(new_param, v_range[0], v_range[1])

      arg_list =
      for c_n in range(child_num):
      arg_list.append([val[c_n] for val in params.values()])

      loss_lst = manager.list([np.inf for i in range(child_num)])






      share|improve this question













      I've implemented a simple genetic algorithm for continuous floating point parameter spaces and without recombination. However, the way I'm passing in parameters and acquiring results from multiprocessing feels wrong. Should I be using concurrent.futures?



      The code below uses the genetic algorithm to find the minimum of the equation x^2+y^2+z/10 over the parameter space -2 < x < 0, 0 < y < 2 and 10 < z < 11, but I'd like to keep the code easy to modify for various parameter spaces and evaluation functions.



      import numpy as np

      import multiprocessing
      from collections import OrderedDict
      import os
      import time


      def eval_iter(arg_lst, l_lst):
      for c_i, args in enumerate(arg_lst):
      yield c_i, args, l_lst


      def eval_func(c_i, args, l_lst):
      assert len(args) == 3
      x = args[0]
      y = args[1]
      z = args[2]
      res = x**2 + y**2 + z/10
      print(f"Eval x, y, z: res")
      l_lst[c_i] = res


      if __name__ == '__main__':

      generation_num = 10
      child_num = 5

      space = OrderedDict((
      ('x', (-2., 0.)),
      ('y', (0., 2.)),
      ('z', (10., 11.))
      ))

      params = OrderedDict([(nm, ) for nm in space.keys()])
      for nm, v_range in space.items():
      params[nm] = np.random.uniform(v_range[0], v_range[1], size=child_num)

      arg_list =
      for c_n in range(child_num):
      arg_list.append([val[c_n] for val in params.values()])

      manager = multiprocessing.Manager()
      loss_lst = manager.list([np.inf for i in range(child_num)])

      for r_n in range(generation_num):
      with multiprocessing.Pool(os.cpu_count()) as pool:
      pool.starmap(eval_func, eval_iter(arg_list, loss_lst))

      fittest_idx = int(np.argmin(loss_lst))
      base_args = arg_list[fittest_idx]
      print(f"Best base_argsn")

      # mutate offspring from fittest individual
      params = OrderedDict([(nm, ) for nm in space.keys()])
      for s_i, (nm, v_range) in enumerate(space.items()):
      std = (v_range[1] - v_range[0]) / 2
      noise = np.random.normal(0, std, size=child_num)
      new_param = base_args[s_i] + noise
      params[nm] = np.clip(new_param, v_range[0], v_range[1])

      arg_list =
      for c_n in range(child_num):
      arg_list.append([val[c_n] for val in params.values()])

      loss_lst = manager.list([np.inf for i in range(child_num)])








      share|improve this question












      share|improve this question




      share|improve this question








      edited Mar 2 at 21:52









      200_success

      123k14142399




      123k14142399









      asked Mar 2 at 21:40









      Seanny123

      758822




      758822




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          3
          down vote



          accepted











          1. Better organisation



            You program in its current state is one huge chunk of snippet with all logic inside it. You should consider splitting it into separate smaller functions. The limits on $ x, y, z $ are preset. Consider putting them as a GLOBAL_CONSTANT.




          2. Ideal import order (according to PEP8) is



            • standard library imports

            • related third party imports

            • local application/library specific imports

            os/collections etc. are standard library, and should be imported first, followed by numpy.




          3. pythonify your code



            x = args[0]
            y = args[1]
            z = args[2]


            can be expressed as:



            x, y, z = args


            similarly



            manager.list([np.inf for i in range(child_num)])


            can become



            manager.list([np.inf] * child_num)


          I am not sure about how numpy.argmin works, but I think using None instead of np.inf could be better in the sense that you might extend your program to also find local/global maxima along with minima points.






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188702%2fgenetic-algorithm-to-find-the-minimum-of-a-three-variable-function%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            3
            down vote



            accepted











            1. Better organisation



              You program in its current state is one huge chunk of snippet with all logic inside it. You should consider splitting it into separate smaller functions. The limits on $ x, y, z $ are preset. Consider putting them as a GLOBAL_CONSTANT.




            2. Ideal import order (according to PEP8) is



              • standard library imports

              • related third party imports

              • local application/library specific imports

              os/collections etc. are standard library, and should be imported first, followed by numpy.




            3. pythonify your code



              x = args[0]
              y = args[1]
              z = args[2]


              can be expressed as:



              x, y, z = args


              similarly



              manager.list([np.inf for i in range(child_num)])


              can become



              manager.list([np.inf] * child_num)


            I am not sure about how numpy.argmin works, but I think using None instead of np.inf could be better in the sense that you might extend your program to also find local/global maxima along with minima points.






            share|improve this answer

























              up vote
              3
              down vote



              accepted











              1. Better organisation



                You program in its current state is one huge chunk of snippet with all logic inside it. You should consider splitting it into separate smaller functions. The limits on $ x, y, z $ are preset. Consider putting them as a GLOBAL_CONSTANT.




              2. Ideal import order (according to PEP8) is



                • standard library imports

                • related third party imports

                • local application/library specific imports

                os/collections etc. are standard library, and should be imported first, followed by numpy.




              3. pythonify your code



                x = args[0]
                y = args[1]
                z = args[2]


                can be expressed as:



                x, y, z = args


                similarly



                manager.list([np.inf for i in range(child_num)])


                can become



                manager.list([np.inf] * child_num)


              I am not sure about how numpy.argmin works, but I think using None instead of np.inf could be better in the sense that you might extend your program to also find local/global maxima along with minima points.






              share|improve this answer























                up vote
                3
                down vote



                accepted







                up vote
                3
                down vote



                accepted







                1. Better organisation



                  You program in its current state is one huge chunk of snippet with all logic inside it. You should consider splitting it into separate smaller functions. The limits on $ x, y, z $ are preset. Consider putting them as a GLOBAL_CONSTANT.




                2. Ideal import order (according to PEP8) is



                  • standard library imports

                  • related third party imports

                  • local application/library specific imports

                  os/collections etc. are standard library, and should be imported first, followed by numpy.




                3. pythonify your code



                  x = args[0]
                  y = args[1]
                  z = args[2]


                  can be expressed as:



                  x, y, z = args


                  similarly



                  manager.list([np.inf for i in range(child_num)])


                  can become



                  manager.list([np.inf] * child_num)


                I am not sure about how numpy.argmin works, but I think using None instead of np.inf could be better in the sense that you might extend your program to also find local/global maxima along with minima points.






                share|improve this answer














                1. Better organisation



                  You program in its current state is one huge chunk of snippet with all logic inside it. You should consider splitting it into separate smaller functions. The limits on $ x, y, z $ are preset. Consider putting them as a GLOBAL_CONSTANT.




                2. Ideal import order (according to PEP8) is



                  • standard library imports

                  • related third party imports

                  • local application/library specific imports

                  os/collections etc. are standard library, and should be imported first, followed by numpy.




                3. pythonify your code



                  x = args[0]
                  y = args[1]
                  z = args[2]


                  can be expressed as:



                  x, y, z = args


                  similarly



                  manager.list([np.inf for i in range(child_num)])


                  can become



                  manager.list([np.inf] * child_num)


                I am not sure about how numpy.argmin works, but I think using None instead of np.inf could be better in the sense that you might extend your program to also find local/global maxima along with minima points.







                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Mar 3 at 1:33









                hjpotter92

                4,97611539




                4,97611539






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188702%2fgenetic-algorithm-to-find-the-minimum-of-a-three-variable-function%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Python Lists

                    Aion

                    JavaScript Array Iteration Methods