Here we present a sample generic parallel algorithm for probabilistic computations. We use this generic algorithm to derive a specific parallel algorithm for a popular discrete optimization problem, the knapsack problem.
1. Introduction
2. Generic Parallel Probabilistic Algorithm
3. Derivation of a Parallel Knapsack Algorithms
4. Related Links
A typical probabilistic algorithm tries to approximately solve a problem with a vast number of potential solutions by first generating a smaller set of candidate solutions. Then, the algorithm tries to improve the set of candidate solution by means of some probabilistic computations, such as simulated annealing, hill-climbing, cross-over, or Monte-Carlo trials. Such probabilistic computations can be very intensive yet highly independent for the individual candidate solutions. As a consequence, a cluster of workstations can efficiently perform probabilistic computations with little coordination between the processors.
It is possible to specify a common parallel control structure as a generic parallel algorithm for probabilistic computations. Such a generic algorithm implements process control and communication in a problem-independent manner. The generic parallel algorithm can be glued together with domain-specific sequential-only algorithms in order to derive approximate parallel solutions for different intractable problems.
We specify this generic master-server algorithm as a module MS
using the parallel language Paradigm/SP.
module MS;
{probabilistic master-server}const
solLimit* = 10;
type
domain* = class
generations*: integer;
end;
solution* = ..;
set* =
array[1..solLimit] of solution;
population* =
class
s*: set;
popSize*: integer;
end;
channel = *(domain, solution);
net = array [1..solLimit] of channel;procedure climb*(
d: domain;
var s: solution);
begin end;procedure recombine*(
d: domain;
var p: population);
begin end;procedure setBest*(
var p: population);
begin end;procedure server(
c: channel);
var
d: domain; s: solution;
i: integer;
begin
{ writeln('server started...');}
receive(c, d);
for i := 1 to d.generations do
begin
receive(c, s);
climb(d, s);
send(c, s);
end;
end;procedure master(c: net;
d: domain;
var p: population);
var
i, g: integer;
begin
writeln('master started...');
for i := 1 to p.popSize do
send(c[i], d);
for g := 1 to d.generations do
begin
for i := 1 to p.popSize do
send(c[i], p.s[i]);
for i := 1 to p.popSize do
receive(c[i], p.s[i]);
setBest(p);
recombine(d, p);
end;
end;procedure PHCC-(d: domain;
var p: population);
var c: net;
i: integer;
begin
for i := 1 to p.popSize do
open(c[i]);
[sic]parallel
master(c, d, p) |
forall i := 1 to p.popSize do
server(c[i])
end
end;begin
writeln('MS loaded');
end.
We specify this parallel knapsack algorithm using Paradigm/SP.
The derivation is programmed by means of module extension and consitst
of the following modules:
Randoms: Random number generation | |
Sol: Solution representation | |
Pop: Population representation | |
Knap: Knapsack solver | |
Main: Main module |
module Randoms;
procedure probab*(var p: real);
begin
p := random(1000) / 1000.0;
end;procedure index*(var ind: integer;
low, high: integer);
begin
ind := random(high-low+1) + low;
end;
begin
writeln('Randoms loaded');
end.{......................}
module Sol(MS);
import Randoms;const
probLimit* = 40; { largest possible solution }
climbLimit* = 10; { largest possible number of climbers }type
bitArray* = array[1..probLimit] of boolean;
solution* = class
bit*: bitArray;
fitness*: real;
end;
domain* =
class
solSize*: integer;
onesProbab*: real;
nofClimbers*: integer;
maxAttempts*: integer;
end;{ methods }
procedure evaluate*(
d: domain;
var s: solution);
{ abstract fitness function,
must be defined in an application }
begin end;procedure newSol*(
d: domain;
var s: solution
);
var k: integer; r: real;
begin
for k := 1 to d.solSize do
begin
Randoms.probab(r);
if r <= d.onesProbab then s.bit[k] := true
else s.bit[k] := false;
end;
evaluate(d, s);
end;procedure climb*(
d: domain;
var s: solution);
type
climbersArray =
array[1..climbLimit] of solution;
var
climbers: climbersArray;
i, k, failed: integer;
climbing: boolean;
begin
failed := 0;
for i := 1 to d.nofClimbers do
climbers[i] := s;
while failed < d.maxAttempts do
begin
climbing := false;
for i := 1 to d.nofClimbers do
begin
{ mutate climbers[i] at location k; }
Randoms.index(k, 1, d.solSize);
climbers[i].bit[k] := not climbers[i].bit[k];
evaluate(d, climbers[i]);
if s.fitness < climbers[i].fitness then
begin
s := climbers[i];
climbing := true;
end;
end;
if climbing then
begin
failed := 0;
for i := 1 to d.nofClimbers do
climbers[i] := s;
end
else
failed := succ(failed);
end;
end;procedure crossover*(
d: domain;
var s, s1: solution);
{ perform in place, w/o creating a new solution }
var
recombLocation, k: integer;
save: boolean;
begin
{ swap genes between solutions s and s1
at a randomly calculated crossover position:
}Randoms.index(recombLocation,
2, d.solSize-1);
for k := 1 to recombLocation do
begin
save := s.bit[k];
s.bit[k] := s1.bit[k];
s1.bit[k] := save;
end;
evaluate(d, s);
evaluate(d, s1);
end;procedure printSol-(
d: domain;
s: solution);
var
i: integer;
begin
for i := 1 to d.solSize do begin
write(ord(s.bit[i]):2);
if (i mod 20) = 0 then writeln
end;
end;begin { sol }
writeln('Sol loaded')
end.{......................}
module Pop(Sol);
import Randoms;type
population* =
class
currentBest*: solution;
end;procedure setBest*(
var p: population);
var i: integer;
begin
for i := 1 to p.popSize do
if p.currentBest.fitness < p.s[i].fitness then
p.currentBest := p.s[i];
end;procedure setPopul*(
var d: domain;
var p: population);
var i: integer;
begin
for i := 1 to p.popSize do
newSol(d, p.s[i]);
p.currentBest := p.s[1];
setBest(p);
end;procedure permute*(
var p: population);
type
tagArray = array[1..solLimit] of boolean;
var
i, k, offset: integer;
solutionsToPermute: integer;
s: solution;
tagged: tagArray;begin
solutionsToPermute := p.popSize;
for i := 1 to p.popSize do
tagged[i] := false; { all initially untagged }
i := 1;
while solutionsToPermute > 1 do
begin
{ determine first solution to cross-over }
while tagged[i] do i := i + 1;
tagged[i] := true;
solutionsToPermute := solutionsToPermute - 1;
{ determine second solution to cross-over }
Randoms.index(offset, 1, solutionsToPermute);
k := i + offset;
while tagged[k] do k := k + 1;
tagged[k] := true;
solutionsToPermute := solutionsToPermute - 1;
s := p.s[i];
p.s[i] := p.s[k];
p.s[k] := s;
end;
end;procedure recombine*(
d: domain;
var p: population
);
var i: integer;
begin
begin
permute(p);
i := p.popSize;
while i > 1 do
begin
[sic] crossover(
d,
p.s[i],
p.s[i-1]
);
i := i-2;
end;
end;end;
begin { population }
writeln('Pop loaded');
end.{......................}
module Knap(Pop);
import Randoms;{parallel knapsack based on the hiearchy
aPMS > dChromo > dPopul >dKnapsak}const
maxValue = 2; { v }
maxRange = 1; { r }
type
numericArray = array [1..solLimit] of integer;
domain* =
class
capacity*: integer;
weight*, profit*: numericArray;
end;procedure evaluate*(
d: domain;
var s: solution);
const
lowFitness = 0;
var k, we, pr: integer;
begin
we := 0; pr := 0;
for k := 1 to d.solSize do
if s.bit[k] then begin
pr := pr + d.profit[k];
we := we + d.weight[k];
end;
if we > d.capacity then pr := lowFitness;
s.fitness := pr;
end;procedure setParameters(
var d: domain);
var k: integer;
f: real;
begin
for k := 1 to d.solSize do
begin
Randoms.index(d.weight[k], 1, maxValue);
d.profit[k] := d.weight[k] + maxRange;
end;
d.capacity := 0;
for k := 1 to d.solSize do
d.capacity := d.capacity + d.weight[k];
d.capacity := d.capacity div 2;
{population onesProbab}
f := 0.0;
for k := 1 to d.solSize do
f := f + d.weight[k];
d.onesProbab := d.capacity / f;
end;procedure setKnapsak-(
var d: domain;
var p: population);
var k: integer;
begin
writeln('strongly correlated average 0/1 knapsack domain');
writeln;
write('problemSize: ':40); readln(d.solSize);
assume((d.solSize > 0) and
(d.solSize <= solLimit));
write('populationSize: ':40); readln(p.popSize);
assume((p.popSize > 0) and
(p.popSize <= solLimit));
write('generations: ':40); readln(d.generations);
assume(d.generations > 0);
write('maxAttempts: ':40); readln(d.maxAttempts);
assume(d.maxAttempts > 0);d.nofClimbers := d.solSize div 4;
assume((d.nofClimbers > 0) and
(d.nofClimbers <= climbLimit));setParameters(d);
setPopul(d, p);
writeln('onesProbability: ':40 , d.onesProbab:1:3);
writeln('initial fitness: ':40, p.currentBest.fitness:1:2);
writeln;
writeln('starting ', d.generations:1, ' generations...');
end;procedure displayKnapsak-
(d: domain;
p: population);
var k: integer;
begin
writeln('capacity: ':20, d.capacity);
write('weights: ':20);
for k := 1 to d.solSize do
write(d.weight[k]:2);
writeln;
write('profits: ':20);
for k := 1 to d.solSize do
write(d.profit[k]:2);
writeln;
write('solution: ':20);
printSol(d, p.currentBest);
writeln('final fitness: ':20, p.currentBest.fitness:1:2);
writeln;
end;procedure run-;
var d: domain;
p: population;
begin
setKnapsak(d, p);
PHCC(d, p);
displayKnapsak(d, p);
end;begin { knapsack }
writeln('Knap loaded');
end.{......................}
module Main(Knap);
begin
writeln('Main loaded');
run;
end.
Last updated: October 1999.