├── .gitignore
├── .gitmodules
├── LICENSE
├── MANIFEST.in
├── OpenCLGA
├── __init__.py
├── __main__.py
├── evaluation
│ ├── ant
│ │ ├── ant_tsp.cl
│ │ ├── ant_tsp.py
│ │ └── python_ant_tsp.py
│ ├── memory_usage
│ │ ├── main.py
│ │ ├── test_local.c
│ │ └── test_private.c
│ ├── simulated_annealing
│ │ ├── __init__.py
│ │ ├── ocl_sa.cl
│ │ ├── ocl_sa.py
│ │ └── sa.py
│ └── type_casting
│ │ ├── casting.c
│ │ ├── casting.py
│ │ └── casting_vector.c
├── kernel
│ ├── Noise.cl
│ ├── ga_utils.cl
│ ├── ocl_ga.cl
│ ├── shuffler_chromosome.cl
│ ├── simple_chromosome.cl
│ └── simple_gene.cl
├── ocl_ga.py
├── ocl_ga_client.py
├── ocl_ga_server.py
├── ocl_ga_wsserver.py
├── shuffler_chromosome.py
├── simple_chromosome.py
├── simple_gene.py
├── ui
│ ├── android-icon-144x144.png
│ ├── android-icon-192x192.png
│ ├── android-icon-36x36.png
│ ├── android-icon-48x48.png
│ ├── android-icon-72x72.png
│ ├── android-icon-96x96.png
│ ├── apple-icon-114x114.png
│ ├── apple-icon-120x120.png
│ ├── apple-icon-144x144.png
│ ├── apple-icon-152x152.png
│ ├── apple-icon-180x180.png
│ ├── apple-icon-57x57.png
│ ├── apple-icon-60x60.png
│ ├── apple-icon-72x72.png
│ ├── apple-icon-76x76.png
│ ├── apple-icon-precomposed.png
│ ├── apple-icon.png
│ ├── asset-manifest.json
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon-96x96.png
│ ├── favicon.ico
│ ├── index.html
│ ├── manifest.json
│ ├── ms-icon-144x144.png
│ ├── ms-icon-150x150.png
│ ├── ms-icon-310x310.png
│ ├── ms-icon-70x70.png
│ └── static
│ │ ├── css
│ │ ├── main.5da4bf40.css
│ │ └── main.5da4bf40.css.map
│ │ └── js
│ │ ├── main.7371ebc2.js
│ │ └── main.7371ebc2.js.map
├── utilities
│ ├── __init__.py
│ ├── generaltaskthread
│ │ ├── __init__.py
│ │ ├── generaltaskthread.py
│ │ └── logger.py
│ ├── httpwebsocketserver
│ │ ├── ExampleWSServer.py
│ │ ├── HTTPWebSocketsHandler.py
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── index.html
│ │ └── websocket.html
│ └── socketserverclient
│ │ ├── __init__.py
│ │ └── server_client.py
└── utils.py
├── README.rst
├── examples
├── algebra_expansion
│ ├── expansion.py
│ └── kernel
│ │ └── expansion.cl
├── grouping
│ ├── grouping.cl
│ └── grouping.py
├── scheduling - power station
│ ├── power.cl
│ └── power.py
├── taiwan_travel
│ ├── TW319_368Addresses-no-far-islands.json
│ ├── TW319_368Addresses.json
│ ├── TW319_368Addresses.xlsx
│ ├── kernel
│ │ └── taiwan_fitness.cl
│ ├── taiwan_travel_client.py
│ ├── taiwan_travel_server.py
│ └── ui
│ │ ├── android-icon-144x144.png
│ │ ├── android-icon-192x192.png
│ │ ├── android-icon-36x36.png
│ │ ├── android-icon-48x48.png
│ │ ├── android-icon-72x72.png
│ │ ├── android-icon-96x96.png
│ │ ├── apple-icon-114x114.png
│ │ ├── apple-icon-120x120.png
│ │ ├── apple-icon-144x144.png
│ │ ├── apple-icon-152x152.png
│ │ ├── apple-icon-180x180.png
│ │ ├── apple-icon-57x57.png
│ │ ├── apple-icon-60x60.png
│ │ ├── apple-icon-72x72.png
│ │ ├── apple-icon-76x76.png
│ │ ├── apple-icon-precomposed.png
│ │ ├── apple-icon.png
│ │ ├── asset-manifest.json
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon-96x96.png
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── ms-icon-144x144.png
│ │ ├── ms-icon-150x150.png
│ │ ├── ms-icon-310x310.png
│ │ ├── ms-icon-70x70.png
│ │ └── static
│ │ ├── css
│ │ ├── main.10448cc5.css
│ │ └── main.10448cc5.css.map
│ │ └── js
│ │ ├── main.0b7ca193.js
│ │ └── main.0b7ca193.js.map
└── tsp
│ ├── kernel
│ └── simple_tsp.cl
│ └── simple_tsp.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.pyc
6 |
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | env/
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | .eggs/
19 | lib/
20 | lib64/
21 | parts/
22 | sdist/
23 | var/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *,cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # IPython Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # dotenv
80 | .env
81 |
82 | # virtualenv
83 | .venv/
84 | venv/
85 | ENV/
86 |
87 | # Spyder project settings
88 | .spyderproject
89 |
90 | # Rope project settings
91 | .ropeproject
92 |
93 | # OpenCLGA tmp files
94 | final.cl
95 |
96 | # mac ds store
97 | .DS_Store
98 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "utilities/generaltaskthread"]
2 | path = utilities/generaltaskthread
3 | url = https://github.com/kilikkuo/py_generaltaskthread.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Python OpenCL
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE README.rst
2 | recursive-include OpenCLGA/kernel *
3 | recursive-include OpenCLGA/ui *
4 | recursive-exclude * __pycache__
5 | recursive-exclude * .gitignore
6 |
--------------------------------------------------------------------------------
/OpenCLGA/__init__.py:
--------------------------------------------------------------------------------
1 | from .simple_gene import SimpleGene
2 | from .shuffler_chromosome import ShufflerChromosome
3 | from .simple_chromosome import SimpleChromosome
4 | from .ocl_ga import OpenCLGA
5 | from .ocl_ga_server import start_ocl_ga_server
6 | from .ocl_ga_client import start_ocl_ga_client
7 | from . import utils
8 |
--------------------------------------------------------------------------------
/OpenCLGA/__main__.py:
--------------------------------------------------------------------------------
1 | def main():
2 | import argparse
3 | from .ocl_ga_client import start_ocl_ga_client
4 | parser = argparse.ArgumentParser(description='OpenCLGA client help')
5 | parser.add_argument('server', metavar='ip', type=str,
6 | help='the server ip, default : 127.0.0.1', default='127.0.0.1')
7 | parser.add_argument('port', metavar='port', type=int,
8 | help='the server port, default : 12345', default=12345)
9 | args = parser.parse_args()
10 | start_ocl_ga_client(args.server, args.port)
11 |
12 | main()
13 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/ant/ant_tsp.cl:
--------------------------------------------------------------------------------
1 | #include "ga_utils.cl"
2 |
3 | /* ======================== codes for preparing execution =========================== */
4 |
5 | __kernel void ant_tsp_calculate_distances(global float* x, global float* y, global float* distances)
6 | {
7 | int global_p = get_global_id(0);
8 | int global_q = get_global_id(1);
9 |
10 | float2 p = (float2)(x[global_p], y[global_p]);
11 | float2 q = (float2)(x[global_q], y[global_q]);
12 | distances[global_p * NODE_COUNT + global_q] = distance(p, q);
13 | }
14 |
15 |
16 | /* ======================== codes for ant going out =========================== */
17 |
18 | bool ant_tsp_in_visited_nodes(global int* ant_visited_nodes, int last_index, int node_id) {
19 | int i = 0;
20 | for (; i <= last_index; i++) {
21 | if (ant_visited_nodes[i] == node_id) {
22 | return true;
23 | }
24 | }
25 | return false;
26 | }
27 |
28 | void ant_tsp_calculate_path_probabilities(global int* ant_visited_nodes,
29 | global float* tmp_path_probabilities,
30 | global float* tmp_pheromones,
31 | global float* path_pheromones,
32 | global float* path_distances,
33 | int last_index)
34 | {
35 | float total = 0.0;
36 | int current_node = ant_visited_nodes[last_index];
37 | int i;
38 |
39 | for (i = 0; i < NODE_COUNT; i++) {
40 | if (current_node == i) {
41 | tmp_path_probabilities[i] = 0;
42 | tmp_pheromones[i] = 0;
43 | } else if (ant_tsp_in_visited_nodes(ant_visited_nodes, last_index, i)) {
44 | tmp_path_probabilities[i] = 0;
45 | tmp_pheromones[i] = 0;
46 | } else {
47 | tmp_pheromones[i] = pow(path_pheromones[current_node * NODE_COUNT + i], (float) ALPHA) *
48 | pow(100 / path_distances[current_node * NODE_COUNT + i], (float) BETA);
49 | total += tmp_pheromones[i];
50 | }
51 | }
52 |
53 | for (i = 0; i < NODE_COUNT; i++) {
54 | if (tmp_pheromones[i] > 0.00001) {
55 | tmp_path_probabilities[i] = tmp_pheromones[i] / total;
56 | }
57 | }
58 | }
59 |
60 | int ant_tsp_random_choose(global float* tmp_path_probabilities, uint* rand_holder)
61 | {
62 | return random_choose_by_ratio(tmp_path_probabilities, rand_holder, NODE_COUNT);
63 | }
64 |
65 | float ant_tsp_calculate_fitness(global int* ant_visited_nodes, global float* path_distances) {
66 | float fitness = 0.0;
67 | int i = 0;
68 | int start, end;
69 | for (; i < NODE_COUNT; i++) {
70 | start = ant_visited_nodes[i];
71 | if (i < NODE_COUNT - 1) {
72 | end = ant_visited_nodes[i + 1];
73 | } else {
74 | end = ant_visited_nodes[0];
75 | }
76 | fitness += path_distances[start * NODE_COUNT + end];
77 | }
78 | return fitness;
79 | }
80 |
81 | // A kernel work item is an ant
82 | __kernel void ant_tsp_run_ant(global int* ant_visited_nodes,
83 | global float* tmp_path_probabilities,
84 | global float* tmp_pheromones,
85 | global float* path_pheromones,
86 | global float* path_distances,
87 | global float* ant_fitnesses,
88 | global uint* rand_input)
89 | {
90 | int idx = get_global_id(0);
91 | // out of bound kernel task for padding
92 | if (idx >= ANT_COUNT) {
93 | return;
94 | }
95 | // init random numbers.
96 | uint ra[1];
97 | init_rand(rand_input[idx], ra);
98 |
99 | int next_index = 1;
100 |
101 | // The first position of visited_node is not 0. We should move it.
102 | global int* my_tmp_visited_nodes = ant_visited_nodes + idx * NODE_COUNT;
103 | global float* my_tmp_path_probabilities = tmp_path_probabilities + idx * NODE_COUNT;
104 | global float* my_tmp_pheromones = tmp_pheromones + idx * NODE_COUNT;
105 | // choose the first node randomly
106 | my_tmp_visited_nodes[0] = rand_range(ra, NODE_COUNT);
107 | // go all nodes
108 | for (; next_index < NODE_COUNT; next_index++) {
109 | ant_tsp_calculate_path_probabilities(my_tmp_visited_nodes,
110 | my_tmp_path_probabilities,
111 | my_tmp_pheromones,
112 | path_pheromones,
113 | path_distances,
114 | next_index - 1);
115 | my_tmp_visited_nodes[next_index] = ant_tsp_random_choose(my_tmp_path_probabilities, ra);
116 | }
117 | // calculate fitness
118 | ant_fitnesses[idx] = ant_tsp_calculate_fitness(my_tmp_visited_nodes, path_distances);
119 | }
120 |
121 | /* ======================== codes for updating pheromone =========================== */
122 |
123 | /**
124 | * kernel design: each path has its owned kernel
125 | **/
126 | __kernel void ant_tsp_evaporate_pheromones(global float* path_pheromones)
127 | {
128 | int idx = get_global_id(0);
129 | path_pheromones[idx] = (1 - EVAPORATION) * path_pheromones[idx] + 1;
130 | }
131 |
132 | /*
133 | * Pheromones is a two dimensional array:
134 | * from
135 | * 1 2 3 4 5 6
136 | * -----------------------
137 | * t 1|
138 | * o 2|
139 | * 3|
140 | *
141 | * Since from and to can be swapped in TSP case, we have to update both side, (1, 2) and (2, 1), if
142 | * an ant goes through (1, 2).
143 | *
144 | * kernel design: each path has its owned kernel
145 | */
146 | __kernel void ant_tsp_update_pheromones(global int* ant_visited_nodes,
147 | global float* ant_fitnesses,
148 | global float* path_pheromones)
149 | {
150 | int start = get_global_id(0);
151 | int end = get_global_id(1);
152 | // out of bound kernel task for padding
153 | if (start * end >= NODE_COUNT * NODE_COUNT) {
154 | return;
155 | }
156 |
157 | int ant_index, node_index, ant_start, ant_end;
158 | float bonus;
159 | int idx = start * NODE_COUNT + end;
160 |
161 | for (ant_index = 0; ant_index < ANT_COUNT; ant_index++) {
162 | bonus = Q / ant_fitnesses[ant_index];
163 | for (node_index = 0; node_index < NODE_COUNT; node_index++) {
164 | if (node_index < NODE_COUNT - 1) {
165 | ant_start = ant_visited_nodes[ant_index * NODE_COUNT + node_index];
166 | ant_end = ant_visited_nodes[ant_index * NODE_COUNT + node_index + 1];
167 | } else {
168 | ant_start = ant_visited_nodes[ant_index * NODE_COUNT + node_index];
169 | ant_end = ant_visited_nodes[ant_index * NODE_COUNT];
170 | }
171 | if ((ant_start == start && ant_end == end) || (ant_start == end && ant_end == start)) {
172 | path_pheromones[idx] += bonus;
173 | }
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/ant/ant_tsp.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import math
3 | import numpy
4 | import os
5 | import pyopencl as cl
6 | import random
7 | import sys
8 | from OpenCLGA import utils
9 |
10 | class AntTSP():
11 |
12 | def __init__(self, options):
13 | self.__iterations = options['iterations']
14 | self.__ants = options['ants']
15 | # the option for pheromone affecting probability
16 | self.__alpha = options['alpha']
17 | # the option for length affecting probability
18 | self.__beta = options['beta']
19 | # node should be an array of object. The structure of object should be
20 | # 1. x: the position of x a float
21 | # 2. y: the position of y a float
22 | self.__nodes = options['nodes']
23 | self.__node_count = len(self.__nodes)
24 | self.__matrix_size = self.__node_count * self.__node_count
25 | # the option for pheromone evaporating
26 | self.__evaporation = options['evaporation']
27 | # the option for leaking pheromone
28 | self.__q = options['q']
29 | self.__best_result = None
30 | self.__best_fitness = sys.float_info.max
31 |
32 | self.__init_cl()
33 | self.__create_program()
34 |
35 | def __init_cl(self, cl_context=None):
36 | self.__ctx = cl_context if cl_context is not None else cl.create_some_context()
37 | self.__queue = cl.CommandQueue(self.__ctx)
38 |
39 | def __create_program(self):
40 | f = open('ant_tsp.cl', 'r');
41 | fstr = ''.join(f.readlines())
42 | f.close()
43 | ocl_kernel_path = os.path.join(os.path.dirname(os.path.abspath('../../' + __file__)), 'kernel').replace(' ', '\\ ')
44 | options = [
45 | '-D', 'ANT_COUNT={}'.format(self.__ants),
46 | '-D', 'NODE_COUNT={}'.format(self.__node_count),
47 | '-D', 'ALPHA={}'.format(self.__alpha),
48 | '-D', 'BETA={}'.format(self.__beta),
49 | '-D', 'EVAPORATION={}'.format(self.__evaporation),
50 | '-D', 'Q={}'.format(self.__q),
51 | '-I', ocl_kernel_path
52 | ]
53 | self.__prg = cl.Program(self.__ctx, fstr).build(options);
54 |
55 | def __prepare_cl_buffers(self):
56 | mf = cl.mem_flags
57 | # prepare distances buffers
58 | self.__path_distances = numpy.zeros(shape=[self.__node_count, self.__node_count],
59 | dtype=numpy.float32)
60 |
61 | self.__dev_path_distances = cl.Buffer(self.__ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
62 | hostbuf=self.__path_distances)
63 |
64 | # initialize all pheromones of paths with 1
65 | self.__path_pheromones = numpy.empty(shape=[self.__node_count, self.__node_count],
66 | dtype=numpy.float32)
67 | self.__path_pheromones.fill(1)
68 | self.__dev_path_pheromones = cl.Buffer(self.__ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
69 | hostbuf=self.__path_pheromones)
70 |
71 | # prepare buffers for node position: x, y
72 | x = numpy.empty(self.__node_count, dtype=numpy.float32)
73 | y = numpy.empty(self.__node_count, dtype=numpy.float32)
74 | for i in range(self.__node_count):
75 | x[i] = self.__nodes[i][0]
76 | y[i] = self.__nodes[i][1]
77 |
78 | self.__dev_x = cl.Buffer(self.__ctx, mf.READ_ONLY | mf.COPY_HOST_PTR,
79 | hostbuf=x)
80 | self.__dev_y = cl.Buffer(self.__ctx, mf.READ_ONLY | mf.COPY_HOST_PTR,
81 | hostbuf=y)
82 |
83 | # Random number should be given by Host program because OpenCL doesn't have a random number
84 | # generator. We just include one, Noise.cl.
85 | rnum = [random.randint(0, 4294967295) for i in range(self.__ants)]
86 | ## note: numpy.random.rand() gives us a list float32 and we cast it to uint32 at the calling
87 | ## of kernel function. It just views the original byte order as uint32.
88 | self.__dev_rnum = cl.Buffer(self.__ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
89 | hostbuf=numpy.array(rnum, dtype=numpy.uint32))
90 |
91 | # we should prepare buffer memory for each ant on each node.
92 | buffer_size = 4 * self.__node_count * self.__ants
93 | # the visited_nodes is used for storing the path of an ant.
94 | self.__dev_visited_nodes = cl.Buffer(self.__ctx, mf.READ_WRITE, buffer_size)
95 | # the path_probabilities is used for choosing next node
96 | self.__dev_path_probabilities = cl.Buffer(self.__ctx, mf.READ_WRITE, buffer_size)
97 | # the tmp pheromones is used for calcuating probabilities for next node
98 | self.__dev_tmp_pheromones = cl.Buffer(self.__ctx, mf.READ_WRITE, buffer_size)
99 | # this is for keepting fitness value of each ant at a single round.
100 | self.__dev_ant_fitnesses = cl.Buffer(self.__ctx, mf.READ_WRITE, 4 * self.__ants)
101 |
102 | def __calculate_distances(self):
103 | # calculate the distances betwen two points.
104 | self.__prg.ant_tsp_calculate_distances(self.__queue,
105 | (self.__node_count, self.__node_count),
106 | (1, 1),
107 | self.__dev_x,
108 | self.__dev_y,
109 | self.__dev_path_distances)
110 |
111 | def __execute_single_generation(self, generation):
112 | self.__prg.ant_tsp_run_ant(self.__queue,
113 | (self.__ants, ),
114 | (1, ),
115 | self.__dev_visited_nodes,
116 | self.__dev_path_probabilities,
117 | self.__dev_tmp_pheromones,
118 | self.__dev_path_pheromones,
119 | self.__dev_path_distances,
120 | self.__dev_ant_fitnesses,
121 | self.__dev_rnum).wait()
122 |
123 | visited_nodes = numpy.empty(self.__ants * self.__node_count, dtype=numpy.int32)
124 | fitnesses = numpy.empty(self.__ants, dtype=numpy.float32)
125 | cl.enqueue_copy(self.__queue, visited_nodes, self.__dev_visited_nodes)
126 | cl.enqueue_copy(self.__queue, fitnesses, self.__dev_ant_fitnesses).wait();
127 |
128 | best_index = -1;
129 | best_fitness = sys.float_info.max
130 | for index, fitness in enumerate(fitnesses):
131 | if fitness < best_fitness:
132 | best_index = index
133 | best_fitness = fitness
134 |
135 | start_index = best_index * self.__node_count
136 | end_index = (best_index + 1) * self.__node_count
137 | best_result = visited_nodes[start_index:end_index]
138 |
139 |
140 | # update path_pheromones
141 | self.__prg.ant_tsp_evaporate_pheromones(self.__queue,
142 | (self.__node_count, self.__node_count),
143 | (1, 1),
144 | self.__dev_path_pheromones)
145 |
146 | self.__prg.ant_tsp_update_pheromones(self.__queue,
147 | (self.__node_count, self.__node_count),
148 | (1, 1),
149 | self.__dev_visited_nodes,
150 | self.__dev_ant_fitnesses,
151 | self.__dev_path_pheromones).wait()
152 | return (best_result, best_fitness)
153 |
154 | def run(self):
155 | self.__prepare_cl_buffers()
156 | self.__calculate_distances()
157 | for generation in range(self.__iterations):
158 | result = self.__execute_single_generation(generation)
159 | print('best fitness #{}: {}'.format(generation, result[1]))
160 | if result[1] < self.__best_fitness:
161 | self.__best_fitness = result[1]
162 | self.__best_result = result[0]
163 |
164 | return (self.__best_result, self.__best_fitness)
165 |
166 | if __name__ == '__main__':
167 | random.seed(1)
168 | city_info = { city_id: (random.random() * 100, random.random() * 100) for city_id in range(30) }
169 | print('cities:')
170 | print(city_info)
171 | ant = AntTSP({
172 | 'iterations': 100,
173 | 'ants': 10000,
174 | 'alpha': 1.1,
175 | 'beta': 1.5,
176 | 'evaporation': 0.85,
177 | 'q': 10000,
178 | 'nodes': city_info
179 | })
180 |
181 | result = ant.run()
182 | print('Length: {}'.format(result[1]))
183 | print('Shortest Path: ' + ' => '.join(str(g) for g in result[0]))
184 | utils.plot_tsp_result(city_info, result[0])
185 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/ant/python_ant_tsp.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import math
3 | import numpy
4 | import random
5 | import sys
6 | from OpenCLGA import utils
7 |
8 | class PythonAntTSP():
9 |
10 | def __init__(self, options):
11 | self.__iterations = options['iterations']
12 | self.__ants = options['ants']
13 | # the option for pheromone affecting probability
14 | self.__alpha = options['alpha']
15 | # the option for length affecting probability
16 | self.__beta = options['beta']
17 | # node should be an array of object. The structure of object should be
18 | # 1. x: the position of x a float
19 | # 2. y: the position of y a float
20 | self.__nodes = options['nodes']
21 | self.__node_count = len(self.__nodes)
22 | self.__matrix_size = self.__node_count * self.__node_count
23 | # the option for pheromone evaporating
24 | self.__evaporation = options['evaporation']
25 | # the option for leaking pheromone
26 | self.__q = options['q']
27 |
28 | self.__init_member()
29 |
30 | def __init_member(self):
31 | self.__calculate_distances()
32 | # initialize all pheromones of paths with 1
33 | self.__path_pheromones = numpy.empty(shape=[self.__node_count, self.__node_count],
34 | dtype=numpy.float32)
35 | self.__path_pheromones.fill(1)
36 | self.__best_result = None
37 | self.__best_fitness = sys.float_info.max
38 |
39 | def __calculate_distances(self):
40 | # calculate the distances betwen two points.
41 | self.__path_distances = numpy.empty(shape=[self.__node_count, self.__node_count],
42 | dtype=numpy.float32)
43 | for start in range(self.__node_count):
44 | for end in range(self.__node_count):
45 | if start == end:
46 | self.__path_distances[(start, end)] = 0
47 | else:
48 | self.__path_distances[(start, end)] = math.hypot(self.__nodes[start][0] - self.__nodes[end][0],
49 | self.__nodes[start][1] - self.__nodes[end][1])
50 |
51 | def __calculate_path_probabilities(self, visited_nodes):
52 | path_probabilities = numpy.empty(shape=[self.__node_count], dtype=numpy.float32)
53 | pheromones = numpy.empty(shape=[self.__node_count], dtype=numpy.float32)
54 | total = 0.0
55 | current_node = visited_nodes[-1]
56 | for end in range(self.__node_count):
57 | if current_node == end:
58 | pheromones[end] = 0
59 | elif end in visited_nodes:
60 | pheromones[end] = 0
61 | else:
62 | pheromones[end] = (self.__path_pheromones[(current_node, end)] ** self.__alpha) * ((1 / self.__path_distances[(current_node, end)]) ** self.__beta)
63 | total += pheromones[end]
64 |
65 | for end in range(self.__node_count):
66 | if current_node == end:
67 | path_probabilities[end] = 0
68 | elif end in visited_nodes:
69 | path_probabilities[end] = 0
70 | else:
71 | path_probabilities[end] = pheromones[end] / total
72 |
73 | return path_probabilities
74 |
75 | def __random_choose(self, probabilities):
76 | rnd = random.random()
77 | for end in range(self.__node_count):
78 | if probabilities[end] == 0:
79 | continue
80 | elif rnd >= probabilities[end]:
81 | rnd -= probabilities[end]
82 | else:
83 | return end
84 |
85 |
86 | def __update_path_pheromones(self, visited_nodes, fitness):
87 | for index, node in enumerate(visited_nodes):
88 | if index < len(visited_nodes) - 1:
89 | if node < visited_nodes[index + 1]:
90 | self.__path_pheromones[(node, visited_nodes[index + 1])] += self.__q / fitness;
91 | else:
92 | self.__path_pheromones[(visited_nodes[index + 1], node)] += self.__q / fitness;
93 | else:
94 | if node < visited_nodes[0]:
95 | self.__path_pheromones[(node, visited_nodes[0])] += self.__q / fitness;
96 | else:
97 | self.__path_pheromones[(visited_nodes[0], node)] += self.__q / fitness;
98 |
99 | def __calculate_visited_fitness(self, visited_nodes):
100 | result = 0.0;
101 | for index, node in enumerate(visited_nodes):
102 | if index < len(visited_nodes) - 1:
103 | if node < visited_nodes[index + 1]:
104 | result += self.__path_distances[(node, visited_nodes[index + 1])]
105 | else:
106 | result += self.__path_distances[(visited_nodes[index + 1], node)]
107 | else:
108 | if node < visited_nodes[0]:
109 | result += self.__path_distances[(node, visited_nodes[0])]
110 | else:
111 | result += self.__path_distances[(visited_nodes[0], node)]
112 | return result
113 |
114 | def __execute_single_generation(self, generation):
115 | ant_result = []
116 | # send a lot of ants out
117 | for ant in range(self.__ants):
118 | visited_nodes = [random.randint(0, self.__node_count - 1)]
119 | # run all nodes
120 | while len(visited_nodes) < self.__node_count:
121 | probabilities = self.__calculate_path_probabilities(visited_nodes)
122 | visited_nodes.append(self.__random_choose(probabilities))
123 |
124 | # calculate fitness
125 | fitness = self.__calculate_visited_fitness(visited_nodes)
126 | ant_result.append((visited_nodes, fitness))
127 | # update best
128 | if fitness < self.__best_fitness:
129 | self.__best_fitness = fitness
130 | self.__best_result = visited_nodes
131 |
132 | # evaporate the pheromones on each path and increase a base value.
133 | for start, value1 in enumerate(self.__path_pheromones):
134 | for end, value2 in enumerate(value1):
135 | self.__path_pheromones[(start, end)] *= (1 - self.__evaporation)
136 | self.__path_pheromones[(start, end)] += 1
137 |
138 | # update pheromone
139 | for result in ant_result:
140 | self.__update_path_pheromones(result[0], result[1])
141 |
142 | def run(self):
143 | for generation in range(self.__iterations):
144 | self.__execute_single_generation(generation)
145 | print('best fitness #{}: {}'.format(generation, self.__best_fitness))
146 |
147 | return (self.__best_result, self.__best_fitness)
148 |
149 | if __name__ == '__main__':
150 | random.seed(1)
151 | city_info = { city_id: (random.random() * 100, random.random() * 100) for city_id in range(30) }
152 | print('cities:')
153 | print(city_info)
154 | ant = PythonAntTSP({
155 | 'iterations': 20,
156 | 'ants': 100,
157 | 'alpha': 1,
158 | 'beta': 9,
159 | 'evaporation': 0.9,
160 | 'q': 10000,
161 | 'nodes': city_info
162 | })
163 |
164 | result = ant.run()
165 | print('Length: {}'.format(result[1]))
166 | print('Shortest Path: ' + ' => '.join(str(g) for g in result[0]))
167 | utils.plot_tsp_result(city_info, result[0])
168 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/memory_usage/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import os
4 | import sys
5 | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
6 |
7 | import math
8 | import time
9 | import utils
10 | import numpy
11 | import pyopencl as cl
12 |
13 |
14 | def get_context():
15 | contexts = []
16 | platforms = cl.get_platforms()
17 | for platform in platforms:
18 | devices = platform.get_devices()
19 | for device in devices:
20 | try:
21 | context = cl.Context(devices=[device])
22 | contexts.append(context)
23 | except:
24 | print('Can NOT create context from P(%s)-D(%s)'%(platform, device))
25 | continue
26 | return contexts[0] if len(contexts) > 0 else None
27 |
28 | def build_program(ctx, filename):
29 | prog = None
30 | try:
31 | f = open(filename, 'r')
32 | fstr = ''.join(f.readlines())
33 | f.close()
34 | # -Werror : Make all warnings into errors.
35 | # https://www.khronos.org/registry/OpenCL/sdk/2.0/docs/man/xhtml/clBuildProgram.html
36 | prog = cl.Program(ctx, fstr).build(options=['-Werror'], cache_dir=None);
37 | except:
38 | import traceback
39 | traceback.print_exc()
40 | return prog
41 |
42 | def create_queue(ctx):
43 | return cl.CommandQueue(ctx)
44 |
45 | def create_bytearray(ctx, size):
46 | mf = cl.mem_flags
47 | py_buffer = numpy.zeros(size, dtype=numpy.int32)
48 | cl_buffer = cl.Buffer(ctx,
49 | mf.READ_WRITE | mf.COPY_HOST_PTR,
50 | hostbuf=py_buffer)
51 | return py_buffer, cl_buffer
52 |
53 | def create_local_bytearray(size):
54 | return cl.LocalMemory(size)
55 |
56 | def get_work_item_dimension(ctx):
57 | from pyopencl import context_info as ci
58 | from pyopencl import device_info as di
59 | devices = ctx.get_info(ci.DEVICES)
60 | assert len(devices) == 1
61 | dev = devices[0]
62 | # print('Max WI Dimensions : {}'.format(dev.get_info(di.MAX_WORK_ITEM_DIMENSIONS)))
63 | WGSize = dev.get_info(di.MAX_WORK_GROUP_SIZE)
64 | WISize = dev.get_info(di.MAX_WORK_ITEM_SIZES)
65 |
66 | LM = dev.get_info(di.LOCAL_MEM_SIZE)
67 | print('LM Size : {}'.format(LM))
68 | print('Max WG Size : {}'.format(WGSize))
69 | print('Max WI Size : {}'.format(WISize))
70 | return WGSize, WISize
71 |
72 | def get_args(ctx, kernal_func_name, total_work_items):
73 | args = None
74 | py_in, dev_in = create_bytearray(ctx, total_work_items)
75 | py_out, dev_out = create_bytearray(ctx, total_work_items)
76 | if kernal_func_name == 'test_input':
77 | local_array_size = 8192
78 | args = (numpy.int32(total_work_items),
79 | dev_in, dev_out,
80 | create_local_bytearray(4 * local_array_size),
81 | numpy.int32(local_array_size),)
82 | elif kernal_func_name == 'test':
83 | args = (numpy.int32(total_work_items),
84 | dev_in, dev_out,)
85 | return args, (py_out, dev_out)
86 |
87 | def evaluate(ctx, prog, queue, kernal_func_name, total_work_items, work_items_per_group, args, outs = None):
88 |
89 | min_time = None
90 | min_time_gws = None
91 | min_time_lws = None
92 |
93 | max_wgsize, wisize = get_work_item_dimension(ctx)
94 | assert total_work_items <= wisize[0] * wisize[1] * wisize[2]
95 |
96 | iter_global_WIs= int(math.log(total_work_items, 2))
97 | for g_factor in range(iter_global_WIs+1):
98 | print('=========================================== ')
99 | g_f_x = int(math.pow(2, g_factor))
100 | g_wi_size = (int(total_work_items/g_f_x), g_f_x, )
101 | print(' Global Work Group Size : {}'.format(g_wi_size))
102 | iterations = int(math.log(work_items_per_group, 2))
103 | for factor in range(iterations+1):
104 | l_f_x = int(math.pow(2, factor))
105 | l_wi_size = (int(work_items_per_group/l_f_x), l_f_x, )
106 | if l_wi_size[1] > g_wi_size[1] or l_wi_size[0] > g_wi_size[0]:
107 | # Local id dimensions should not exceed global dimensions.
108 | continue
109 | print('-------- ')
110 | print(' Local Work Group Size : {}'.format(l_wi_size))
111 |
112 | divided_wg_info = [int(gwi/l_wi_size[idx]) for idx, gwi in enumerate(g_wi_size)]
113 | print(' Divided Work Groups Info : {}'.format(divided_wg_info))
114 | start_time = time.perf_counter()
115 |
116 | caller = eval('prog.{}'.format(kernal_func_name))
117 | caller(queue, g_wi_size, l_wi_size, *args).wait()
118 |
119 | elapsed_time = time.perf_counter() - start_time
120 | if not min_time:
121 | min_time = elapsed_time
122 | min_time_gws = g_wi_size
123 | min_time_lws = l_wi_size
124 | else:
125 | if elapsed_time <= min_time:
126 | min_time = elapsed_time
127 | min_time_gws = g_wi_size
128 | min_time_lws = l_wi_size
129 |
130 | if outs:
131 | cl.enqueue_read_buffer(queue, outs[1], outs[0])
132 | print('**************************************** ')
133 | print(outs[0])
134 | print(' Best Global WI Info : {}'.format(min_time_gws))
135 | print(' Best Local WI Info : {}'.format(min_time_lws))
136 | print(' Best Elapsed Time : {}'.format(min_time))
137 |
138 | lines = ''
139 | def get_input():
140 | global lines
141 | data = None
142 | try:
143 | if sys.platform in ['linux', 'darwin']:
144 | import select
145 | time.sleep(0.01)
146 | if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
147 | data = sys.stdin.readline().rstrip()
148 | elif sys.platform == 'win32':
149 | import msvcrt
150 | time.sleep(0.01)
151 | if msvcrt.kbhit():
152 | data = msvcrt.getch().decode('utf-8')
153 | if data == '\r':
154 | # Enter is pressed
155 | data = lines
156 | lines = ''
157 | else:
158 | lines += data
159 | print(data)
160 | data = None
161 | else:
162 | pass
163 | except KeyboardInterrupt:
164 | data = 'exit'
165 | return data
166 |
167 | if __name__ == '__main__':
168 | ctx = get_context()
169 | prog = None
170 | print('Enter 1 to test local memory usage')
171 | print('Enter 2 to test private memory usage')
172 | while True:
173 | user_input = get_input()
174 | if user_input == '1':
175 | prog = build_program(ctx, 'test_local.c')
176 | break
177 | elif user_input == '2':
178 | prog = build_program(ctx, 'test_private.c')
179 | break
180 | else:
181 | pass
182 |
183 | total_WorkItems = 1024
184 | # https://software.intel.com/sites/landingpage/opencl/optimization-guide/Work-Group_Size_Considerations.htm
185 | recommended_wi_per_group = 8
186 | kernal_func_name = 'test_input'
187 | args, outs = get_args(ctx, kernal_func_name, total_WorkItems)
188 |
189 | cwg, pwgs, lm, pm = None, None, None, None
190 | if ctx and prog:
191 | cwg, pwgs, lm, pm = utils.calculate_estimated_kernel_usage(prog, ctx, kernal_func_name)
192 | else:
193 | print('Nothing is calculated !')
194 |
195 | queue = create_queue(ctx)
196 | evaluate(ctx, prog, queue, kernal_func_name, total_WorkItems, recommended_wi_per_group, args, outs = outs)
197 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/memory_usage/test_local.c:
--------------------------------------------------------------------------------
1 |
2 | #define LM_SIZE 8192
3 |
4 | __kernel void test(int size, global int* in, global int* out)
5 | {
6 | int2 globalId = (int2)(get_global_id(0), get_global_id(1));
7 | int2 localId = (int2)(get_local_id(0), get_local_id(1));
8 | int2 groupId = (int2)(get_group_id (0), get_group_id (1));
9 | int2 globalSize = (int2)(get_global_size(0), get_global_size(1));
10 | int2 locallSize = (int2)(get_local_size(0), get_local_size(1));
11 | if (globalId.x + globalId.y * globalSize.x >= size) {
12 | return;
13 | }
14 |
15 | int gIdx = globalId.x + globalId.y * globalSize.x;
16 | /*
17 | * Device local memory size can be queired from
18 | import pyopencl as cl
19 | cl.Kernel.get_work_group_info(cl.kernel_work_group_info.LOCAL_MEM_SIZE, cl.Device)
20 | */
21 |
22 | // The local memory usage will be 4*LM_SIZE bytes.
23 | // You may adjust LM_SIZE to test if OUT OF RESOURCES while compiling.
24 | __local int lv[LM_SIZE];
25 | int lIdx = gIdx % LM_SIZE;
26 | lv[lIdx] = globalId.y + globalId.x * globalSize.y;
27 |
28 | /*
29 | * On Intel CPU, 128 bytes private memory is used for the barrier below.
30 | */
31 | // barrier(CLK_LOCAL_MEM_FENCE);
32 | out[gIdx] = lv[lIdx] - gIdx;
33 | }
34 |
35 | __kernel void test_input(int size, global int* in, global int* out, local int* lv, int lv_size)
36 | {
37 | int2 globalId = (int2)(get_global_id(0), get_global_id(1));
38 | int2 localId = (int2)(get_local_id(0), get_local_id(1));
39 | int2 groupId = (int2)(get_group_id (0), get_group_id (1));
40 | int2 globalSize = (int2)(get_global_size(0), get_global_size(1));
41 | int2 locallSize = (int2)(get_local_size(0), get_local_size(1));
42 | if (globalId.x + globalId.y * globalSize.x >= size) {
43 | return;
44 | }
45 |
46 | int gIdx = globalId.x + globalId.y * globalSize.x;
47 | /*
48 | * Device local memory size can be queired from
49 | import pyopencl as cl
50 | cl.Kernel.get_work_group_info(cl.kernel_work_group_info.LOCAL_MEM_SIZE, cl.Device)
51 | */
52 |
53 | // The local memory usage will be 4*LM_SIZE bytes.
54 | // You may adjust LM_SIZE to test if OUT OF RESOURCES while compiling.
55 | int lIdx = gIdx % lv_size;
56 | lv[lIdx] = globalId.y + globalId.x * globalSize.y;
57 | if (lIdx >= 512) {
58 | lv[lIdx] -= 128;
59 | }
60 | /*
61 | * On Intel CPU, 128 bytes private memory is used for the barrier below.
62 | */
63 | // barrier(CLK_LOCAL_MEM_FENCE);
64 | out[gIdx] = lv[lIdx] - gIdx;
65 | }
66 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/memory_usage/test_private.c:
--------------------------------------------------------------------------------
1 |
2 | int calcutate(int index)
3 | {
4 | int a = (index * 32) & 128;
5 | return a;
6 | }
7 |
8 | #define PM_SIZE 10240 + 4096 + 200
9 |
10 | __kernel void test(int size, global int* in, global int* out)
11 | {
12 | int idx = get_global_id(0);
13 | // out of bound kernel task for padding
14 |
15 | if (idx >= size) {
16 | return;
17 | }
18 |
19 | /*
20 | On Intel CPU, the private memroy limitation is 64 KB.
21 | */
22 | int ga[PM_SIZE] = {1};
23 |
24 | /*
25 | On Intel CPU, private memory will be used more efficient if the barrier below is used.
26 | That is, we can create larger ga.
27 | */
28 | // barrier(CLK_LOCAL_MEM_FENCE);
29 |
30 | out[idx] = ga[idx%PM_SIZE];
31 | }
32 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/simulated_annealing/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/evaluation/simulated_annealing/__init__.py
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/simulated_annealing/ocl_sa.cl:
--------------------------------------------------------------------------------
1 |
2 | #include "ga_utils.cl"
3 |
4 | float calc_linear_distance(float x1, float y1, float x2, float y2)
5 | {
6 | return sqrt(pown(x2 - x1, 2) + pown(y2 - y1, 2));
7 | }
8 |
9 | float calc_cost(local int* solution, global float* xy)
10 | {
11 | float cost = 0.0;
12 | int next_i, x, y, nx, ny;
13 | for (int i = 0; i < SOLUTION_SIZE; i++) {
14 | next_i = (i+1) % SOLUTION_SIZE;
15 | x = xy[solution[i]*2];
16 | y = xy[solution[i]*2+1];
17 | nx = xy[solution[next_i]*2];
18 | ny = xy[solution[next_i]*2+1];
19 | cost += calc_linear_distance(x, y, nx, ny);
20 | }
21 | return cost;
22 | }
23 |
24 | float acceptance_probability(float old_cost, float new_cost, float temperature)
25 | {
26 | if (new_cost < old_cost) {
27 | return 1.0;
28 | }
29 | return exp((old_cost - new_cost) / temperature);
30 | }
31 |
32 | void find_neighbor(uint* rand_holder, local int* ori_solution,
33 | local int* neighbor_solution)
34 | {
35 | int rndIdxA, rndIdxB;
36 | rndIdxA = rand_range(rand_holder, SOLUTION_SIZE);
37 | rndIdxB = rand_range(rand_holder, SOLUTION_SIZE);
38 | while (rndIdxA == rndIdxB) {
39 | rndIdxB = rand_range(rand_holder, SOLUTION_SIZE);
40 | }
41 | neighbor_solution[rndIdxA] = ori_solution[rndIdxB];
42 | neighbor_solution[rndIdxB] = ori_solution[rndIdxA];
43 | }
44 |
45 | __kernel void ocl_sa_populate_solutions(int iterations,
46 | float temperature,
47 | float terminate_temperature,
48 | float alpha,
49 | global int* solutions,
50 | global uint* rand_holder,
51 | global float* xy,
52 | global float* costs)
53 | {
54 | int idx = get_global_id(0);
55 |
56 | if (idx >= NUM_OF_SOLUTION) {
57 | return;
58 | }
59 | // create a private variable for each kernel to hold randome number.
60 | uint ra[1];
61 | init_rand(rand_holder[idx], ra);
62 |
63 | int elements[] = ELEMENT_SPACE;
64 | int rndIdx;
65 |
66 | __local int target_solution[SOLUTION_SIZE];
67 | __local int neighbor_solution[SOLUTION_SIZE];
68 |
69 | for (int i = 0; i < SOLUTION_SIZE - 1; i++) {
70 | rndIdx = rand_range(ra, (SOLUTION_SIZE - i - 1));
71 | target_solution[i] = elements[rndIdx];
72 | elements[rndIdx] = elements[SOLUTION_SIZE - i - 1];
73 | }
74 | target_solution[SOLUTION_SIZE - 1] = elements[0];
75 |
76 | int iter;
77 | float ap;
78 | float target_cost, neighbor_cost;
79 | float temp_temperature = temperature;
80 |
81 | target_cost = calc_cost(target_solution, xy);
82 | while (temp_temperature > terminate_temperature) {
83 | iter = 1;
84 | while (iter <= iterations) {
85 | for (int i = 0; i < SOLUTION_SIZE; i++) {
86 | neighbor_solution[i] = target_solution[i];
87 | }
88 | find_neighbor(ra, target_solution, neighbor_solution);
89 | neighbor_cost = calc_cost(neighbor_solution, xy);
90 |
91 | ap = acceptance_probability(target_cost, neighbor_cost, temp_temperature);
92 | if (ap > rand_prob(ra)) {
93 | for (int i = 0; i < SOLUTION_SIZE; i++) {
94 | target_solution[i] = neighbor_solution[i];
95 | target_cost = neighbor_cost;
96 | }
97 | }
98 | iter += 1;
99 | }
100 | temp_temperature = temp_temperature * alpha;
101 | }
102 |
103 | global int* result_solution = solutions + idx * SOLUTION_SIZE;
104 | for (int i = 0; i < SOLUTION_SIZE; i++) {
105 | result_solution[i] = target_solution[i];
106 | }
107 | costs[idx] = target_cost;
108 | }
109 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/simulated_annealing/ocl_sa.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | import sys
4 | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
5 |
6 | from abc import ABCMeta
7 | from utils import calc_linear_distance, plot_tsp_result, plot_grouping_result
8 | import math
9 | import numpy
10 | import random
11 | import pyopencl as cl
12 |
13 | from sa import SimulatedAnnealing, TSPSolution
14 |
15 | class OclTSPSolution(TSPSolution):
16 | def __init__(self, city_info):
17 | TSPSolution.__init__(self, city_info)
18 |
19 | self.size_of_solution = len(city_info.keys())
20 | self.num_of_solutions = 100
21 |
22 | @property
23 | def elements_kernel_str(self):
24 | # Chromosome can use this function to declare elements array
25 | elements_str = ', '.join([str(v) for v in list(self.city_info.keys())])
26 | return '{' + elements_str + '}\n'
27 |
28 | def kernelize(self):
29 | candidates = '#define ELEMENT_SPACE ' + self.elements_kernel_str + '\n'
30 | populats = '#define NUM_OF_SOLUTION ' + str(self.num_of_solutions) + '\n'
31 | defines = '#define SOLUTION_SIZE ' + str(self.size_of_solution) + '\n'
32 | return candidates + populats + defines
33 |
34 | def get_solution_info(self):
35 | self.__np_solution = numpy.zeros(self.size_of_solution * self.num_of_solutions, dtype=numpy.int32)
36 | return self.num_of_solutions, self.__np_solution
37 |
38 | def get_cost_buffer(self):
39 | self.__np_costs = numpy.zeros(self.num_of_solutions, dtype=numpy.float32)
40 | return self.__np_costs
41 |
42 | def create_internal_buffer(self, ctx):
43 | cityxy = [(self.city_info[idx][0], self.city_info[idx][1]) for idx in range(len(self.city_info))]
44 | self.__np_cityxy = numpy.array(cityxy, dtype=numpy.float32)
45 |
46 | mf = cl.mem_flags
47 |
48 | self.__dev_cityxy = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
49 | hostbuf=self.__np_cityxy)
50 |
51 | self.__dev_cityxy = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
52 | hostbuf=self.__np_cityxy)
53 |
54 | self.__np_iterations = numpy.int32(self.iterations)
55 | self.__np_temperature = numpy.float32(self.temperature)
56 | self.__np_terminate_temperature = numpy.float32(self.terminate_temperature)
57 | self.__np_alpha = numpy.float32(self.alpha)
58 |
59 | def anneal(self, prog, queue, rand, solutions, costs):
60 | prog.ocl_sa_populate_solutions(queue,
61 | (self.num_of_solutions,),
62 | (1,),
63 | self.__np_iterations,
64 | self.__np_temperature,
65 | self.__np_terminate_temperature,
66 | self.__np_alpha,
67 | solutions,
68 | rand,
69 | self.__dev_cityxy,
70 | costs).wait()
71 |
72 | cl.enqueue_copy(queue, self.__np_solution, solutions).wait()
73 | cl.enqueue_copy(queue, self.__np_costs, costs).wait()
74 | self.plot_best_solution()
75 |
76 | def plot_best_solution(self):
77 | min_cost = float('inf')
78 | min_idx = 0
79 | for idx, cost in enumerate(self.__np_costs):
80 | if cost < min_cost:
81 | min_cost = cost
82 | min_idx = idx
83 |
84 | solution = self.__np_solution[min_idx*self.size_of_solution:(min_idx+1)*self.size_of_solution]
85 | plot_tsp_result(self.city_info, solution)
86 |
87 | def plot_all_solutions(self):
88 | for i in range(self.num_of_solutions):
89 | solution = []
90 | if i == self.num_of_solutions - 1:
91 | solution = self.__np_solution[i * self.size_of_solution:]
92 | else:
93 | solution = self.__np_solution[i * self.size_of_solution:(i+1) * self.size_of_solution]
94 | plot_tsp_result(self.city_info, solution)
95 |
96 | class OpenCLSA(SimulatedAnnealing):
97 | def __init__(self, cls_solution, options):
98 | SimulatedAnnealing.__init__(self, cls_solution)
99 |
100 | extra_path = options.get('extra_include_path', [])
101 | cl_context = options.get('cl_context', None)
102 |
103 | self.__debug_mode = True
104 | self.__init_cl(cl_context, extra_path)
105 | self.__create_program()
106 | self.__init_cl_member()
107 | pass
108 |
109 | def __init_cl(self, cl_context, extra_include_path):
110 | # create OpenCL context, queue, and memory
111 | # NOTE: Please set PYOPENCL_CTX=N (N is the device number you want to use)
112 | # at first if it's in external_process mode, otherwise a exception
113 | # will be thrown, since it's not in interactive mode.
114 | # TODO: Select a reliable device during runtime by default.
115 | self.__ctx = cl_context if cl_context is not None else cl.create_some_context()
116 | self.__queue = cl.CommandQueue(self.__ctx)
117 | self.__include_path = []
118 |
119 | ocl_kernel_path = os.path.join(os.path.dirname(os.path.abspath('../../' + __file__)), 'kernel').replace(' ', '\\ ')
120 | paths = extra_include_path + [ocl_kernel_path]
121 | for path in paths:
122 | escapedPath = path.replace(' ', '^ ') if sys.platform.startswith('win')\
123 | else path.replace(' ', '\\ ')
124 | # After looking into the source code of pyopencl/__init__.py
125 | # '-I' and folder path should be sepearetd. And ' should not included in string path.
126 | self.__include_path.append('-I')
127 | self.__include_path.append(os.path.join(os.getcwd(), escapedPath))
128 |
129 | def __create_program(self):
130 | self.__include_code = self.sas.kernelize()
131 | codes = self.__include_code + '\n'
132 | # codes = self.__args_codes + '\n' +\
133 | # self.__populate_codes + '\n' +\
134 | # self.__evaluate_code + '\n' +\
135 | # self.__include_code + '\n' +\
136 | # self.__fitness_kernel_str
137 |
138 | f = open('ocl_sa.cl', 'r')
139 | fstr = ''.join(f.readlines())
140 | f.close()
141 |
142 | if self.__debug_mode:
143 | fdbg = open('final.cl', 'w')
144 | fdbg.write(codes + fstr)
145 | fdbg.close()
146 |
147 | self.__prg = cl.Program(self.__ctx, codes + fstr).build(self.__include_path);
148 |
149 | def __init_cl_member(self):
150 | self.__np_costs = self.sas.get_cost_buffer()
151 | num_of_solution, self.__np_solution = self.sas.get_solution_info()
152 |
153 | mf = cl.mem_flags
154 |
155 | # Random number should be given by Host program because OpenCL doesn't have a random number
156 | # generator. We just include one, Noise.cl.
157 | rnum = [random.randint(0, 4294967295) for i in range(num_of_solution)]
158 | ## note: numpy.random.rand() gives us a list float32 and we cast it to uint32 at the calling
159 | ## of kernel function. It just views the original byte order as uint32.
160 | self.__dev_rnum = cl.Buffer(self.__ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
161 | hostbuf=numpy.array(rnum, dtype=numpy.uint32))
162 |
163 | self.__dev_costs = cl.Buffer(self.__ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
164 | hostbuf=self.__np_costs)
165 | self.__dev_solution = cl.Buffer(self.__ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
166 | hostbuf=self.__np_solution)
167 |
168 | self.sas.create_internal_buffer(self.__ctx)
169 |
170 | ## To save the annealing state
171 | def save(self):
172 | pass
173 |
174 | ## To restore the annealing state
175 | def restore(self):
176 | pass
177 |
178 | ## Start annealing
179 | def anneal(self):
180 | self.sas.anneal(self.__prg,
181 | self.__queue,
182 | self.__dev_rnum,
183 | self.__dev_solution,
184 | self.__dev_costs)
185 |
186 | pass
187 |
188 | if __name__ == '__main__':
189 | sa = OpenCLSA(OclTSPSolution, {})
190 | sa.anneal()
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/simulated_annealing/sa.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | import sys
4 | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
5 |
6 | from abc import ABCMeta
7 | from utils import calc_linear_distance, plot_tsp_result, plot_grouping_result
8 | import math
9 | import random
10 |
11 | class SAImpl(metaclass = ABCMeta):
12 | def __init__(self):
13 | self.temperature = 1000.0
14 | self.alpha = 0.9
15 | self.terminate_temperature = 0.00001
16 | self.iterations = 500
17 | pass
18 |
19 | ## Get the initial solution to anneal
20 | def get_init_solution(self):
21 | return None
22 | ## Calculate the cost of the solution
23 | def cost(self, solution):
24 | return None
25 | ## Return a new neighbor solution
26 | def neighbor(self, solution):
27 | return None
28 | ## Return a probability to decide whether accpet or not.
29 | def acceptance_probability(self, old_cost, new_cost, temperature):
30 | return None
31 | ## Start annealing
32 | def anneal(self):
33 | solution = self.get_init_solution()
34 | old_cost = self.cost(solution)
35 | # print('1st round : cost = {} '.format(old_cost))
36 | T = self.temperature
37 | T_min = self.terminate_temperature
38 | alpha = self.alpha
39 | while T > T_min:
40 | i = 1
41 | print('T={}'.format(T))
42 | while i <= self.iterations:
43 | new_solution = self.neighbor(solution)
44 | new_cost = self.cost(new_solution)
45 | ap = self.acceptance_probability(old_cost, new_cost, T)
46 | if ap > random.random():
47 | solution = new_solution
48 | old_cost = new_cost
49 | # print('i={} round : cost = {} '.format(T, i, old_cost))
50 | i += 1
51 | T = T*alpha
52 | return solution
53 |
54 | class ClassificationSolution(SAImpl):
55 | def __init__(self, group_info):
56 | SAImpl.__init__(self)
57 | self.group_info = group_info
58 |
59 | def get_init_solution(self):
60 | return self.group_info['init_solution']
61 |
62 | @staticmethod
63 | def get_init_params():
64 | # The number of points randomly generated
65 | num_points = 40
66 | random.seed()
67 | point_ids = list(range(0, num_points))
68 | point_info = {point_id: (random.random() * 100, random.random() * 100) for point_id in point_ids}
69 | pointX = [point_info[v][0] for v in point_info]
70 | pointY = [point_info[v][1] for v in point_info]
71 |
72 | # The number of group you want to divide.
73 | numOfGroups = 5
74 | group_id_set = list(range(0, numOfGroups))
75 | init_solution = [random.randint(0,numOfGroups-1) for x in range(num_points)]
76 | info = { 'num_of_group' : numOfGroups, 'init_solution' : init_solution,
77 | 'X' : pointX, 'Y' : pointY, 'g_set' : set(group_id_set),
78 | 'point_info' : point_info}
79 | return info
80 |
81 | ## For classification, we calculate the total distance among all points in
82 | ## the same group.
83 | def cost(self, solution):
84 | total = len(solution)
85 | cost = 0
86 | for i in range(self.group_info['num_of_group']):
87 | for j, gid in enumerate(solution):
88 | k = (j + 1)
89 | next_gid = gid
90 | while k < total:
91 | next_gid = solution[k]
92 | if gid == i and next_gid == i:
93 | cost += calc_linear_distance(self.group_info['X'][j], self.group_info['Y'][j],
94 | self.group_info['X'][k], self.group_info['Y'][k])
95 | k += 1
96 | return cost
97 |
98 | ## Find a neighbor solution by swapping random two nodes.
99 | def neighbor(self, solution):
100 | neighbor = solution[:]
101 | total = len(solution)
102 | a = random.randint(0, total-1)
103 | b = random.randint(0, total-1)
104 | while a == b:
105 | b = random.randint(0, total-1)
106 | neighbor[a] = solution[b]
107 | neighbor[b] = solution[a]
108 | return neighbor
109 |
110 | def acceptance_probability(self, old_cost, new_cost, temperature):
111 | if new_cost < old_cost:
112 | return 1.0
113 | else:
114 | return math.exp(float(old_cost - new_cost) / temperature)
115 |
116 | def anneal(self):
117 | solution = SAImpl.anneal(self)
118 | plot_grouping_result(self.group_info['g_set'], solution, self.group_info['point_info'])
119 | return solution
120 |
121 | class TSPSolution(SAImpl):
122 | def __init__(self, tsp_info):
123 | SAImpl.__init__(self)
124 | self.tsp_info = tsp_info
125 |
126 | def get_init_solution(self):
127 | return self.tsp_info['init_solution']
128 |
129 | @staticmethod
130 | def get_init_params():
131 | num_cities = 20
132 | random.seed()
133 | city_ids = list(range(0, num_cities))
134 | city_info = {city_id: (random.random() * 100, random.random() * 100) for city_id in city_ids}
135 | solution = list(city_info.keys())
136 | random.shuffle(solution)
137 | tsp_info = {}
138 | tsp_info['init_solution'] = solution
139 | tsp_info['city_info'] = city_info
140 | return tsp_info
141 |
142 | ## For TSP, we calculate the total distance between all cities.
143 | def cost(self, solution):
144 | city_info = self.tsp_info['city_info']
145 | total = len(city_info.keys())
146 | cost = 0
147 | for index, cid in enumerate(solution):
148 | first_city = cid
149 | next_city = solution[(index + 1) % total]
150 |
151 | cost += calc_linear_distance(city_info[first_city][0], city_info[first_city][1],
152 | city_info[next_city][0], city_info[next_city][1])
153 | return cost
154 |
155 | ## Find a neighbor solution by swapping random two nodes.
156 | def neighbor(self, solution):
157 | city_info = self.tsp_info['city_info']
158 | neighbor = solution[:]
159 | total = len(city_info.keys())
160 | a = random.randint(0, total-1)
161 | b = random.randint(0, total-1)
162 | while a == b:
163 | b = random.randint(0, total-1)
164 | neighbor[a] = solution[b]
165 | neighbor[b] = solution[a]
166 | return neighbor
167 |
168 | def acceptance_probability(self, old_cost, new_cost, temperature):
169 | if new_cost < old_cost:
170 | return 1.0
171 | else:
172 | return math.exp(float(old_cost - new_cost) / temperature)
173 |
174 | def anneal(self):
175 | solution = SAImpl.anneal(self)
176 | plot_tsp_result(self.tsp_info['city_info'], solution)
177 | return solution
178 |
179 | class SimulatedAnnealing(object):
180 | def __init__(self, cls_solution):
181 | self.sas = cls_solution(cls_solution.get_init_params())
182 | pass
183 |
184 | ## To save the annealing state
185 | def save(self):
186 | pass
187 |
188 | ## To restore the annealing state
189 | def restore(self):
190 | pass
191 |
192 | ## Start annealing
193 | def anneal(self):
194 | best_solution = self.sas.anneal()
195 | return best_solution
196 |
197 | def main():
198 | print('Input 1 for SA-TSP ; 2 for SA-Classification')
199 | try:
200 | sa = None
201 | int_choice = int(input())
202 | if int_choice == 1:
203 | sa = SimulatedAnnealing(TSPSolution)
204 | elif int_choice == 2:
205 | sa = SimulatedAnnealing(ClassificationSolution)
206 | else:
207 | print('Unsupported input, bye !')
208 | return None
209 | sa.anneal()
210 | except Exception as e:
211 | print('Exception : {}'.format(e))
212 |
213 | if __name__ == '__main__':
214 | main()
215 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/type_casting/casting.c:
--------------------------------------------------------------------------------
1 | typedef struct {
2 | float data[GLOBAL_SIZE];
3 | } TypedStruct;
4 |
5 | __kernel void casting_test(__global const float* a_g, __global const float* b_g,
6 | __global float *res_g) {
7 |
8 | int id = get_global_id(0);
9 | if (id >= GLOBAL_SIZE) {
10 | return;
11 | }
12 | __global TypedStruct* tsA = (__global TypedStruct*) a_g;
13 | __global TypedStruct* tsB = (__global TypedStruct*) b_g;
14 | __global TypedStruct* tsR = (__global TypedStruct*) res_g;
15 | tsR->data[id] = tsA->data[id] + tsB->data[id];
16 | }
17 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/type_casting/casting.py:
--------------------------------------------------------------------------------
1 | import pyopencl as cl
2 | import numpy as np
3 |
4 | def run(vector):
5 | ctx = cl.create_some_context()
6 | queue = cl.CommandQueue(ctx, properties=cl.command_queue_properties.PROFILING_ENABLE)
7 | f = open('casting_vector.c' if vector else 'casting.c', 'r')
8 | fstr = ''.join(f.readlines())
9 | f.close()
10 |
11 | data_size = 100;
12 | global_size = int(data_size / 4) if vector else data_size
13 |
14 | if vector:
15 | struct = 'typedef struct {\n'
16 | code = ' switch(id) {\n'
17 | codeTemp = ' case {0}:\n tsR->data{0} = tsA->data{0} + tsB->data{0};\n break;\n'
18 | for i in range(global_size):
19 | struct += ' float4 data' + str(i) + ';\n'
20 | code += codeTemp.format(i)
21 | struct += '} TypedStruct2;\n'
22 | code += ' }\n'
23 | fstr = fstr.replace('%code_generation%', code);
24 | fstr = '#define GLOBAL_SIZE ' + str(global_size) + '\n' + struct + fstr
25 | else:
26 | fstr = '#define GLOBAL_SIZE ' + str(global_size) + '\n' + fstr;
27 |
28 | print('=' * 50)
29 | print(fstr)
30 | print('-' * 50)
31 |
32 | a_np = np.random.rand(data_size).astype(np.float32)
33 | b_np = np.random.rand(data_size).astype(np.float32)
34 |
35 | mf = cl.mem_flags
36 | a_g = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=a_np)
37 | b_g = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=b_np)
38 |
39 | res_g = cl.Buffer(ctx, mf.WRITE_ONLY, a_np.nbytes)
40 |
41 | prg = cl.Program(ctx, fstr).build();
42 | exec_evt = prg.casting_test(queue, (global_size,), None, a_g, b_g, res_g)
43 | exec_evt.wait()
44 |
45 | res_np = np.empty_like(a_np)
46 | cl.enqueue_copy(queue, res_np, res_g).wait()
47 | print(res_np)
48 |
49 | elapsed = 1e-9 * (exec_evt.profile.end - exec_evt.profile.start)
50 | print('Vector: {0} => Execution time of test: {1}s'.format(vector, elapsed))
51 |
52 | if __name__ == '__main__':
53 | run(False)
54 | run(True)
55 |
--------------------------------------------------------------------------------
/OpenCLGA/evaluation/type_casting/casting_vector.c:
--------------------------------------------------------------------------------
1 |
2 | __kernel void casting_test(__global const float4* a_g, __global const float4* b_g,
3 | __global float4 *res_g) {
4 |
5 | int id = get_global_id(0);
6 | if (id >= (GLOBAL_SIZE)) {
7 | return;
8 | }
9 | __global TypedStruct2* tsA = (__global TypedStruct2*) a_g;
10 | __global TypedStruct2* tsB = (__global TypedStruct2*) b_g;
11 | __global TypedStruct2* tsR = (__global TypedStruct2*) res_g;
12 |
13 | %code_generation%
14 | }
15 |
--------------------------------------------------------------------------------
/OpenCLGA/kernel/ga_utils.cl:
--------------------------------------------------------------------------------
1 | #ifndef __ga_utils__
2 | #define __ga_utils__
3 |
4 | #include "Noise.cl"
5 |
6 | /**
7 | * prints the value of chromosomes. We can also use this function to print a
8 | * single chromome with 1 value of num_of_chromosomes.
9 | * @param *chromosomes (global) the reference of chromosomes array
10 | * @param size_of_chromosome the size of a single chromesome
11 | * @param num_of_chromosomes the number of chromosomes in this array.
12 | * @param *fitnesses (global) the fitness array of all chromosomes. The size of
13 | * this array is num_of_chromosomes.
14 | */
15 | void print_chromosomes(global int* chromosomes, int size_of_chromosome,
16 | int num_of_chromosomes, global float* fitnesses)
17 | {
18 | int idx = get_global_id(0);
19 | if (idx > 0) {
20 | // We use this function to dump all of chromosomes. In most of the cases,
21 | // we shouldn't let all threads to print result because it may give us too
22 | // many information. So, the first thread is the only one can dump infos.
23 | return;
24 | }
25 | for (int c = 0; c < num_of_chromosomes; c++) {
26 | int start = c * size_of_chromosome;
27 | printf("Chromosome[%d]/dist[%f]:", c, fitnesses[c]);
28 | for (int i = 0; i < size_of_chromosome; i++) {
29 | printf("->(%d)", chromosomes[start+i]);
30 | }
31 | printf("\n");
32 | }
33 | printf("\n");
34 | }
35 |
36 | /**
37 | * rand generates a random number between 0 to max value of uint.
38 | * NOTE : Since we cannot create a real random number in kernel,
39 | * By passing in a random value from python, and storing the last
40 | * random value generated from table, we could simulate a pesudo random
41 | * number in kernel.
42 | * @param *holder a pointer of uint for storing the last rand value.
43 | * @return a random uint value.
44 | */
45 | uint rand(uint* holder)
46 | {
47 | uint b = ParallelRNG(holder[0]);
48 | uint c = ParallelRNG2(b, holder[0]);
49 | uint d = ParallelRNG3(c, b, holder[0]);
50 | holder[0] = c;
51 | return d;
52 | }
53 |
54 | /**
55 | * rand_range generates a random number between 0 <= return_vlue < range.
56 | * @param *holder a pointer of uint for storing the last rand value.
57 | * @param range the max value of random number.
58 | * @return a random uint value in the range.
59 | */
60 | uint rand_range(uint* holder, uint range)
61 | {
62 | uint r = rand(holder) % range;
63 | return r;
64 | }
65 |
66 | /**
67 | * rand_range_exclude generates a random number between 0 < return_vlue < range.
68 | * But the value aExcluded is excluded.
69 | * @param *holder a pointer of uint for storing the last rand value.
70 | * @param range the max value of random number.
71 | * @param aExclude the excluded value
72 | * @return a random uint value in the range except aExcluded.
73 | */
74 | uint rand_range_exclude(uint* holder, uint range, uint aExcluded)
75 | {
76 | uint r = rand(holder) % (range - 1);
77 | return r == aExcluded ? range - 1 : r;
78 | }
79 |
80 | /**
81 | * rand_prob generates a random number between 0 < return_vlue < 1 in float.
82 | * @param *holder a pointer of uint for storing the last rand value.
83 | * @return a random float value.
84 | */
85 | float rand_prob(uint* holder)
86 | {
87 | uint r = rand(holder);
88 | float p = r / (float)UINT_MAX;
89 | return p;
90 | }
91 |
92 | /**
93 | * initialze the random seed.
94 | * @param seed the reandom seed.
95 | * @param *holder a pointer of uint for storing the last rand value.
96 | */
97 | void init_rand(int seed, uint* holder)
98 | {
99 | holder[0] = ParallelRNG(seed);
100 | }
101 |
102 | /**
103 | * random choose a chromosome based on the ratio. For GA, we need to choose a
104 | * chromosome for crossover based on fitnesses. If a chromosome with better
105 | * fitness, the probability is higher. The ratio is an array with population in
106 | * size. The value in ratio array is the uniform[0, 1] distribution value. The
107 | * accumulated value of the whole array is 1.
108 | * @param *ratio (global) the probability array for each chromosomes.
109 | * @param *holder a pointer of uint for storing the last rand value.
110 | * @param num_of_chromosomes the size of ratio.
111 | */
112 | int random_choose_by_ratio(global float* ratio, uint* holder,
113 | int num_of_chromosomes)
114 | {
115 |
116 | // generate a random number from between 0 and 1
117 | float rand_choose = rand_prob(holder);
118 | float accumulated = 0.0;
119 | int i;
120 | // random choose a chromosome based on probability of each chromosome.
121 | for (i = 0; i < num_of_chromosomes; i++) {
122 | accumulated += ratio[i];
123 | if (accumulated > rand_choose) {
124 | return i;
125 | }
126 | }
127 | return num_of_chromosomes - 1;
128 | }
129 |
130 | /**
131 | * calc_min_max_fitness find the max and min value among all fitness values.
132 | * @param *fitnesses (global) the fitness value array of all chromosomes
133 | * @param num_of_chromosomes the number of chromosomes
134 | * @param *min (out) for returing the min fitness value
135 | * @param *max (out) for returning the max fitness value
136 | */
137 | void calc_min_max_fitness(global float* fitnesses, int num_of_chromosomes,
138 | float* min, float* max)
139 | {
140 | for (int i = 0; i < num_of_chromosomes; i++) {
141 | max[0] = fmax(fitnesses[i], max[0]);
142 | min[0] = fmin(fitnesses[i], min[0]);
143 | }
144 | }
145 |
146 | /**
147 | * utils_calc_ratio find the best, worst, avg fitnesses among all chromosomes.
148 | * It also calculates the probability for each chromosomes based on their
149 | * fitness value. The best and worst definition is based on the initial options
150 | * of OpenCLGA.
151 | * @param *fitness (global) the fitness value array of all chromosomes
152 | * @param *ratio (global, out) the probability array of each chromosomes
153 | * @param num_of_chromosomes the number of chromosomes.
154 | */
155 | void utils_calc_ratio(global float* fitness,
156 | global float* ratio,
157 | int num_of_chromosomes)
158 | {
159 | float local_min = INT_MAX;
160 | float local_max = 0;
161 | // OPTIMIZATION_FOR_MAX is set at ocl_ga.py
162 | float best;
163 | float worst;
164 | #if OPTIMIZATION_FOR_MAX
165 | calc_min_max_fitness(fitness, num_of_chromosomes, &local_max, &local_min);
166 | best = local_max;
167 | worst = local_min;
168 | #else
169 | calc_min_max_fitness(fitness, num_of_chromosomes, &local_min, &local_max);
170 | best = local_min;
171 | worst = local_max;
172 | #endif
173 |
174 | float temp_worst = worst;
175 | float diffTotal = 0;
176 | int i;
177 | // we use total and diff to calculate the probability for each chromosome
178 | for (i = 0; i < num_of_chromosomes; i++) {
179 | // to have a significant different between better and worst, we use square
180 | // of diff to calculate the probability.
181 | diffTotal += (temp_worst - fitness[i]) * (temp_worst - fitness[i]);
182 | }
183 | // calculate probability for each one
184 | for (i = 0; i < num_of_chromosomes; i++) {
185 | ratio[i] = (temp_worst - fitness[i]) * (temp_worst - fitness[i]) /
186 | diffTotal;
187 | }
188 | }
189 |
190 | #endif
191 |
--------------------------------------------------------------------------------
/OpenCLGA/kernel/ocl_ga.cl:
--------------------------------------------------------------------------------
1 | #include "ga_utils.cl"
2 |
3 | /**
4 | * ocl_ga_calculate_fitness is a wrapper function to call specified fitness
5 | * function. To simplify the implementation of fitness calcuation, we handles
6 | * the multi-threading part and let implementor write the core calculation.
7 | * Note: this is a kernel function and will be called by python.
8 | *
9 | * @param *chromosomes (global) the chromosomes array
10 | * @param *fitness (global) the fitness array for each chromosomes.
11 | * @param FITNESS_ARGS the fitness arguments from ocl_ga options.
12 | */
13 | __kernel void ocl_ga_calculate_fitness(global int* chromosomes,
14 | global float* fitness FITNESS_ARGS)
15 | {
16 | int idx = get_global_id(0);
17 | // out of bound kernel task for padding
18 | if (idx >= POPULATION_SIZE) {
19 | return;
20 | }
21 | // calls the fitness function specified by user and gives the chromosome for
22 | // current thread.
23 | CALCULATE_FITNESS(((global CHROMOSOME_TYPE*) chromosomes) + idx,
24 | fitness + idx,
25 | CHROMOSOME_SIZE, POPULATION_SIZE FITNESS_ARGV);
26 | }
27 |
--------------------------------------------------------------------------------
/OpenCLGA/kernel/simple_chromosome.cl:
--------------------------------------------------------------------------------
1 | #ifndef __oclga_simple_chromosome__
2 | #define __oclga_simple_chromosome__
3 |
4 | #include "ga_utils.cl"
5 |
6 | typedef struct {
7 | int genes[SIMPLE_CHROMOSOME_GENE_SIZE];
8 | } __SimpleChromosome;
9 |
10 | /* ============== populate functions ============== */
11 | /**
12 | * simple_chromosome_do_populate populates a chromosome randomly. Unlike shuffler chromosome, it
13 | * chooses a gene randomly based on gene's element.
14 | * @param *chromosome (global, out) the target chromosome.
15 | * @param *rand_holder the random seed holder.
16 | */
17 | void simple_chromosome_do_populate(global __SimpleChromosome* chromosome,
18 | uint* rand_holder)
19 | {
20 | uint gene_elements_size[] = SIMPLE_CHROMOSOME_GENE_ELEMENTS_SIZE;
21 | for (int i = 0; i < SIMPLE_CHROMOSOME_GENE_SIZE; i++) {
22 | // choose an element randomly based on each gene's element size.
23 | chromosome->genes[i] = rand_range(rand_holder, gene_elements_size[i]);
24 | }
25 | }
26 |
27 | /**
28 | * simple_chromosome_populate populates all chromosomes randomly.
29 | * Note: this is a kernel function and will be called by python.
30 | * @param *cs (global, out) all chromosomes where population to be stored.
31 | * @param *input_rand (global) random seeds.
32 | */
33 | __kernel void simple_chromosome_populate(global int* cs,
34 | global uint* input_rand)
35 | {
36 | int idx = get_global_id(0);
37 | // out of bound kernel task for padding
38 | if (idx >= POPULATION_SIZE) {
39 | return;
40 | }
41 | // create a private variable for each kernel to hold randome number.
42 | uint ra[1];
43 | init_rand(input_rand[idx], ra);
44 | simple_chromosome_do_populate(((global __SimpleChromosome*) cs) + idx,
45 | ra);
46 | input_rand[idx] = ra[0];
47 | }
48 | /* ============== end of populating functions ============== */
49 |
50 | /* ============== mutation functions ============== */
51 | /**
52 | * mutate a single gene of a chromosome.
53 | * @param *chromosome (global) the chromosome for mutation.
54 | * @param *ra random seed holder.
55 | */
56 | void simple_chromosome_do_mutate(global __SimpleChromosome* chromosome,
57 | uint* ra)
58 | {
59 | // create element size list
60 | uint elements_size[] = SIMPLE_CHROMOSOME_GENE_ELEMENTS_SIZE;
61 | uint gene_idx = rand_range(ra, SIMPLE_CHROMOSOME_GENE_SIZE);
62 | // use gene's mutate function to mutate it.
63 | SIMPLE_CHROMOSOME_GENE_MUTATE_FUNC(chromosome->genes + gene_idx,
64 | elements_size[gene_idx], ra);
65 | }
66 | /**
67 | * mutate a single gene of all chromosomes based on the prob_mutate. If a
68 | * chromosome pass the probability, a single gene of it will be mutated.
69 | * @param *chromosome (global) the chromosome for mutation.
70 | * @param *ra (global) random seed holder.
71 | * @param prob_mutate the probability of mutation.
72 | */
73 | __kernel void simple_chromosome_mutate(global int* cs,
74 | global uint* input_rand,
75 | float prob_mutate)
76 | {
77 | int idx = get_global_id(0);
78 | // out of bound kernel task for padding
79 | if (idx >= POPULATION_SIZE) {
80 | return;
81 | }
82 |
83 | uint ra[1];
84 | init_rand(input_rand[idx], ra);
85 | float prob_m = rand_prob(ra);
86 | if (prob_m > prob_mutate) {
87 | input_rand[idx] = ra[0];
88 | return;
89 | }
90 |
91 | simple_chromosome_do_mutate((global __SimpleChromosome*) cs, ra);
92 | }
93 |
94 | /**
95 | * mutate all genes of all chromosomes based on the prob_mutate. Once a
96 | * chromosome passes the probability, all genes of it will be mutated.
97 | * @param *chromosome (global) the chromosome for mutation.
98 | * @param *ra (global) random seed holder.
99 | * @param prob_mutate the probability of mutation.
100 | */
101 | __kernel void simple_chromosome_mutate_all(global int* cs,
102 | global uint* input_rand,
103 | float prob_mutate)
104 | {
105 | int idx = get_global_id(0);
106 | // out of bound kernel task for padding
107 | if (idx >= POPULATION_SIZE) {
108 | return;
109 | }
110 | uint elements_size[] = SIMPLE_CHROMOSOME_GENE_ELEMENTS_SIZE;
111 | int i;
112 | uint ra[1];
113 | init_rand(input_rand[idx], ra);
114 | for (i = 0; i < SIMPLE_CHROMOSOME_GENE_SIZE; i++) {
115 | if (rand_prob(ra) > prob_mutate) {
116 | continue;
117 | }
118 | SIMPLE_CHROMOSOME_GENE_MUTATE_FUNC(cs + i, elements_size[i], ra);
119 | }
120 |
121 | input_rand[idx] = ra[0];
122 | }
123 | /* ============== end of mutation functions ============== */
124 |
125 | /* ============== crossover functions ============== */
126 | /**
127 | * simple_chromosome_calc_ratio uses utils_calc_ratio to find the best, worst,
128 | * avg fitnesses among all chromosomes the probability for each chromosomes
129 | * based on their fitness value.
130 | * Note: this is a kernel function and will be called by python.
131 | * @param *fitness (global) the fitness value array of all chromosomes
132 | * @param *ratio (global, out) the probability array of each chromosomes
133 | * @seealso ::utils_calc_ratio
134 | */
135 | __kernel void simple_chromosome_calc_ratio(global float* fitness,
136 | global float* ratio)
137 | {
138 | int idx = get_global_id(0);
139 | // we use the first kernel to calculate the ratio
140 | if (idx > 0) {
141 | return;
142 | }
143 | utils_calc_ratio(fitness, ratio, POPULATION_SIZE);
144 | }
145 |
146 | /**
147 | * simple_chromosome_pick_chromosomes picks a chromosome randomly based on the
148 | * ratio of each chromosome and copy all genes to p_other for crossover. The
149 | * reason copy to p_other is that OpenCLGA runs crossover at multi-thread mode.
150 | * The picked chromosomes may also be modified at the same time while crossing
151 | * over. If we don't copy them, we may have duplicated genes in a chromosome.
152 | * Note: this is a kernel function and will be called by python.
153 | * @param *cs (global) all chromosomes
154 | * @param *fitness (global) all fitness of chromosomes
155 | * @param *p_other (global) a spared space for storing another chromosome for
156 | * crossover.
157 | * @param *ratio (global) the ratio of all chromosomes.
158 | * @param *input_rand (global) all random seeds.
159 | */
160 | __kernel void simple_chromosome_pick_chromosomes(global int* cs,
161 | global float* fitness,
162 | global int* p_other,
163 | global float* ratio,
164 | global uint* input_rand)
165 | {
166 | int idx = get_global_id(0);
167 | // out of bound kernel task for padding
168 | if (idx >= POPULATION_SIZE) {
169 | return;
170 | }
171 | uint ra[1];
172 | init_rand(input_rand[idx], ra);
173 | global __SimpleChromosome* chromosomes = (global __SimpleChromosome*) cs;
174 | global __SimpleChromosome* other = (global __SimpleChromosome*) p_other;
175 | int i;
176 | // Pick another chromosome as parent_other.
177 | int cross_idx = random_choose_by_ratio(ratio, ra, POPULATION_SIZE);
178 | // copy the chromosome to local memory for cross over
179 | for (i = 0; i < SIMPLE_CHROMOSOME_GENE_SIZE; i++) {
180 | other[idx].genes[i] = chromosomes[cross_idx].genes[i];
181 | }
182 | input_rand[idx] = ra[0];
183 | }
184 |
185 | /**
186 | * simple_chromosome_do_crossover does crossover for all chromosomes.
187 | * If a chromosomes passes the prob_crossover, we pick another chromosome for
188 | * crossover, see simple_chromosome_pick_chromosomes for more information. The
189 | * crossover procedure copys a range of genes from parent 2(p_other) to
190 | * parent 1(cs), like:
191 | * CS1 |----------------------------------|
192 | * |start (copy from parent 2) |end
193 | * |^^^^^^^^^^^^^^^^^^^^^^^^^^^^|
194 | * CS2 |----------------------------------|
195 | */
196 | __kernel void simple_chromosome_do_crossover(global int* cs,
197 | global float* fitness,
198 | global int* p_other,
199 | global uint* input_rand,
200 | float best_fitness,
201 | float prob_crossover)
202 | {
203 | int idx = get_global_id(0);
204 | // out of bound kernel task for padding
205 | if (idx >= POPULATION_SIZE) {
206 | return;
207 | }
208 | uint ra[1];
209 | init_rand(input_rand[idx], ra);
210 |
211 | // keep the shortest path, we have to return here to prevent async barrier
212 | // if someone is returned.
213 | if (fabs(fitness[idx] - best_fitness) < 0.000001) {
214 | input_rand[idx] = ra[0];
215 | return;
216 | } else if (rand_prob(ra) >= prob_crossover) {
217 | input_rand[idx] = ra[0];
218 | return;
219 | }
220 | global __SimpleChromosome* chromosomes = (global __SimpleChromosome*) cs;
221 | global __SimpleChromosome* other = (global __SimpleChromosome*) p_other;
222 | int i;
223 | // keep at least one for .
224 | int start = rand_range(ra, SIMPLE_CHROMOSOME_GENE_SIZE - 1);
225 | int end = start + rand_range(ra, SIMPLE_CHROMOSOME_GENE_SIZE - start);
226 | // copy partial genes from other chromosome
227 | for (i = start; i < end; i++) {
228 | chromosomes[idx].genes[i] = other[idx].genes[i];
229 | }
230 |
231 | input_rand[idx] = ra[0];
232 | }
233 | /* ============== end of crossover functions ============== */
234 | /* ============== elitism ================================= */
235 | /**
236 | * simple_chromosome_get_the_elites get the elites which meet the best_fitness
237 | * Note: this is a kernel function and will be called by python.
238 | * @param top (global) the number of chromosomes in the indices.
239 | * @param *best_indices (global) the index list of top N best fitness chromosomes.
240 | * @param *cs (global) all chromosomes.
241 | * @param *elites (global) elite chromosomes.
242 | */
243 | __kernel void simple_chromosome_get_the_elites(global float* best_indices,
244 | global int* cs,
245 | global int* elites,
246 | int top)
247 | {
248 | int idx = get_global_id(0);
249 | // we use the first kernel to get the best chromosome for now.
250 | if (idx > 0) {
251 | return;
252 | }
253 | int i;
254 | int j;
255 | int index;
256 | global __SimpleChromosome* chromosomes = (global __SimpleChromosome*) cs;
257 | global __SimpleChromosome* elites_chromosome = (global __SimpleChromosome*) elites;
258 | for (i = 0; i < top; i++) {
259 | index = best_indices[i];
260 | for (j = 0; j < SIMPLE_CHROMOSOME_GENE_SIZE; j++) {
261 | elites_chromosome[i].genes[j] = chromosomes[index].genes[j];
262 | }
263 | }
264 | }
265 |
266 | /**
267 | * simple_chromosome_update_the_elites update sorted elites into chromosomes.
268 | * Note: this is a kernel function and will be called by python.
269 | * @param top (local) the number of chromosomes in the indices.
270 | * @param *worst_indices (global) the index list of bottom N worst fitness chromosomes.
271 | * @param *cs (global) all chromosomes.
272 | * @param *fitnesses (global) fitnesses of all chromosomes
273 | * @param *elites (global) elite chromosomes.
274 | * @param *elite_fitnesses (global) fitnesses of all elite chromosomes.
275 | */
276 | __kernel void simple_chromosome_update_the_elites(int top,
277 | global int* worst_indices,
278 | global int* cs,
279 | global int* elites,
280 | global float* fitnesses,
281 | global float* elite_fitnesses)
282 | {
283 | int idx = get_global_id(0);
284 | // we use the first kernel to update all elites and their fitnesses.
285 | if (idx > 0) {
286 | return;
287 | }
288 | int i;
289 | int j;
290 | int index;
291 | global __SimpleChromosome* chromosomes = (global __SimpleChromosome*) cs;
292 | global __SimpleChromosome* elites_chromosome = (global __SimpleChromosome*) elites;
293 | for (i = 0 ; i < top; i++) {
294 | index = worst_indices[i];
295 | for (j = 0; j < SIMPLE_CHROMOSOME_GENE_SIZE; j++) {
296 | chromosomes[index].genes[j] = elites_chromosome[i].genes[j];
297 | }
298 | fitnesses[index] = elite_fitnesses[index];
299 | }
300 | }
301 | /* ============== end of elitism functions ============== */
302 | #endif
303 |
--------------------------------------------------------------------------------
/OpenCLGA/kernel/simple_gene.cl:
--------------------------------------------------------------------------------
1 | #ifndef __oclga_simple_gene__
2 | #define __oclga_simple_gene__
3 |
4 | #include "ga_utils.cl"
5 |
6 | /**
7 | * simle_gene_mutate mutates a single gene. Because we mapped the elements as
8 | * the index, we random select another index as the value of the gene.
9 | * @param *gene (global) the gene we need to mutate
10 | * @param max the size of elements.
11 | * @param *ra the random number holder.
12 | */
13 | void simple_gene_mutate(global int* gene, uint max, uint* ra) {
14 | *gene = rand_range_exclude(ra, max, *gene);
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/OpenCLGA/ocl_ga_wsserver.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import os
3 | import sys
4 | import ssl
5 | import threading
6 | from base64 import b64encode
7 | from socketserver import ThreadingMixIn
8 | from http.server import HTTPServer
9 | from io import StringIO
10 |
11 | from .utilities.generaltaskthread import TaskThread, Task, Logger
12 | from .utilities.httpwebsocketserver import HTTPWebSocketsHandler
13 |
14 | ## A Handler class when the Http request is upgraded to websocket, the following
15 | # methods will be called accordingly.
16 | class HttpWSMessageHandler(HTTPWebSocketsHandler):
17 |
18 | base_path = None
19 | cn_hdlr = None
20 | msg_hdlr = None
21 | dcn_hdlr = None
22 | def on_ws_message(self, message):
23 | if message is None:
24 | message = ''
25 |
26 | self.log_message('websocket received %s', str(message))
27 | if self.msg_hdlr:
28 | self.msg_hdlr(self.client_address, message)
29 |
30 | def on_ws_connected(self):
31 | self.log_message('%s','websocket connected')
32 | if self.cn_hdlr:
33 | self.cn_hdlr(self.client_address, self)
34 |
35 | def on_ws_closed(self):
36 | if self.dcn_hdlr:
37 | self.dcn_hdlr(self.client_address)
38 | self.log_message('%s','websocket closed')
39 |
40 | ## Handle requests in a separate thread.
41 | class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
42 | pass
43 |
44 | ## A task class which intends to call HTTPServer's serve_forever in separated
45 | # thread.
46 | # @param server The Http Server instance
47 | # @param credentials The credentials to be wrapped if secure connection is required.
48 | class HttpWSTask(Task):
49 | def __init__(self, server, credentials = ''):
50 | Task.__init__(self)
51 | self.logger_level = Logger.MSG_ALL ^ Logger.MSG_VERBOSE
52 | self.server = server
53 | self.server.daemon_threads = True
54 | self.server.auth = b64encode(credentials.encode('ascii'))
55 | if credentials:
56 | self.server.socket = ssl.wrap_socket(self.server.socket, certfile='./server.pem', server_side=True)
57 | self.info('Secure https server is created @ port {}.'.format(self.server.server_port))
58 | else:
59 | self.info('Http server is created @ port {}.'.format(self.server.server_port))
60 |
61 | def run(self):
62 | self.verbose('Http WS server is serving forever now !!')
63 | try:
64 | self.server.serve_forever()
65 | except:
66 | pass
67 |
68 | ## Create threaded http server which is able to upgrade HTTP request to websocket
69 | # @param ip IP for server.
70 | # @param port Listen on this port to accept connection.
71 | # @param credentials The credentials file if a secure connection is required.
72 | # @param connect_handler A handler function which is called when the http request
73 | # is upgraded to websocket.
74 | # @param message_handler A handler function which is called when there's any
75 | # message received by the websocket.
76 | # @param disconnect_handler A handler function which is called when the websocket
77 | # disconnects to server.
78 | class OclGAWSServer(object):
79 | def __init__(self, ip, port, credentials = '', connect_handler = None,
80 | message_handler = None, disconnect_handler = None,
81 | base_path = None):
82 | HttpWSMessageHandler.cn_hdlr = connect_handler
83 | HttpWSMessageHandler.msg_hdlr = message_handler
84 | HttpWSMessageHandler.dcn_hdlr = disconnect_handler
85 | HttpWSMessageHandler.base_path = base_path
86 | self.httpwsserver = ThreadedHTTPServer((ip, port), HttpWSMessageHandler)
87 | self.httpwsserver_thread = TaskThread(name='httpwsserver')
88 | self.httpwsserver_thread.daemon = True
89 | self.credentials = credentials
90 |
91 | ## Run the http server's serve_forever function in a separated thread.
92 | def run_server(self):
93 | if self.httpwsserver_thread:
94 | self.httpwsserver_thread.start()
95 | task = HttpWSTask(self.httpwsserver, self.credentials)
96 | self.httpwsserver_thread.addtask(task)
97 |
98 | ## Shut down HttpServer and close the thread for it.
99 | def shutdown(self):
100 | if self.httpwsserver:
101 | self.httpwsserver.socket.close()
102 | self.httpwsserver.shutdown()
103 | self.httpwsserver = None
104 | if self.httpwsserver_thread:
105 | self.httpwsserver_thread.stop()
106 | self.httpwsserver_thread = None
107 |
--------------------------------------------------------------------------------
/OpenCLGA/shuffler_chromosome.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import numpy
3 | import pyopencl as cl
4 |
5 | from .simple_gene import SimpleGene
6 |
7 | class ShufflerChromosome:
8 | # ShufflerChromosome - a chromosome contains a list of Genes.
9 | # __genes - an ordered list of Genes
10 | # __name - name of the chromosome
11 | # __improving_func - function name in kernel to gurantee a better mutation result.
12 | # dna - an listed of Gene's dna
13 | # dna_total_length - sum of the lenght of all genes's dna
14 | def __init__(self, genes, name = ''):
15 | assert all(isinstance(gene, SimpleGene) for gene in genes)
16 | assert type(genes) == list
17 | self.__genes = genes
18 | self.__name = name
19 | self.__improving_func = None
20 |
21 | @property
22 | def num_of_genes(self):
23 | return len(self.__genes)
24 |
25 | @property
26 | def name(self):
27 | return self.__name
28 |
29 | @property
30 | def dna_total_length(self):
31 | return self.num_of_genes
32 |
33 | @property
34 | def dna(self):
35 | return [gene.dna for gene in self.__genes]
36 |
37 | @dna.setter
38 | def dna(self, dna):
39 | assert self.num_of_genes == len(dna)
40 | for i, gene in enumerate(self.__genes):
41 | gene.dna = dna[i]
42 |
43 | @property
44 | def genes(self):
45 | return self.__genes
46 |
47 | @property
48 | def gene_elements(self):
49 | return [] if len(self.__genes) == 0 else self.__genes[0].elements
50 |
51 | @property
52 | def gene_elements_in_kernel(self):
53 | return [] if len(self.__genes) == 0 else self.__genes[0].elements_in_kernel
54 |
55 | @property
56 | def kernel_file(self):
57 | return 'shuffler_chromosome.cl'
58 |
59 | @property
60 | def struct_name(self):
61 | return '__ShufflerChromosome';
62 |
63 | @property
64 | def chromosome_size_define(self):
65 | return 'SHUFFLER_CHROMOSOME_GENE_SIZE'
66 |
67 | def early_terminated(self, best, worst):
68 | return False
69 |
70 | def from_kernel_value(self, data):
71 | assert len(data) == self.num_of_genes
72 | genes = [self.__genes[idx].from_kernel_value(v) for idx, v in enumerate(data)]
73 | return ShufflerChromosome(genes, self.__name)
74 |
75 | def use_improving_only_mutation(self, helper_func_name):
76 | self.__improving_func = helper_func_name
77 |
78 | def kernelize(self):
79 | improving_func = self.__improving_func if self.__improving_func is not None\
80 | else 'shuffler_chromosome_dummy_improving_func'
81 | candidates = '#define SIMPLE_GENE_ELEMENTS ' + self.__genes[0].elements_in_kernel_str
82 | defines = '#define SHUFFLER_CHROMOSOME_GENE_SIZE ' + str(self.num_of_genes) + '\n' +\
83 | '#define IMPROVED_FITNESS_FUNC ' + improving_func + '\n'
84 |
85 | improving_func_header = 'int ' + improving_func + '(global int* c,' +\
86 | 'int idx,' +\
87 | 'int chromosome_size FITNESS_ARGS);'
88 | return candidates + defines + improving_func_header
89 |
90 | def save(self, data, ctx, queue, population):
91 | total_dna_size = population * self.dna_total_length
92 | # prepare memory
93 | other_chromosomes = numpy.zeros(total_dna_size, dtype=numpy.int32)
94 | cross_map = numpy.zeros(total_dna_size, dtype=numpy.int32)
95 | ratios = numpy.zeros(population, dtype=numpy.float32)
96 | # read data from cl
97 | cl.enqueue_read_buffer(queue, self.__dev_ratios, ratios)
98 | cl.enqueue_read_buffer(queue, self.__dev_other_chromosomes, other_chromosomes)
99 | cl.enqueue_read_buffer(queue, self.__dev_cross_map, cross_map).wait()
100 | # save all of them
101 | data['other_chromosomes'] = other_chromosomes
102 | data['cross_map'] = cross_map
103 | data['ratios'] = ratios
104 |
105 | def restore(self, data, ctx, queue, population):
106 | other_chromosomes = data['other_chromosomes']
107 | cross_map = data['cross_map']
108 | ratios = data['ratios']
109 | # build CL memory from restored memory
110 | mf = cl.mem_flags
111 | self.__dev_ratios = cl.Buffer(ctx, mf.WRITE_ONLY, ratios.nbytes)
112 | self.__dev_other_chromosomes = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
113 | hostbuf=other_chromosomes)
114 | self.__dev_cross_map = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
115 | hostbuf=cross_map)
116 |
117 | def preexecute_kernels(self, ctx, queue, population):
118 | ## initialize global variables for kernel execution
119 | total_dna_size = population * self.dna_total_length
120 |
121 | other_chromosomes = numpy.zeros(total_dna_size, dtype=numpy.int32)
122 | cross_map = numpy.zeros(total_dna_size, dtype=numpy.int32)
123 | ratios = numpy.zeros(population, dtype=numpy.float32)
124 |
125 | mf = cl.mem_flags
126 |
127 | self.__dev_ratios = cl.Buffer(ctx, mf.WRITE_ONLY, ratios.nbytes)
128 | self.__dev_other_chromosomes = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
129 | hostbuf=other_chromosomes)
130 | self.__dev_cross_map = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
131 | hostbuf=cross_map)
132 |
133 | def get_populate_kernel_names(self):
134 | return ['shuffler_chromosome_populate']
135 |
136 | def get_crossover_kernel_names(self):
137 | return ['shuffler_chromosome_calc_ratio',\
138 | 'shuffler_chromosome_pick_chromosomes',\
139 | 'shuffler_chromosome_do_crossover']
140 |
141 | def get_mutation_kernel_names(self):
142 | return ['shuffler_chromosome_single_gene_mutate']
143 |
144 | def execute_populate(self, prg, queue, population, dev_chromosomes, dev_rnum):
145 | prg.shuffler_chromosome_populate(queue,
146 | (population,),
147 | (1,),
148 | dev_chromosomes,
149 | dev_rnum).wait()
150 |
151 | def selection_preparation(self, prg, queue, dev_fitnesses):
152 | prg.shuffler_chromosome_calc_ratio(queue,
153 | (1,),
154 | (1,),
155 | dev_fitnesses,
156 | self.__dev_ratios).wait()
157 |
158 | def execute_get_current_elites(self, prg, queue, top,
159 | dev_chromosomes, dev_current_elites,
160 | dev_best_indices):
161 | prg.shuffler_chromosome_get_the_elites(queue, (1,), (1,),
162 | dev_best_indices,
163 | dev_chromosomes,
164 | dev_current_elites,
165 | numpy.int32(top)).wait()
166 |
167 | def execute_update_current_elites(self, prg, queue, top, dev_worst_indices,
168 | dev_chromosomes, dev_updated_elites,
169 | dev_fitnesses, dev_updated_elite_fitness):
170 | prg.shuffler_chromosome_update_the_elites(queue, (1,), (1,),
171 | numpy.int32(top),
172 | dev_worst_indices,
173 | dev_chromosomes,
174 | dev_updated_elites,
175 | dev_fitnesses,
176 | dev_updated_elite_fitness).wait()
177 |
178 | def execute_crossover(self, prg, queue, population, generation_idx, prob_crossover,
179 | dev_chromosomes, dev_fitnesses, dev_rnum, best_fitness):
180 | prg.shuffler_chromosome_pick_chromosomes(queue,
181 | (population,),
182 | (1,),
183 | dev_chromosomes,
184 | dev_fitnesses,
185 | self.__dev_other_chromosomes,
186 | self.__dev_ratios,
187 | dev_rnum).wait()
188 | prg.shuffler_chromosome_do_crossover(queue,
189 | (population,),
190 | (1,),
191 | dev_chromosomes,
192 | dev_fitnesses,
193 | self.__dev_other_chromosomes,
194 | self.__dev_cross_map,
195 | dev_rnum,
196 | numpy.float32(best_fitness),
197 | numpy.float32(prob_crossover)).wait()
198 |
199 |
200 | def execute_mutation(self, prg, queue, population, generation_idx, prob_mutate,
201 | dev_chromosomes, dev_fitnesses, dev_rnum, extra_list):
202 |
203 | args = [dev_chromosomes,
204 | dev_rnum,
205 | numpy.float32(prob_mutate),
206 | numpy.int32(self.__improving_func is not None)]
207 | args = args + extra_list
208 | prg.shuffler_chromosome_single_gene_mutate(queue,
209 | (population,),
210 | (1,),
211 | *args).wait()
212 |
--------------------------------------------------------------------------------
/OpenCLGA/simple_chromosome.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import numpy
3 | import pyopencl as cl
4 | from .simple_gene import SimpleGene
5 |
6 | class SimpleChromosome:
7 | # SimpleChromosome - a chromosome contains a list of Genes.
8 | # __genes - a list of Genes
9 | # __name - name of the chromosome
10 | # __improving_func - a function name in kernel to gurantee a better mutation result.
11 | # dna - an listed of Gene's dna
12 | # dna_total_length - sum of the lenght of all genes's dna
13 | def __init__(self, genes, name = ''):
14 | assert all(isinstance(gene, SimpleGene) for gene in genes)
15 | assert type(genes) == list
16 | self.__genes = genes
17 | self.__name = name
18 | self.__improving_func = None
19 |
20 | @property
21 | def num_of_genes(self):
22 | # The number of genes inside this SimpleChromosome.
23 | return len(self.__genes)
24 |
25 | @property
26 | def name(self):
27 | return self.__name
28 |
29 | @property
30 | def dna_total_length(self):
31 | # Sum of the dna lenght of each gene.
32 | return sum([gene.length for gene in self.__genes])
33 |
34 | @property
35 | def dna(self):
36 | return [gene.dna for gene in self.__genes]
37 |
38 | @dna.setter
39 | def dna(self, dna_sequence):
40 | assert self.num_of_genes == len(dna_sequence)
41 | for i, gene in enumerate(self.__genes):
42 | gene.dna = dna_sequence[i]
43 |
44 | @property
45 | def genes(self):
46 | return self.__genes
47 |
48 | @property
49 | def gene_elements(self):
50 | return [] if len(self.__genes) == 0 else self.__genes[0].elements
51 |
52 | @property
53 | def gene_elements_in_kernel(self):
54 | return [] if len(self.__genes) == 0 else self.__genes[0].elements_in_kernel
55 |
56 | @property
57 | def kernel_file(self):
58 | return 'simple_chromosome.cl'
59 |
60 | @property
61 | def struct_name(self):
62 | return '__SimpleChromosome';
63 |
64 | @property
65 | def chromosome_size_define(self):
66 | return 'SIMPLE_CHROMOSOME_GENE_SIZE'
67 |
68 | def early_terminated(self, best , worst):
69 | # If the difference between the best and the worst is negligible,
70 | # terminate the program to save time.
71 | return abs(worst - best) < 0.0001
72 |
73 | def from_kernel_value(self, data):
74 | # Construct a SimpleChromosome object on system memory according to
75 | # the calculated 'data' on opencl(device) memory.
76 | assert len(data) == self.num_of_genes
77 | genes = [self.__genes[idx].from_kernel_value(v) for idx, v in enumerate(data)]
78 | return SimpleChromosome(genes, self.__name)
79 |
80 | def use_improving_only_mutation(self, helper_func_name):
81 | # Set a helper function to make sure a better mutation result.
82 | self.__improving_func = helper_func_name
83 |
84 | def kernelize(self):
85 | # - Build a str which contains c99-like codes. This str will be written
86 | # into a final kernel document called 'final.cl' for execution.
87 | # - Gene elements, size, mutation function is pre-defined as MACRO for
88 | # easier usage.
89 | elements_size_list = [str(gene.elements_length) for gene in self.__genes]
90 | candidates = '#define SIMPLE_CHROMOSOME_GENE_ELEMENTS_SIZE {' +\
91 | ', '.join(elements_size_list) + '}\n'
92 | defines = '#define SIMPLE_CHROMOSOME_GENE_SIZE ' + str(self.num_of_genes) + '\n' +\
93 | '#define SIMPLE_CHROMOSOME_GENE_MUTATE_FUNC ' +\
94 | self.__genes[0].mutate_func_name + '\n'
95 |
96 | return candidates + defines
97 |
98 | def save(self, data, ctx, queue, population):
99 | total_dna_size = population * self.dna_total_length
100 | # prepare memory
101 | other_chromosomes = numpy.zeros(total_dna_size, dtype=numpy.int32)
102 | ratios = numpy.zeros(population, dtype=numpy.float32)
103 | # read data from cl
104 | cl.enqueue_read_buffer(queue, self.__dev_ratios, ratios)
105 | cl.enqueue_read_buffer(queue, self.__dev_other_chromosomes, other_chromosomes).wait()
106 | # save all of them
107 | data['other_chromosomes'] = other_chromosomes
108 | data['ratios'] = ratios
109 |
110 | def restore(self, data, ctx, queue, population):
111 | other_chromosomes = data['other_chromosomes']
112 | ratios = data['ratios']
113 | # prepare CL memory
114 | mf = cl.mem_flags
115 | self.__dev_ratios = cl.Buffer(ctx, mf.WRITE_ONLY, ratios.nbytes)
116 | self.__dev_other_chromosomes = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
117 | hostbuf=other_chromosomes)
118 | # Copy data from main memory to GPU memory
119 | cl.enqueue_copy(queue, self.__dev_ratios, ratios)
120 | cl.enqueue_copy(queue, self.__dev_other_chromosomes, other_chromosomes)
121 |
122 | def preexecute_kernels(self, ctx, queue, population):
123 | # initialize global variables for kernel execution
124 | total_dna_size = population * self.dna_total_length
125 |
126 | other_chromosomes = numpy.zeros(total_dna_size, dtype=numpy.int32)
127 | ratios = numpy.zeros(population, dtype=numpy.float32)
128 |
129 | mf = cl.mem_flags
130 |
131 | # prepare device memory for usage.
132 | self.__dev_ratios = cl.Buffer(ctx, mf.WRITE_ONLY, ratios.nbytes)
133 | self.__dev_other_chromosomes = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR,
134 | hostbuf=other_chromosomes)
135 |
136 | def get_populate_kernel_names(self):
137 | return ['simple_chromosome_populate']
138 |
139 | def get_crossover_kernel_names(self):
140 | return ['simple_chromosome_calc_ratio',\
141 | 'simple_chromosome_pick_chromosomes',\
142 | 'simple_chromosome_do_crossover']
143 |
144 | def get_mutation_kernel_names(self):
145 | return ['simple_chromosome_mutate_all']
146 |
147 | def execute_populate(self, prg, queue, population, dev_chromosomes, dev_rnum):
148 | prg.simple_chromosome_populate(queue,
149 | (population,),
150 | (1,),
151 | dev_chromosomes,
152 | dev_rnum).wait()
153 |
154 | def selection_preparation(self, prg, queue, dev_fitnesses):
155 | prg.simple_chromosome_calc_ratio(queue,
156 | (1,),
157 | (1,),
158 | dev_fitnesses,
159 | self.__dev_ratios).wait()
160 |
161 | def execute_get_current_elites(self, prg, queue, top,
162 | dev_chromosomes, dev_current_elites,
163 | dev_best_indices):
164 | prg.simple_chromosome_get_the_elites(queue, (1,), (1,),
165 | dev_best_indices,
166 | dev_chromosomes,
167 | dev_current_elites,
168 | numpy.int32(top)).wait()
169 |
170 | def execute_update_current_elites(self, prg, queue, top, dev_worst_indices,
171 | dev_chromosomes, dev_updated_elites,
172 | dev_fitnesses, dev_updated_elite_fitness):
173 | prg.simple_chromosome_update_the_elites(queue, (1,), (1,),
174 | numpy.int32(top),
175 | dev_worst_indices,
176 | dev_chromosomes,
177 | dev_updated_elites,
178 | dev_fitnesses,
179 | dev_updated_elite_fitness).wait()
180 |
181 | def execute_crossover(self, prg, queue, population, generation_idx, prob_crossover,
182 | dev_chromosomes, dev_fitnesses, dev_rnum, best_fitness):
183 | prg.simple_chromosome_pick_chromosomes(queue,
184 | (population,),
185 | (1,),
186 | dev_chromosomes,
187 | dev_fitnesses,
188 | self.__dev_other_chromosomes,
189 | self.__dev_ratios,
190 | dev_rnum).wait()
191 | prg.simple_chromosome_do_crossover(queue,
192 | (population,),
193 | (1,),
194 | dev_chromosomes,
195 | dev_fitnesses,
196 | self.__dev_other_chromosomes,
197 | dev_rnum,
198 | numpy.float32(best_fitness),
199 | numpy.float32(prob_crossover)).wait()
200 |
201 |
202 | def execute_mutation(self, prg, queue, population, generation_idx, prob_mutate,
203 | dev_chromosomes, dev_fitnesses, dev_rnum, extra_list):
204 | prg.simple_chromosome_mutate_all(queue,
205 | (population,),
206 | (1,),
207 | dev_chromosomes,
208 | dev_rnum,
209 | numpy.float32(prob_mutate)).wait()
210 |
--------------------------------------------------------------------------------
/OpenCLGA/simple_gene.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import random
3 | HUMAN_DNA_ELEMENTS = ['A','C','G','T']
4 |
5 | class SimpleGene:
6 |
7 | @staticmethod
8 | def clone_gene(g):
9 | # Clone a new gene containing the same dna symbol in g.
10 | return SimpleGene(g.dna, g.elements, g.name)
11 |
12 | # SimpleGene - is a Gene with only one DNA element.
13 | # dna - a single element picked from elements.
14 | # elements - a set of element which is the basic component of dna.
15 | def __init__(self, dna, elements=HUMAN_DNA_ELEMENTS, name=''):
16 | assert dna is not None
17 | assert type(elements) == list
18 | self.__elements = elements
19 | self.__name = name
20 | self.dna = dna
21 |
22 | @property
23 | def dna(self):
24 | return self.__dna
25 |
26 | @dna.setter
27 | def dna(self, dna):
28 | assert dna is not None
29 | self.__dna = dna
30 |
31 | @property
32 | def dna_in_kernel(self):
33 | return self._elements.index(self.dna)
34 |
35 | @property
36 | def length(self):
37 | # SimpleGene is designed to be only 1 dna element inside.
38 | return 1
39 |
40 | @property
41 | def name(self):
42 | # The name of this SimpleGene
43 | return self.__name
44 |
45 | @property
46 | def elements(self):
47 | # The list of dna elements.
48 | return self.__elements
49 |
50 | @property
51 | def elements_in_kernel(self):
52 | return list(range(0, len(self.__elements)))
53 |
54 | @property
55 | def kernel_file(self):
56 | # Kernel file which contains related operating functions for SimpleGene,
57 | # i.e. simple_gene_mutate()
58 | return 'simple_gene.cl'
59 |
60 | @property
61 | def elements_length(self):
62 | # The elements size of this SimpleGene.
63 | # Could be considered as the problem space represented by this SimpleGene.
64 | return len(self.__elements)
65 |
66 | @property
67 | def mutate_func_name(self):
68 | # Chromosome can use this function to execute built-in mutate function which choose an
69 | # excluded elments randomly.
70 | return 'simple_gene_mutate'
71 |
72 | @property
73 | def elements_in_kernel_str(self):
74 | # Chromosome can use this function to declare elements array
75 | elements_str = ', '.join([str(v) for v in self.elements_in_kernel])
76 | return '{' + elements_str + '}\n'
77 |
78 | def from_kernel_value(self, v):
79 | # Construct a SimpleGene object on system memory according to
80 | # the calculated index values 'v' on opencl(device) memory.
81 | assert 0 <= v < len(self.__elements)
82 | return SimpleGene(self.__elements[v], self.__elements, self.__name)
83 |
--------------------------------------------------------------------------------
/OpenCLGA/ui/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/android-icon-144x144.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/android-icon-192x192.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/android-icon-36x36.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/android-icon-48x48.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/android-icon-72x72.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/android-icon-96x96.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-114x114.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-120x120.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-144x144.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-152x152.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-180x180.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-57x57.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-60x60.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-72x72.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-76x76.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyOCL/OpenCLGA/8192a337d5317795b5d4f1d5c9b76299bf4c4df4/OpenCLGA/ui/apple-icon.png
--------------------------------------------------------------------------------
/OpenCLGA/ui/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "main.css": "static/css/main.5da4bf40.css",
3 | "main.css.map": "static/css/main.5da4bf40.css.map",
4 | "main.js": "static/js/main.7371ebc2.js",
5 | "main.js.map": "static/js/main.7371ebc2.js.map"
6 | }
--------------------------------------------------------------------------------
/OpenCLGA/ui/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |