summaryrefslogtreecommitdiff
path: root/glpk-5.0/examples/threads
diff options
context:
space:
mode:
Diffstat (limited to 'glpk-5.0/examples/threads')
-rw-r--r--glpk-5.0/examples/threads/Build_Multiseed.bat12
-rw-r--r--glpk-5.0/examples/threads/Makefile5
-rw-r--r--glpk-5.0/examples/threads/Makefile_VC26
-rw-r--r--glpk-5.0/examples/threads/README66
-rw-r--r--glpk-5.0/examples/threads/clustering.mod109
-rw-r--r--glpk-5.0/examples/threads/multiseed.c255
-rw-r--r--glpk-5.0/examples/threads/thread.h50
7 files changed, 523 insertions, 0 deletions
diff --git a/glpk-5.0/examples/threads/Build_Multiseed.bat b/glpk-5.0/examples/threads/Build_Multiseed.bat
new file mode 100644
index 0000000..34dd95f
--- /dev/null
+++ b/glpk-5.0/examples/threads/Build_Multiseed.bat
@@ -0,0 +1,12 @@
+rem Build GLPK DLL with Microsoft Visual Studio Community 2015
+
+rem NOTE: Make sure that HOME variable specifies correct path
+set HOME="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC"
+
+call %HOME%\vcvarsall.bat x64
+copy config_VC config.h
+%HOME%\bin\nmake.exe /f Makefile_VC
+set PATH=..\..\w64\
+%HOME%\bin\nmake.exe /f Makefile_VC check
+
+pause
diff --git a/glpk-5.0/examples/threads/Makefile b/glpk-5.0/examples/threads/Makefile
new file mode 100644
index 0000000..61063d3
--- /dev/null
+++ b/glpk-5.0/examples/threads/Makefile
@@ -0,0 +1,5 @@
+all:
+ gcc multiseed.c -I. -lglpk -pthread -o multiseed
+
+check:
+ ./multiseed clustering.mod 20
diff --git a/glpk-5.0/examples/threads/Makefile_VC b/glpk-5.0/examples/threads/Makefile_VC
new file mode 100644
index 0000000..5bb8179
--- /dev/null
+++ b/glpk-5.0/examples/threads/Makefile_VC
@@ -0,0 +1,26 @@
+## Build multiseed example with Microsoft Visual Studio Express ##
+
+CFLAGS = \
+/I. \
+/I..\..\src \
+/I..\..\w64 \
+/DHAVE_CONFIG_H=1 \
+/D_CRT_SECURE_NO_WARNINGS=1 \
+/nologo \
+/W3 \
+/O2 \
+/Zi
+
+.c.obj:
+ cl.exe $(CFLAGS) /Fo$*.obj /c $*.c
+
+all: multiseed.exe
+
+multiseed.exe: multiseed.obj ..\..\w64\glpk_4_61.dll
+ cl.exe $(CFLAGS) /Fmultiseed.exe \
+ multiseed.obj ..\..\w64\glpk_4_61.lib
+
+check: multiseed.exe
+ .\multiseed.exe clustering.mod 20
+
+## eof ##
diff --git a/glpk-5.0/examples/threads/README b/glpk-5.0/examples/threads/README
new file mode 100644
index 0000000..cb547f0
--- /dev/null
+++ b/glpk-5.0/examples/threads/README
@@ -0,0 +1,66 @@
+Thread local memory example
+===========================
+
+The GLPK library, when compiled with default options, uses a separate environment
+for each thread that is executed. So each thread is isolated. The only exeption
+is error handling. An error in any of the library functions will not only
+terminate the active thread but the complete process.
+
+This can be circumvented by defining an error handling routine with
+glp_error_hook(). This directory contains an example demonstrating running a
+multithreaded application with error handling.
+
+
+The example code
+----------------
+
+The program multiseed solves a MathProg model multiple times in separate parallel
+threads. Each threads uses a different seed for the MathProg pseudo random number
+generator.
+
+The MathProg model clustering.mod generates 50 randomly distributed "towns". Out
+of the towns it selects 3 to be cluster centers and assign the other towns to the
+clusters such that the sum of the population weighted euclidian distances between
+towns and centers is minimized.
+
+The solution is written to a Scalable Vector File which can be viewed with a web
+browser.
+
+For demonstration purposes at the end of every third thread the error handling
+routine is triggered by calling glp_error(). This results in output like
+
+ 18-00086 Model has been successfully processed
+ 18-00087 Voluntarily throwing an error in multiseed.c at line 147
+ 18-00088 Error detected in file multiseed.c at line 146
+ 18-00089 Error caught
+
+Terminal output is preceeded by numbers indicating the thread index and the
+output line. You can pipe it through sort to get a better overiew, e.g.
+
+ multiseed clustering.mod 20 | sort
+
+
+Building and running the example code
+-------------------------------------
+
+On Linux multiseed can be compiled with gcc by calling
+
+ make
+
+The code can be executed with
+
+ make check
+
+For compiling the example on 64bit Windows with Microsoft Visual Studio C++ run
+
+ Build_Multiseed.bat
+
+You may have to adust the variable HOME in the batch file depending on the
+installation path of Visual Studio.
+
+You can run multiseed with
+
+ multiseed <filename> <count>
+
+Where filename is the path to the model file and count is the number of parallel
+threads.
diff --git a/glpk-5.0/examples/threads/clustering.mod b/glpk-5.0/examples/threads/clustering.mod
new file mode 100644
index 0000000..cd8cd37
--- /dev/null
+++ b/glpk-5.0/examples/threads/clustering.mod
@@ -0,0 +1,109 @@
+/*
+ * Author: Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This model solves a clustering problem:
+ *
+ * Out of 50 towns select 3 to be cluster centers and assign the other
+ * towns to the clusters such that the sum of the population weighted
+ * euclidian distances between towns and centers is minimized.
+ *
+ * The solution is saved as a scalable vector graphic file with a
+ * pseudo-random file name.
+ */
+
+# Output file
+param fn, symbolic := "00000" & 100000 * Uniform01();
+param f, symbolic := "ct" & substr(fn, length(fn) - 4) & ".svg";
+
+# Centers
+param nc := 3;
+set C := {1 .. nc};
+
+# Towns
+param nt := 50;
+set T := {1 .. nt};
+param xt{T} := Uniform01();
+param yt{T} := Uniform01();
+param pt{T} := ceil(1000 * Uniform01());
+
+# Image size
+param scale := 1000;
+
+# Colors
+# saturation [0, 255]
+param sat := 192;
+param hue{c in C} := 6 * (c - 1) / nc;
+param red{c in C} :=
+ if hue[c] <= 1 or hue[c] >= 5 then 255
+ else (if hue[c] >=2 and hue[c] <= 4 then 255 - sat
+ else (if hue[c] <=2 then 255 - sat + sat * (2-hue[c])
+ else 255 - sat + sat * (hue[c]-4) ));
+param green{c in C} :=
+ if hue[c] >= 1 and hue[c] <= 3 then 255
+ else (if hue[c] >= 4 then 255 - sat
+ else (if hue[c] <=1 then 255 - sat + sat * hue[c]
+ else 255 - sat + sat * (4-hue[c]) ));
+param blue{c in C} :=
+ if hue[c] >= 3 and hue[c] <= 5 then 255
+ else (if hue[c] <=2 then 255 - sat
+ else (if hue[c] <=3 then 255 - sat + sat * (hue[c]-2)
+ else 255 - sat + sat * (6-hue[c]) ));
+
+var x{T};
+var y{T,T}, binary;
+
+minimize obj : sum{c in T, t in T} y[c,t] * pt[t]
+ * sqrt((xt[c] - xt[t])^2 + (yt[c] - yt[t])^2);
+
+s.t. sumx : sum{c in T} x[c] = nc;
+s.t. cxy{c in T, t in T} : y[c,t] <= x[c];
+s.t. sumy{t in T} : sum{c in T} y[c,t] = 1;
+
+solve;
+
+for {c in T : x[c] > .5} {
+ printf "Center %5.4f %5.4f\n", xt[c], yt[c];
+ for {t in T : y[c,t] > .5} {
+ printf " Town %5.4f %5.4f (%5.0f)\n", xt[t], yt[t], pt[t];
+ }
+}
+
+# Output the solution as scalable vector graphic
+
+# header
+printf "<?xml version=""1.0"" standalone=""no""?>\n" > f;
+printf "<!DOCTYPE svg PUBLIC ""-//W3C//DTD SVG 1.1//EN"" \n" >> f;
+printf """http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"">\n" >> f;
+printf "<svg width=""%d"" height=""%d"" version=""1.0"" \n",
+ 1.2 * scale, 1.2 * scale >> f;
+printf "xmlns=""http://www.w3.org/2000/svg"">\n" >> f;
+
+# background
+printf "<rect x=""0"" y=""0"" width=""%d"" height=""%d""" &
+ " stroke=""none"" fill=""white""/>\n",
+ 1.2 * scale, 1.2 * scale>> f;
+
+# border
+printf "<rect x=""%d"" y=""%d"" width=""%d"" height=""%d""" &
+ " stroke=""black"" stroke-width="".5"" fill=""white""/>\n",
+ .1 * scale, .1 * scale, scale, scale >> f;
+
+# circles for towns
+for {t in T}
+ printf {s in T, c in C : y[s,t] > .5
+ && c = floor( .5 + sum{u in T : u <= s} x[u])}
+ "<circle cx=""%f"" cy=""%f"" r=""%f"" stroke=""black"" " &
+ "stroke-width=""1"" fill=""rgb(%d,%d,%d)""/>\n",
+ (.1 + xt[t]) * scale, (.1 + yt[t]) * scale, .001 * sqrt(pt[t]) * scale,
+ red[c], green[c] , blue[c] >> f;
+
+# lines from towns to assigned centers
+for {t in T, c in T : y[c,t] > .5}
+ printf "<line x1=""%f"" y1=""%f"" x2=""%f"" y2=""%f""" &
+ " style=""stroke:black;stroke-width:.5""/>\n",
+ (.1 + xt[c]) * scale, (.1 + yt[c]) * scale,
+ (.1 + xt[t]) * scale, (.1 + yt[t]) * scale >> f;
+
+printf "</svg>\n" >> f;
+
+end;
diff --git a/glpk-5.0/examples/threads/multiseed.c b/glpk-5.0/examples/threads/multiseed.c
new file mode 100644
index 0000000..eb5d215
--- /dev/null
+++ b/glpk-5.0/examples/threads/multiseed.c
@@ -0,0 +1,255 @@
+/* multiseed.c (multithreading demo) */
+
+/***********************************************************************
+* This code is part of GLPK (GNU Linear Programming Kit).
+* Copyright (C) 2017 Free Software Foundation, Inc.
+* Written by Heinrich Schuchardt <xypron.glpk@gmx.de>.
+*
+* GLPK is free software: you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GLPK is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+* License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
+***********************************************************************/
+
+/*
+ * This program demonstrates running the GLPK library with multiple threads.
+ *
+ * When called the program requires two arguments:
+ *
+ * filename - the name of the MathProg model to be solved
+ * threads - the count of parallel threads to be run.
+ *
+ * Each thread is run with a different seed for the random number generator
+ * provided by the GLPK library.
+ */
+
+#include <glpk.h>
+#include <malloc.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "thread.h"
+
+#define BUFLEN 256
+
+/* Task descriptor */
+struct task {
+ pthread_t tid;
+ char *filename;
+ int seed;
+ size_t pos;
+ char buf[BUFLEN + 1];
+ int line;
+ jmp_buf jmp;
+};
+
+/* Mutex for console output */
+pthread_mutex_t mutex;
+
+/* Console output handler */
+int term_hook(void *info, const char *text)
+{
+ struct task *task = (struct task *) info;
+ size_t len = strlen(text);
+
+ /* Lock mutex so this is the only task creating console output. */
+ pthread_mutex_lock(&mutex);
+
+ /* Append the new text to the buffer. */
+ if (task->pos + len > BUFLEN) {
+ printf("%02d-%05d %s%s", task->seed, ++task->line, task->buf, text);
+ task->pos = 0;
+ task->buf[0] = 0;
+ } else {
+ strcpy(task->buf + task->pos, text);
+ task->pos += len;
+ }
+
+ /* If a complete line is available, send it to the console. */
+ if (strchr(task->buf, '\n')) {
+ printf("%02d-%05d %s", task->seed, ++task->line, task->buf);
+ task->pos = 0;
+ task->buf[0] = 0;
+ }
+
+ /* Unlock the mutex. */
+ pthread_mutex_unlock(&mutex);
+
+ /* Disable default output. */
+ return -1;
+}
+
+/* Error handler */
+void error_hook(void *info)
+{
+ struct task *task = (struct task *) info;
+
+ term_hook(task, "Error caught\n");
+ glp_free_env();
+ longjmp(task->jmp, 1);
+}
+
+void worker(void *arg)
+{
+ struct task *task = (struct task *) arg;
+ int ret;
+ glp_prob *lp;
+ glp_tran *tran;
+ glp_iocp iocp;
+
+ if (setjmp(task->jmp)) {
+ /* If an error is caught leave the function. */
+ return;
+ }
+
+ /* Set the error handler. */
+ glp_error_hook(error_hook, task);
+
+ /* Set the console output handler. */
+ glp_term_hook(term_hook, arg);
+
+ glp_printf("Seed %02d\n", task->seed);
+
+ /* Create the problem object. */
+ lp = glp_create_prob();
+ if (!lp) {
+ glp_error("Out of memory\n");
+ }
+
+ /* Create the MathProg translator workspace. */
+ tran = glp_mpl_alloc_wksp();
+ if (!lp) {
+ glp_error("Out of memory\n");
+ }
+
+ /* Set the pseudo random number generator seed. */
+ glp_mpl_init_rand(tran, task->seed);
+
+ /* Read the model file. */
+ ret = glp_mpl_read_model (tran, task->filename, GLP_OFF);
+ if (ret != 0) {
+ glp_error("Model %s is not valid\n", task->filename);
+ }
+
+ /* Generate the model. */
+ ret = glp_mpl_generate(tran, NULL);
+ if (ret != 0) {
+ glp_error("Cannot generate model %s\n", task->filename);
+ }
+
+ /* Build the problem. */
+ glp_mpl_build_prob(tran, lp);
+
+ /* Solve the problem. */
+ glp_init_iocp(&iocp);
+ iocp.presolve = GLP_ON;
+ ret = glp_intopt(lp, &iocp);
+ if (ret == 0) {
+ /* Execute the post solve part of the model. */
+ glp_mpl_postsolve(tran, lp, GLP_MIP);
+ }
+
+ /* Release the memory. */
+ glp_mpl_free_wksp (tran);
+ glp_delete_prob(lp);
+
+ if (0 == task->seed % 3) {
+ glp_error("Voluntarily throwing an error in %s at line %d\n",
+ __FILE__, __LINE__);
+ }
+
+ glp_term_hook(NULL, NULL);
+
+ glp_error_hook(NULL, NULL);
+
+ glp_free_env();
+}
+
+#ifdef __WOE__
+DWORD run(void *arg)
+{
+#else
+void *run(void *arg)
+{
+#endif
+ worker(arg);
+ pthread_exit(NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, n, rc;
+ struct task *tasks;
+
+ /* Make sure thread local memory is used by the GLPK library. */
+ if (!glp_config("TLS")) {
+ printf("The loaded GLPK library does not support thread local memory.\n"
+ "You need a version of the library configured with "
+ "--enable-reentrant=yes to run this program.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check program arguments. */
+ if (argc != 3) {
+ printf("Usage %s filename threads\n"
+ " filename - MathProg model file\n"
+ " threads - number of threads\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Parse the arguments. */
+ n = atoi(argv[2]);
+ if (n > 50) {
+ printf("Number of threads is to high (> 50).\n");
+ exit(EXIT_FAILURE);
+ }
+ if (n <= 1) {
+ printf("Need positive number of threads\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Allocate memory for the task descriptors. */
+ tasks = calloc(n, sizeof(struct task));
+ if (!tasks) {
+ printf("Out of memory");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create a mutex for console output. */
+ pthread_mutex_init(&mutex, NULL);
+
+ /* Create the threads. */
+ for (i = 0; i < n; ++i) {
+ tasks[i].filename = argv[1];
+ tasks[i].seed = i + 1;
+ tasks[i].pos = 0;
+ tasks[i].buf[0] = 0;
+ tasks[i].line = 0;
+ rc = pthread_create(&tasks[i].tid, NULL, run, &tasks[i]);
+ if (rc) {
+ printf("ERROR; return code from pthread_create() is %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Wait for all threads to complete. */
+ for (i = 0; i < n; ++i) {
+ pthread_join(tasks[i].tid, NULL);
+ }
+
+ /* Destroy the mutex. */
+ pthread_mutex_destroy(&mutex);
+
+ return EXIT_SUCCESS;
+}
diff --git a/glpk-5.0/examples/threads/thread.h b/glpk-5.0/examples/threads/thread.h
new file mode 100644
index 0000000..79c3dde
--- /dev/null
+++ b/glpk-5.0/examples/threads/thread.h
@@ -0,0 +1,50 @@
+/* thread.h (pthread emulation for Windows) */
+
+/***********************************************************************
+* This code is part of GLPK (GNU Linear Programming Kit).
+* Copyright (C) 2011-2017 Free Software Foundation, Inc.
+* Written by Heinrich Schuchardt <xypron.glpk@gmx.de>.
+*
+* GLPK is free software: you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GLPK is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+* License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
+***********************************************************************/
+
+#ifndef THREAD_H
+
+#define THREAD_H 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#ifdef __WOE__
+#include <windows.h>
+typedef CRITICAL_SECTION pthread_mutex_t;
+typedef HANDLE pthread_t;
+// @todo The return type of routine C is "DWORD" for Windows and
+// "void *" for Posix.
+#define pthread_create(A,B,C,D) \
+ (int)((*A = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&C,D,0,NULL))==NULL)
+#define pthread_exit(A) ExitThread(0)
+#define pthread_mutex_destroy(A) DeleteCriticalSection(A)
+#define pthread_mutex_init(A,B) (InitializeCriticalSection(A),0)
+#define pthread_mutex_lock(A) (EnterCriticalSection(A),0)
+#define pthread_mutex_unlock(A) (LeaveCriticalSection(A),0)
+#define pthread_self() GetCurrentThreadId()
+#define pthread_join(A, B) \
+ (WaitForSingleObject(A, INFINITE),CloseHandle(A),0)
+#else
+#include <pthread.h>
+#endif
+
+#endif // THREAD_H