Skip to content

Commit 318f695

Browse files
committed
Merge branch 'development' of https://github.com/gjbex/Python-for-machine-learning into development
2 parents 09942f6 + 0b93616 commit 318f695

19 files changed

+1734
-0
lines changed

source-code/REEADME.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#| Source code
2+
3+
This is source code that is either used in the presentation, or was developed
4+
to create it. There is some material not covered in the presentation as well.
5+
6+
## Requirements
7+
8+
* Python version: at least 3.7
9+
* Packages (names listed that can be used with `pip` or `conda` to install):
10+
* pandas
11+
* xlrd
12+
* seaborn
13+
* matplotlib
14+
* scipy
15+
* jupyter
16+
* scikit-learn
17+
* keras
18+
* hyperopt
19+
20+
## What is it?
21+
* [`keras`](keras): illustration of using Keras for machine learning.
22+
* [`parameter-optimization`](parameter-optimization): example of parameter
23+
optimization kusing hyperopt, although the examples do not optimize
24+
hyperparameters in machine learning, that would be very similar.
25+
* [`scikit-learn`](scikit-learn): examples of scikit-learn for machine learning,
26+
examples are provided forsupervised (regression and classification) and
27+
unsupervised (clustering) learnign, as well as dimensionality reduction for
28+
visualization of high-demensional data.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.exe
2+
*.pbs.*
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CXX = g++
2+
CXXFLAGS = -fopenmp -std=c++14 -O2 -g -Wall -Wextra
3+
LDLIBS = -lm
4+
5+
all: julia_omp.exe
6+
7+
%.exe: %.o
8+
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDLIBS)
9+
10+
clean:
11+
$(RM) $(wildcard *.exe) $(wildcard *.o)
12+
$(RM) core $(wildcard core.*)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# JuliaJobs
2+
3+
Illustration of optimizing application parameters by running jobs.
4+
5+
## What is it?
6+
7+
1. `julia_omp.cpp`: C++ application that compute the Julia set
8+
fractal, using OpenMP for parallelization. The parallel
9+
for-loop schedule is `runtime`, and hence can be controled
10+
using the `OMP_SCHEDULE` environment variable. Timing
11+
information is writting to standard error.
12+
1. `Makefile`: make file to build the Julia set application.
13+
1. `julia.pbs`: PBS script to run the Julia set applciation
14+
as a job.
15+
1. `julia_optimize.py`: Python script to find optimal OpenMP
16+
schedule, chunk size and number of threads for the Julia set
17+
application.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env bash
2+
#PBS -A lp_sys
3+
#PBS -l pmem=2gb
4+
#PBS -l walltime=00:05:00
5+
#PBS -j oe
6+
7+
cd $PBS_O_WORKDIR
8+
9+
source switch_to_2018a
10+
module load foss
11+
12+
# verify that OpenMP environment variables have been passed
13+
# using -v options
14+
if [[ -z "$schedule" ]]
15+
then
16+
(>&2 echo "### error: OpenMP schedule not set")
17+
exit 1
18+
fi
19+
if [[ -z "$chunk" ]]
20+
then
21+
(>&2 echo "### error: OpenMP chunk not set")
22+
exit 1
23+
fi
24+
25+
OMP_SCHEDULE="${schedule},${chunk}"
26+
(>&2 echo "### info: OMP_SCHEDULE=\'${OMP_SCHEDULE}\'")
27+
28+
if [[ -z "$OMP_NUM_THREADS" ]]
29+
then
30+
(>&2 echo "### error: OpenMP number of threads not set")
31+
exit 2
32+
else
33+
(>&2 echo "### info: OMP_NUM_THREADS=\'${OMP_NUM_THREADS}\'")
34+
fi
35+
36+
# ensure the executable is op to date
37+
make all
38+
if [[ $? -ne 0 ]]
39+
then
40+
(>&2 echo "### error: make failed")
41+
exit 3
42+
fi
43+
44+
# determine time file name
45+
time_file="julia.pbs.time${PBS_JOBID/\.*/}"
46+
47+
# run application on a sufficiently large matrix, disregarding
48+
# output, timing information is writting to a file
49+
for i in $(seq 3)
50+
do
51+
./julia_omp.exe 4096 > /dev/null 2>> "${time_file}"
52+
done
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#include <algorithm>
2+
#include <chrono>
3+
#include <cmath>
4+
#include <complex>
5+
#include <iostream>
6+
#include <valarray>
7+
8+
using namespace std;
9+
using my_time_t = chrono::nanoseconds;
10+
11+
using cmplx = complex<float>;
12+
13+
valarray<float> coordinates(float min_coord, float max_coord,
14+
size_t steps);
15+
valarray<cmplx> z_values(const valarray<float>& x_coords,
16+
const valarray<float>& y_coords);
17+
valarray<int> iterate_zs(valarray<cmplx>& zs, const complex<float>& c,
18+
size_t max_iters);
19+
void print_results(const valarray<int>& ns);
20+
21+
int main(int argc, char *argv[]) {
22+
const cmplx c(-0.62772, - 0.42193);
23+
const float x1 {-1.8};
24+
const float x2 {1.8};
25+
const float y1 {-1.8};
26+
const float y2 {1.8};
27+
const size_t max_iters {255};
28+
size_t steps {100};
29+
if (argc > 1)
30+
steps = stoi(argv[1]);
31+
valarray<float> x_coords = coordinates(x1, x2, steps);
32+
valarray<float> y_coords = coordinates(y1, y2, steps);
33+
valarray<cmplx> zs = z_values(x_coords, y_coords);
34+
auto start_time = chrono::steady_clock::now();
35+
valarray<int> ns = iterate_zs(zs, c, max_iters);
36+
auto end_time = chrono::steady_clock::now();
37+
auto duration = chrono::duration_cast<my_time_t>(end_time - start_time);
38+
cerr << duration.count()*1.0e-9 << endl;
39+
print_results(ns);
40+
return 0;
41+
}
42+
43+
valarray<float> coordinates(float min_coord, float max_coord,
44+
size_t steps) {
45+
valarray<float> coords(steps);
46+
const float step {(max_coord - min_coord)/steps};
47+
float value {min_coord};
48+
for (size_t i = 0; i < steps; i++) {
49+
coords[i] = value;
50+
value += step;
51+
}
52+
return coords;
53+
}
54+
55+
valarray<cmplx> z_values(const valarray<float>& x_coords,
56+
const valarray<float>& y_coords) {
57+
valarray<cmplx> zs(x_coords.size()*y_coords.size());
58+
size_t i {0};
59+
for (auto y: y_coords)
60+
for (auto x: x_coords) {
61+
complex<float> z(x, y);
62+
zs[i++] = z;
63+
}
64+
return zs;
65+
}
66+
67+
int iterate_z(cmplx z, const cmplx& c, size_t max_iters) {
68+
size_t n {0};
69+
const complex<float> z_in {z};
70+
while (abs(z) < 2.0 && n++ < max_iters)
71+
z = z*z + c;
72+
return n;
73+
}
74+
75+
valarray<int> iterate_zs(valarray<cmplx>& zs, const complex<float>& c,
76+
size_t max_iters) {
77+
valarray<int> ns(zs.size());
78+
#pragma omp parallel for schedule(runtime)
79+
for (size_t i = 0; i < zs.size(); i++)
80+
ns[i] = iterate_z(zs[i], c, max_iters);
81+
return ns;
82+
}
83+
84+
void print_results(const valarray<int>& ns) {
85+
size_t steps = ((size_t) sqrt(ns.size()) + 0.1);
86+
size_t count {0};
87+
for (auto n: ns) {
88+
cout << n;
89+
if (++count % steps == 0)
90+
cout << endl;
91+
else
92+
cout << " ";
93+
}
94+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python
2+
3+
from argparse import ArgumentParser
4+
from pathlib import Path
5+
import subprocess
6+
import sys
7+
import time
8+
from hyperopt import fmin, hp, STATUS_OK, tpe, Trials
9+
10+
11+
def function(params):
12+
schedule, chunk, ppn = params
13+
# chunk is given as a floating point number
14+
chunk = int(chunk)
15+
# ppn ranges from 0 to 35 (inclusive)
16+
ppn = 1 + int(ppn)
17+
omp_env = (f'schedule={schedule},chunk={int(chunk)},' +
18+
f'OMP_NUM_THREADS={ppn}')
19+
cmd = ['qsub', '-l', f'nodes=1:ppn={ppn}:haswell',
20+
'-v', omp_env, 'julia.pbs']
21+
process = subprocess.run(cmd, stdout=subprocess.PIPE,
22+
encoding='utf8')
23+
job_id, *_ = process.stdout.split('.')
24+
print(f'### info: submitted job {job_id}', file=sys.stderr)
25+
output_file = Path(f'julia.pbs.o{job_id}')
26+
while not output_file.exists():
27+
time.sleep(3)
28+
print(f'### info: job {job_id} finished', file=sys.stderr)
29+
runtimes = list()
30+
with open(f'julia.pbs.time{job_id}', 'r') as time_file:
31+
for line in time_file:
32+
runtimes.append(float(time_file.readline()))
33+
runtime = sum(runtimes)/len(runtimes)
34+
return {
35+
'loss': runtime, 'schedule': schedule, 'chunk': chunk,
36+
'ppn': ppn, 'job_id': job_id, 'status': STATUS_OK,
37+
'time': time.strftime('%Y-%m-%d %H:%M:%S'),
38+
}
39+
40+
41+
def optimize(max_evals, max_ppn):
42+
space = hp.choice('schedule', [
43+
('static', hp.qloguniform('chunk_s', 2, 11, 10),
44+
hp.randint('ppn_s', max_ppn)),
45+
('dynamic', hp.qloguniform('chunk_d', 2, 11, 10),
46+
hp.randint('ppn_d', max_ppn)),
47+
('guided', hp.qloguniform('chunk_g', 2, 11, 10),
48+
hp.randint('ppn_g', max_ppn)),
49+
])
50+
trials = Trials()
51+
best = fmin(function, space=space, algo=tpe.suggest,
52+
max_evals=max_evals, trials=trials)
53+
return best, trials
54+
55+
56+
def main():
57+
arg_parser = ArgumentParser(description='optimize external '
58+
'process')
59+
arg_parser.add_argument('--max-ppn', type=int, default=20,
60+
help='maximum number of cores to use')
61+
arg_parser.add_argument('--max-evals', type=int,
62+
default=100, help='maximum evals')
63+
arg_parser.add_argument('--trials', required=True,
64+
help='file to save trials')
65+
options = arg_parser.parse_args()
66+
_, trials = optimize(options.max_evals, options.max_ppn)
67+
with open(options.trials, 'w') as trials_file:
68+
print('schedule,chunk,ppn,job_id,runtime',
69+
file=trials_file)
70+
for trial in trials.results:
71+
schedule = trial['schedule']
72+
chunk = trial['chunk']
73+
ppn = trial['ppn']
74+
job_id = trial['job_id']
75+
runtime = trial['loss']
76+
print(f'{schedule},{chunk:d},{ppn:d},'
77+
f'({job_id}),{runtime}', file=trials_file)
78+
79+
80+
if __name__ == '__main__':
81+
sys.exit(main())
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CXX = g++
2+
CXXFLAGS = -fopenmp -std=c++14 -O2 -g -Wall -Wextra
3+
LDLIBS = -lm
4+
5+
all: julia_omp.exe
6+
7+
%.exe: %.o
8+
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDLIBS)
9+
10+
clean:
11+
$(RM) $(wildcard *.exe) $(wildcard *.o)
12+
$(RM) core $(wildcard core.*)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# JuliaProcesses
2+
3+
Illustration of optimizing application parameters by running
4+
processes.
5+
6+
## What is it?
7+
8+
1. `julia_omp.cpp`: C++ application that compute the Julia set
9+
fractal, using OpenMP for parallelization. The parallel
10+
for-loop schedule is `runtime`, and hence can be controled
11+
using the `OMP_SCHEDULE` environment variable. Timing
12+
information is writting to standard error.
13+
1. `Makefile`: make file to build the Julia set application.
14+
1. `julia_optimize.py`: Python script to find optimal OpenMP
15+
schedule, chunk size and number of threads for the Julia set
16+
application.
17+
1. `julia_gcc.pbs`: PBS script to optimize the Julia set
18+
applciation parameters as a job (GCC compiler).
19+
1. `julia_intel.pbs`: PBS script to optimize the Julia set
20+
applciation parameters as a job (Intel compiler).
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
#PBS -A lp_sys
3+
#PBS -l nodes=1:ppn=24:haswell
4+
#PBS -l pmem=2gb
5+
#PBS -l walltime=03:00:00
6+
#PBS -j oe
7+
#PBS -m ae
8+
9+
10+
cd $PBS_O_WORKDIR
11+
12+
source switch_to_2018a
13+
module load foss
14+
15+
# ensure the executable is op to date
16+
make CXX=g++ CXXFLAGS='-fopenmp -std=c++14 -O2' clean all
17+
if [[ $? -ne 0 ]]
18+
then
19+
(>&2 echo "### error: make failed")
20+
exit 1
21+
fi
22+
23+
module load conda
24+
source activate hyperopt
25+
26+
./julia_optimize.py --max-ppn 20 --max-evals 100 \
27+
--trials trials_gcc.txt

0 commit comments

Comments
 (0)