From 7ca3e8817f1a6fa9f4985abdecaec777cfff1a9e Mon Sep 17 00:00:00 2001 From: Joe Moudrik Date: Tue, 4 Jun 2013 11:41:46 +0200 Subject: [PATCH] gostyle_old, commit old --- clanek_go_congress/Makefile | 2 +- cross_val.py | 2 +- gostyle.py | 2 +- kohonen.py | 757 ++++++++++++++++++++++ make_test_plot.sh | 10 + make_test_set.sh | 12 + make_train_set_strength.py | 94 +++ pca_str.py | 109 ++++ pdb-gtl/stats.py | 33 + style_consensus/style_consensus.tex | 6 +- tex/POSUDEK | 94 +-- tex/POSUDEK2 | 4 - tex/patcountdist.eps | 1199 +++++++++++++++++++++++++++++++++++ 13 files changed, 2276 insertions(+), 48 deletions(-) create mode 100755 kohonen.py create mode 100755 make_test_plot.sh create mode 100755 make_test_set.sh create mode 100755 make_train_set_strength.py create mode 100755 pca_str.py create mode 100755 pdb-gtl/stats.py rewrite tex/POSUDEK (96%) create mode 100644 tex/patcountdist.eps diff --git a/clanek_go_congress/Makefile b/clanek_go_congress/Makefile index c39f25b..efd06da 100644 --- a/clanek_go_congress/Makefile +++ b/clanek_go_congress/Makefile @@ -20,7 +20,7 @@ clanek.ps: clanek.dvi clanek.dvi: clanek.tex $(wildcard *.tex) clanek.bib Makefile $(wildcard ./img/*.eps) $(wildcard ./*.sty) rm -f *bbl cslatex $< - #bibtex clanek + bibtex clanek cslatex $< cslatex $< cslatex $< diff --git a/cross_val.py b/cross_val.py index 1c0901e..4a8f7e1 100644 --- a/cross_val.py +++ b/cross_val.py @@ -49,6 +49,6 @@ if __name__ == '__main__': print "train: ",len(tr), tr print "valid: ",len(val), val print - for tr, val in Shuffled(CrossValidation)(range(10), 10): + for tr, val in Shuffled(CrossValidation)(range(24), 5): print "train: ",len(tr), tr print "valid: ",len(val), val diff --git a/gostyle.py b/gostyle.py index 04a3913..101892b 100755 --- a/gostyle.py +++ b/gostyle.py @@ -151,7 +151,7 @@ class OccurenceVectorGenerator(VectorGenerator): def __init__(self, main_pat_file, num_features): self.output_dim = num_features - self.filename = main_pat_file + self.filename = main_pat_file self.generate_top_pattern_dict() def __call__(self, pat_file): diff --git a/kohonen.py b/kohonen.py new file mode 100755 index 0000000..d83b027 --- /dev/null +++ b/kohonen.py @@ -0,0 +1,757 @@ +# Copyright (c) 2009 Leif Johnson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +'''Basic self-organizing map implementation. + +This module contains the following Kohonen map implementations : + + - Map. A standard rectangular N-dimensional Kohonen map. + + - Gas. A vector quantizer that does not have a fixed topology. Neurons in a + gas are sorted for updates based on their distance from the cue, with the + sort order defining a topology for each cue presentation. + + - GrowingGas. A Gas-based quantizer that can add neurons dynamically to + explain high-error areas of the input space. + + - Filter. A wrapper over an underlying Map instance that maintains an explicit + estimate of the likelihood of each neuron. + +These are tested using the kohonen_test.py file in this source distribution. + +Because they have a grid topology, Map objects have some cool visualization +options, including Map.neuron_colormap and Map.distance_heatmap. These require +the Python Image Library. + +There are also some small utility classes : + + - DistanceMetric. A callable that takes two arrays with the same shapes and + returns a 2D matrix of elementwise distances between the inputs. Basically, + this class is used to calculate the distance between a cue and each neuron + in a Kohonen Map. + + - EuclideanMetric. A callable that calculates the Euclidean distance between + a cue and each neuron in a Kohonen Map. + + - etc. + + - Timeseries. A callable that takes no arguments and returns a value that + might vary over time. Each call to the function will generally return a + unique value (though this is not necessary). + + - ExponentialTimeseries. A callable that takes no arguments and returns an + exponentially decreasing (or increasing) series of values, dependent on + the parameters passed in at construction time. + + - etc. + +These utility objects are generally used to regulate the learning parameters in +Kohonen Map objects. +''' + +import numpy +from numpy import random as rng + + +class DistanceMetric(object): + '''A class that implements a distance metric for a self-organizing map.''' + + def __call__(self, x, y): + '''Return the distances from x to y, along the last array index.''' + raise NotImplementedError + +class CosineMetric(DistanceMetric): + '''Implements the cosine distance.''' + + def __call__(self, x, y): + nx = numpy.sqrt(numpy.sum(x * x, axis=-1)) + ny = numpy.sqrt(numpy.sum(y * y, axis=-1)) + # the cosine metric returns 1 when the args are equal, 0 when they are + # orthogonal, and -1 when they are opposite. we want the opposite + # effect, i.e. we minimize metric distance to find the "winner". also, + # add 1 to the result so that the distance is always nonnegative. + return 1 - numpy.sum(x * y, axis=-1) / nx / ny + +class EuclideanMetric(DistanceMetric): + '''Implements the euclidean distance (L-2 norm).''' + + def __call__(self, x, y): + d = x - y + return numpy.sqrt(numpy.sum(d * d, axis=-1)) + +class ManhattanMetric(DistanceMetric): + '''Implements the manhattan distance (L-1 norm).''' + + def __call__(self, x, y): + return numpy.sum(numpy.abs(x - y), axis=-1) + +class WeightedEuclideanMetric(EuclideanMetric): + '''Implements a standard euclidean distance with weighted dimensions.''' + + def __init__(self, weights): + self._weights = numpy.array(weights) + + def __call__(self, x, y): + d = x - y + w = numpy.resize(self._weights, d.shape) + return numpy.sqrt(numpy.sum(w * d * d, axis=-1)) + + +class Timeseries(object): + '''Represents some sort of value that changes over time.''' + + def __init__(self): + '''Set up this timeseries.''' + super(Timeseries, self).__init__() + self.ticks = 0 + + def __call__(self): + '''Call this timeseries.''' + t = self.ticks + self.ticks += 1 + return t + + def reset(self): + '''Reset the time for this series.''' + self.ticks = 0 + +class ConstantTimeseries(Timeseries): + '''This timeseries just returns a constant value.''' + + def __init__(self, k=1): + '''Set up this series with a constant value.''' + self.k = k + + def __call__(self): + '''Return the constant.''' + return self.k + +class ExponentialTimeseries(Timeseries): + '''Represents an exponential decay process.''' + + def __init__(self, rate=-1, initial=1, final=0): + '''Create a new exponential timeseries object.''' + super(ExponentialTimeseries, self).__init__() + self.initial = initial - final + self.rate = rate + self.final = final + self.last = initial + + def __call__(self): + '''Return an exponentially-decreasing series of values.''' + super(ExponentialTimeseries, self).__call__() + self.last = self.final + self.initial * numpy.exp(self.rate * self.ticks) + return self.last + + +class Parameters(object): + '''We are plain old data holding self-organizing map parameters.''' + + def __init__(self, + dimension=None, + shape=None, + metric=None, + learning_rate=None, + neighborhood_size=None, + noise_variance=None): + '''This class holds standard parameters for self-organizing maps. + + dimension: The length of a neuron vector in a Map or a Gas. + + shape: The shape of the neuron topology in whatever Map or Gas we are + building. + + metric: The distance metric to use when comparing cues to neurons in the + map. Defaults to EuclideanMetric. + + learning_rate: This parameter determines the time course of the learning + rate for a Map. This parameter should be a callable that takes no + arguments and returns a floating point value for the learning rate. + + If this parameter is None, a default learning rate series will be + used, equivalent to ExponentialTimeseries(-1e-3, 1, 0.2). + + If this parameter is a numeric value, it will be used as the + constant value for the learning rate: ConstantTimeseries(value). + + neighborhood_size: Like the learning rate, this parameter determines the + time course of the neighborhood size parameter. It should be a + callable that takes no arguments and returns a neighborhood size for + storing each cue. + + If this is None, a default neighborhood size series will be used. The + initial size will be the maximum of the dimensions given in shape, and + the decay will be -1e-3: ExponentialTimeseries(-1e-3, max(shape), 1). + + If this is a floating point value, it will be used as a constant + neighborhood size: ConstantTimeseries(value). + + noise_variance: Like the learning rate and neighborhood size, this + should be a factory for creating a callable that creates noise + variance values. + + If this is None, no noise will be included in the created Maps. + + If this parameter is a number, it will be used as a constant noise + variance. + ''' + assert dimension is not None + self.dimension = dimension + + assert shape is not None + self.shape = shape + + self.metric = metric or EuclideanMetric() + + ET = ExponentialTimeseries + CT = ConstantTimeseries + + self.learning_rate = learning_rate + if isinstance(learning_rate, (float, int)): + self.learning_rate = CT(learning_rate) + if learning_rate is None: + self.learning_rate = ET(-1e-3, 1, 0.2) + + self.neighborhood_size = neighborhood_size + if isinstance(neighborhood_size, (float, int)): + self.neighborhood_size = CT(neighborhood_size) + if neighborhood_size is None: + self.neighborhood_size = ET(-1e-3, max(shape), 1) + + self.noise_variance = noise_variance + if isinstance(noise_variance, (float, int)): + self.noise_variance = CT(noise_variance) + + +def heatmap(raw, axes=(0, 1), lower=None, upper=None): + '''Create a heat map image from the given raw matrix. + + raw: An array of values to use for the image pixels. + axes: The axes in the array that we want to preserve for the final image. + All other axes will be summed away. + lower: If given, clip values in the matrix to this lower limit. If not + given, raw.min() will be used. + upper: If given, clip values in the matrix to this upper limit. If not + given, raw.max() will be used. + + Returns an annotated Image object (as returned from _image). + ''' + assert len(axes) == 2 + for ax in xrange(len(raw.shape) - 1, -1, -1): + if ax in axes: + continue + raw = raw.sum(axis=ax) + l = lower + if l is None: + l = raw.min() + l *= l < 0 and 1.01 or 0.99 + u = upper + if u is None: + u = raw.max() * 1.01 + u *= u > 0 and 1.01 or 0.99 + return _image(raw, l, u) + + +def colormap(raw, axes=(0, 1, 2), layers=(0, 1, 2)): + '''Create an RGB image using the given layers of a 3D raw values matrix. + + raw: An array of raw values to use for the image. + axes: The axes in the array that we want to preserve for the final image. + All other axes will be summed away. + layers: The indices of the third preserved axis that we should use for the + red, green, and blue channels in the output image. + + Raw values will be scaled along each layer to lie in [lower, upper], where + lower (upper) is the global lower (upper) bound of all values in each of the + raw layers. + + Returns an Image object, as in the heatmap() function. + ''' + assert len(axes) == len(layers) == 3 + for ax in xrange(len(raw.shape) - 1, -1, -1): + if ax in axes: + continue + raw = raw.sum(axis=ax) + u = -numpy.inf + l = numpy.inf + for i in layers: + v = raw[:, :, i] + l = min(l, v.min()) + u = max(u, v.max()) + l *= l < 0 and 1.01 or 0.99 + u *= u > 0 and 1.01 or 0.99 + return _image(raw[:, :, layers], l, u, 'RGB') + + +def _image(values, lower, upper, format='L'): + '''Create a PIL image using the given 2D array of values. + + Pixel values in the range [lower, upper] are scaled linearly to [0, 1] + before creating the image. + + Returns an Image object annotated with the lower and upper bounds that were + used to scale the values to convert them to pixels. + ''' + from PIL import Image + ratios = (values - lower) / (upper - lower) + img = Image.fromarray(numpy.array(256 * ratios, numpy.uint8), format) + img.lower_bound = lower + img.upper_bound = upper + return img + + +def _zeros(shape, dtype='d'): + '''Get a blank (all-zero) matrix with a certain shape.''' + return numpy.zeros(shape, dtype=dtype) + + +def itershape(shape): + '''Given a shape tuple, iterate over all indices in that shape.''' + if not shape: + yield () + return + for i in xrange(shape[0]): + for z in itershape(shape[1:]): + yield (i, ) + z + + +def argsample(pdf, n=1): + '''Return n indices drawn proportionally from a discrete mass vector.''' + assert (pdf >= 0).all(), 'cannot sample from %r!' % pdf + cdf = pdf.cumsum() + return numpy.searchsorted(cdf, rng.uniform(0, cdf[-1], n)) + + +def sample(pdf, n=1): + '''Return n samples drawn proportionally from a discrete mass vector.''' + assert len(pdf.shape) == 1 + return pdf[argsample(pdf, n)] + + +class Map(object): + '''Basic implementation of a rectangular N-dimensional self-organizing map. + + A Self-Organizing or Kohonen Map (henceforth just Map) is a group of + lightweight processing units called neurons, which are here implemented as + vectors of real numbers. Neurons in a Map are arranged in a specific + topology, so that a given neuron is connected to a small, specific subset of + the overall neurons in the Map. In addition, the Map uses a distance metric + (e.g., Euclidean distance) for computing similarity between neurons and cue + vectors, as described below. + + The Map accepts cues---vectors of real numbers---as inputs. In standard Map + usage, cues represent some data point of interest. Normally applications of + Maps use input vectors like the activation patterns for an array of sensors, + term frequency vectors for a document, etc. Cues are stored in the Map as + follows : First, a "winner" neuron w is chosen from the Map, and, second, + the neurons in the Map topologically near w are altered so that they become + closer to the cue. Each of these steps is described briefly below. + + For the first step, the Map computes the distance between the cue and each + of the Map neurons using its metric. The neuron closest to the cue under + this metric is declared the "winner" w. Alternatively, the winner can be + selected probabilistically based on the overall distance landscape. + + Next, the Map alters the neurons in the neighborhood of w, normally using + some function of the difference between the cue and the neuron being + modified. The weight of the alteration decreases exponentially as the + topological distance from w increases. The learning rule for a neuron n is + + n += eta * exp(-d**2 / sigma**2) * (c - n) + + where eta is the learning rate, sigma is called the neighborhood size, d is + the topological distance between n and w, and c is the cue vector being + stored in the map. Eta and sigma normally decrease in value over time, to + take advantage of the empirical machine learning benefits of simulated + annealing. + + The storage mechanism in a Map has the effect of grouping cues with similar + characteristics into similar areas of the Map. Because the winner---and its + neighborhood---are altered to look more like the cues that they capture, the + winner for a given cue will tend to win similar inputs in the future. This + tends to cluster similar Map inputs, and can lead to interesting data + organization patterns. + ''' + + def __init__(self, params): + '''Initialize this Map.''' + self._shape = params.shape + self.dimension = params.dimension + self.neurons = _zeros(self.shape + (self.dimension, )) + + self._metric = params.metric + + self._learning_rate = params.learning_rate + self._neighborhood_size = params.neighborhood_size + self._noise_variance = params.noise_variance + + # precompute a neighborhood mask for performing fast storage updates. + # this mask is the same dimensionality as self.shape, but twice the size + # along each axis. the maximum value in the mask is 1, occurring in the + # center. values decrease in a gaussian fashion from the center. + S = tuple(2 * size - 1 for size in self.shape) + self._neighborhood_mask = _zeros(S) + for coords in itershape(S): + z = 0 + for axis, offset in enumerate(coords): + d = offset + 1 - self.shape[axis] + z += d * d + self._neighborhood_mask[coords] = numpy.exp(-z / 2) + + @property + def shape(self): + return self._shape + + def neuron(self, coords): + '''Get the current state of a specific neuron.''' + return self.neurons[coords] + + def reset(self, f=None): + '''Reset the neurons and timeseries in the Map. + + f: A callable that takes a neuron coordinate and returns a value for + that neuron. Defaults to random values from the standard normal. + ''' + self._learning_rate.reset() + self._neighborhood_size.reset() + if f is None: + self.neurons = rng.randn(*self.neurons.shape) + else: + for z in itershape(self.shape): + self.neurons[z] = f(z) + + def weights(self, distances): + '''Get an array of learning weights to use for storing a cue.''' + i = self.smallest(distances) + z = [] + for axis, size in enumerate(self.flat_to_coords(i)): + offset = self.shape[axis] - size - 1 + z.append(slice(offset, offset + self.shape[axis])) + sigma = self._neighborhood_size() + return self._neighborhood_mask[z] ** (1.0 / sigma / sigma) + + def distances(self, cue): + '''Get the distance of each neuron in the Map to a particular cue.''' + z = numpy.resize(cue, self.neurons.shape) + return self._metric(z, self.neurons) + + def flat_to_coords(self, i): + '''Given a flattened index, convert it to a coordinate tuple.''' + coords = [] + for limit in reversed(self.shape[1:]): + i, j = divmod(i, limit) + coords.append(j) + coords.append(i) + return tuple(reversed(coords)) + + def winner(self, cue): + '''Get the coordinates of the most similar neuron to the given cue. + + Returns a flat index ; use flat_to_coords to convert this to a neuron + index. + ''' + return self.smallest(self.distances(cue)) + + def sample(self, n): + '''Get a sample of n neuron coordinates from the map. + + The returned values will be flat indices ; use flat_to_coords to convert + them to neuron indices. + ''' + return rng.randint(0, self.neurons.size / self.dimension - 1, n) + + def smallest(self, distances): + '''Get the index of the smallest element in the given distances array. + + Returns a flat index ; use flat_to_coords to convert this to a neuron + index. + ''' + assert distances.shape == self.shape + return distances.argmin() + + def learn(self, cue, weights=None, distances=None): + '''Add a new cue vector to the Map, moving neurons as needed.''' + if weights is None: + if distances is None: + distances = self.distances(cue) + weights = self.weights(distances) + assert weights.shape == self.shape + weights.shape += (1, ) + delta = numpy.resize(cue, self.neurons.shape) - self.neurons + eta = self._learning_rate() + self.neurons += eta * weights * delta + if self._noise_variance: + self.neurons += rng.normal( + 0, self._noise_variance(), self.neurons.shape) + + def neuron_heatmap(self, axes=(0, 1), lower=None, upper=None): + '''Return an image representation of this Map.''' + return heatmap(self.neurons, axes, lower, upper) + + def distance_heatmap(self, cue, axes=(0, 1), lower=None, upper=None): + '''Return an image representation of the distance to a cue.''' + return heatmap(self.distances(cue), axes, lower, upper) + + +class Gas(Map): + '''A neural Gas is a topologically unordered collection of neurons. + + Learning takes place in the Gas by ordering the neurons according to their + distance from each cue that is presented. Neurons are updated using this + sorted order, with an exponentially decreasing weight for neurons that are + further (in sort order) from the cue. + ''' + + def __init__(self, params): + '''Initialize this Gas. A Gas must have a 1D shape.''' + super(Gas, self).__init__(params) + assert len(params.shape) == 1 + self.N = params.shape[0] + + def weights(self, distances): + # this is slightly different from a traditional gas, which uses a linear + # negative exponential for update weights: + # + # return numpy.exp(-distances.argsort() / sigma) + # + # quadratic weights more closely match the standard kohonen behavior. + z = distances.argsort() / self._neighborhood_size() + return numpy.exp(-z * z) + + +def _array_without(a, i): + '''Remove the ith row and column from 2x2 array a.''' + if i == 0: + return a[1:, 1:].copy() + if i == a.shape[0] - 1: + return a[:-1, :-1].copy() + return numpy.hstack((numpy.vstack((a[:i, :i], a[i+1:, :i])), + numpy.vstack((a[:i, i+1:], a[i+1:, i+1:])))) + + +def _vector_without(v, i): + '''Remove the ith element from vector v.''' + if i == 0: + return v[1:].copy() + if i == v.shape[0] - 1: + return v[:-1].copy() + return numpy.concatenate((v[:i], v[i+1:])) + + +class GrowingGasParameters(Parameters): + '''Parameters for Growing Neural Gases.''' + + def __init__(self, + growth_interval=2, + max_connection_age=5, + error_decay=0.99, + neighbor_error_decay=0.99, + **kwargs): + super(GrowingGasParameters, self).__init__(**kwargs) + + self.growth_interval = growth_interval + self.max_connection_age = max_connection_age + + self.error_decay = error_decay + self.neighbor_error_decay = neighbor_error_decay + + +class GrowingGas(Gas): + '''A Growing Neural Gas uses a variable number of variable-topology neurons. + + In essence, a GNG is similar to a standard Gas, but there is additional + logic in this class for adding new neurons to better explain areas of the + sample space that currently have large error. + ''' + + def __init__(self, params): + '''Initialize a new Growing Gas with parameters.''' + self._size = 2 + + super(GrowingGas, self).__init__(params) + + self._growth_interval = params.growth_interval + self._max_connection_age = params.max_connection_age + + self._error_decay = params.error_decay + self._neighbor_error_decay = params.neighbor_error_decay + + self._errors = _zeros(self.shape) + self._connections = _zeros((self._size, self._size), '=i2') - 1 + + self._cue_count = 0 + + @property + def shape(self): + return (self._size, ) + + def neighbors(self, i): + return self._connections[i] + + def _connect(self, a, b): + self._set_connection(a, b, 0) + + def _age_connection(self, a, b): + self._set_connection(a, b, self._connections[a, b] + 1) + + def _disconnect(self, a, b): + self._set_connection(a, b, -1) + + def _set_connection(self, a, b, age): + self._connections[a, b] = self._connections[b, a] = age + + def learn(self, cue, weights=None, distances=None): + '''Store a cue in the gas.''' + distances = self.distances(cue) + + # find the two closest neurons. connect them. add error to the winner. + w = distances.argmin() + d = distances[w] + self._errors[w] += d * d + distances[w] = 1 + distances.max() + self._connect(w, distances.argmin()) + + # move the winner and all of its neighbors toward the cue. + eta = self._learning_rate() + def adjust(i): + self.neurons[i] += eta * (cue - self.neurons[i]) + adjust(w) + for j, age in enumerate(self.neighbors(w)): + if 0 <= age < 65535: # prevent 16-bit age counter overflow + adjust(j) + self._age_connection(w, j) + + # add noise. + if self._noise_variance: + self.neurons += rng.normal( + 0, self._noise_variance(), self.neurons.shape) + + # manipulate the gas topology by pruning and growing as needed. + self._prune() + self._cue_count += 1 + if (self._cue_count % self._growth_interval == 0 and + self._size < self.N): + self._grow() + + # decrease unit error. + self._errors *= self._error_decay + + def _prune(self): + '''Remove old connections, and prune any disconnected neurons.''' + mask = numpy.where(self._connections > self._max_connection_age) + if self._size == 2 or len(mask[0]) == 0: + return + + # remove connections older than max_connection_age (set to -1). + self._connections[mask] = -1 + + # remove neurons that were disconnected after removing connections. + indices, = numpy.where((self._connections < 0).all(axis=0)) + for i in indices[::-1]: + self.neurons = _vector_without(self.neurons, i) + self._errors = _vector_without(self._errors, i) + self._connections = _array_without(self._connections, i) + self._size -= 1 + + def _grow(self): + '''Add a single neuron between two high-error neurons.''' + # identify the neuron with max error, and its max error neighbor. + q = self._errors.argmax() + f = (self._errors * (self.neighbors(q) >= 0)).argmax() + r = self._size + + # allocate a new neurons array. + neurons = _zeros((r + 1, self.dimension)) + neurons[:r] = self.neurons + self.neurons = neurons + self.neurons[r] = (self.neurons[q] + self.neurons[f]) / 2 + + # insert new node between old two nodes. + self._disconnect(q, f) + conn = _zeros((r + 1, r + 1), '=i2') - 1 + conn[:r, :r] = self._connections + self._connections = conn + self._connect(q, r) + self._connect(r, q) + + # update error for the new and old neurons. + self._errors = numpy.concatenate((self._errors, [0])) + self._errors[f] *= self._neighbor_error_decay + self._errors[q] *= self._neighbor_error_decay + self._errors[r] = (self._errors[f] + self._errors[q]) / 2 + + self._size += 1 + + +class Filter(object): + '''A Filter is an estimate of the probability density of the inputs.''' + + def __init__(self, map, history=None): + '''Initialize this Filter with an underlying Map implementation. + + history: A callable that returns values in the open interval (0, 1). + These values determine how much new cues influence the activation + state of the Filter. + + A 0 value would mean that no history is preserved (i.e. each new cue + stored in the Filter completely determines the activity of the Filter) + while a 1 value would mean that new cues have no impact on the + activity of the Filter (i.e. the initial activity is the only activity + that is ever used). + ''' + self.map = map + self.activity = _zeros(self.map.shape) + 1 + self.activity /= self.activity.sum() + self._history = history is None and ConstantTimeseries(0.7) or history + + @property + def shape(self): + return self.map.shape + + def neuron(self, coords): + return self.map.neuron(coords) + + def reset(self, f=None): + return self.map.reset(f=f) + + def distances(self, cue): + return self.map.distances(cue) + + def flat_to_coords(self, i): + return self.map.flat_to_coords(i) + + def winner(self, cue): + return self.map.winner(cue) + + def smallest(self, distances): + return self.map.smallest(distances) + + def weights(self, distances): + return self.map.weights(distances) * (1 - self.activity) + + def sample(self, n): + return argsample(self.activity, n) + + def learn(self, cue, **kwargs): + d = self.distances(cue) + p = numpy.exp(-self.distances(cue).argsort()) + l = self._history() + self.activity = l * self.activity + (1 - l) * p / p.sum() + self.map.learn(cue, **kwargs) diff --git a/make_test_plot.sh b/make_test_plot.sh new file mode 100755 index 0000000..ad0ae0f --- /dev/null +++ b/make_test_plot.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +find "../pdb-gtl/rawpat_files_merged" | while read a ; do + +[ ! -e "$a" -o -d "$a" ] && continue +rank="$(echo $a | sed 's/\/[^\/]*$//;s/.*\///')" + +echo "$rank $(./make_input_vector.py $a | ./gnet/gnet_run go_net.net) 0" + +done diff --git a/make_test_set.sh b/make_test_set.sh new file mode 100755 index 0000000..fb193c9 --- /dev/null +++ b/make_test_set.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +find "../pdb-gtl/rawpat_files_merged_test" | while read a ; do + +[ ! -e "$a" -o -d "$a" ] && continue +rank="$(echo $a | sed 's/\/[^\/]*$//;s/.*\///')" + +#echo "$rank $(./make_input_vector.py $a | ./gnet/gnet_run go_net.net) 0" +./make_input_vector.py $a +echo "from data_about_players import Data ; print Data.strength_linear_vector['$rank'][0]" | python + +done diff --git a/make_train_set_strength.py b/make_train_set_strength.py new file mode 100755 index 0000000..d647f3d --- /dev/null +++ b/make_train_set_strength.py @@ -0,0 +1,94 @@ +#!/usr/bin/python +import sys +from gostyle import print_vector, print_set_to_file, dump_object_to_file, load_object_from_file +from gostyle import InputVectorGenerator, PlanarOutputVectorGenerator, StrategyOutputVectorGenerator, PlayerStrategyIdentificator +from gostyle import Compose, PCA, Combinator, Rescale + +from data_about_players import Data + + +if __name__ == '__main__': + pickle_filename = 'input_gen.pickle' + train_set_filename = 'train_set.data' + root_dir = '../pdb-gtl/' + main_pat_filename = root_dir + 'all.pat' + player_vector = Data.strength_linear_vector + num_features = 400 + + ### Objects creating input vector when called + print >>sys.stderr, "Creating input vector generator from main pat file:", main_pat_filename + i = InputVectorGenerator(main_pat_filename, num_features) + ### Objects creating output vector when called + o = PlanarOutputVectorGenerator(player_vector) + + def list_all(): + import os, random, shutil + #raw = root_dir + 'testpat_files' + raw = root_dir + 'rawpat_files_merged' + ranks = os.listdir(raw) + for rank in ranks: + plays = os.listdir(raw + '/'+ rank) + for play in plays: + yield (rank, raw + '/' + rank + '/' + play) + + + # Create list of input vectors + input_vectors = [] + output_vectors = [] + for rank,filename in list_all(): + try: + ii = i(filename) + oo = o(rank) + input_vectors += [ ii ] + output_vectors += [ oo ] + except: + continue + + print len(input_vectors) + + ### PCA example usage + # Change this to False, if you do not want to use PCA + use_pca = True + if use_pca: + # Create PCA object, trained on input_vectors + print >>sys.stderr, "Running PCA." + pca = PCA(input_vectors, reduce=True) + # Perform a PCA on input vectors + input_vectors = pca.process_list_of_vectors(input_vectors) + # Creates a Composed object that first generates an input vector + # and then performs a PCA analysis on it. + i = Compose(i, pca) + + ### We now save the InputGenerator to a file for later use. + # This is especially feasible when we use the PCA, since thus we may use + # the once trained object again. + print "Saving the input generator object to file:", pickle_filename + dump_object_to_file(i, pickle_filename) + + #r = Rescale(-1.0,1.0) + # Create list of output vectors + # Create list of pairs: [ (input_vec_1, output_vec_1), (input_vec_2, output_vec_2), ... ] + data = zip(input_vectors, output_vectors) + # And filter out players with no output vector + # (since PlanarOutputVectorGenerator returns None if the player does not have defined output vector) + # Note: This would not be needed if we set `players = PlanarOutputVectorGenerator.players' in the beggining + data = filter(lambda x: x[1] != None, data) + + if len(data) == 0: + print >>sys.stderr, "No data." + sys.exit() + + ### We can enlarge the data set by adding linear combinations of input and output vectors + use_lin_combinations = False + if use_lin_combinations: + data += Combinator().combine(data) + + # Save the result + # Create the neural network train set + print >>sys.stderr, "Saving neural network train set to file:", train_set_filename + print_set_to_file(data,train_set_filename) + print >>sys.stderr, "Printed %d pairs of dimensions (%d,%d)."%(len(data), len(data[0][0]), len(data[0][1])) + + #for i,o in data: + # print_vector(i) + # print_vector(o) diff --git a/pca_str.py b/pca_str.py new file mode 100755 index 0000000..96eaf40 --- /dev/null +++ b/pca_str.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +import sys +from gostyle import * +from math import sqrt +from itertools import izip +import numpy + +from data_about_players import Data + +if __name__ == '__main__': + root_dir = '../pdb-gtl/' + main_pat_filename = root_dir + 'all.pat' + player_vector = Data.strength_linear_vector + num_features = 400 + + ### Object creating input vector when called + print >>sys.stderr, "Creating input vector generator from main pat file:", main_pat_filename + i = InputVectorGenerator(main_pat_filename, num_features)#, rescale=LogRescale) + + #raw = root_dir + 'testpat_files' + def list_dir(raw): + import os, random, shutil + ranks = os.listdir(raw) + tot={} + for rank in ranks: + plays = os.listdir(raw + '/'+ rank) + for play in plays: + tot[raw + '/' + rank + '/' + play] = rank + return tot + + train_set_dir = root_dir + 'train_set' + test_set_dir = root_dir + 'rawpat_files_merged_test' + train_dict = list_dir(train_set_dir) + test_dict = list_dir(test_set_dir) + + train_pl = [] + input_vectors_train = [] + for f, rank in train_dict.items(): + try: + input_vectors_train += [i(f)] + except: + continue + train_pl += [rank] + + input_vectors_test = [] + test_pl = [] + test_files = [] + for f, rank in test_dict.items(): + try: + input_vectors_test += [i(f)] + except: + continue + + test_pl += [rank] + test_files += [f] + + + #if len(input_vectors_train) == 0: + # print >>sys.stderr, "No reference vectors." + # sys.exit() + if len(input_vectors_test) == 0: + print >>sys.stderr, "No vectors to process." + sys.exit() + + ### PCA example usage + # Change this to False, if you do not want to use PCA + use_pca = True + if use_pca: + # Create PCA object, trained on input_vectors + print >>sys.stderr, "Running PCA." + pca = PCA(input_vectors_train, reduce=True) + # Creates a Composed object that first generates an input vector + # and then performs a PCA analysis on it. + i = Compose(i, pca) + + def revnorm(vec): + return [ (1-x) * 16.5 - 3.0 for x in vec ] + + def rand_vect(k): + return list(2.0*numpy.random.random(k)-1.0) + + print "running" + # Create list of output vectors using weighted kNN algorithm approximating output_vector + output_vectors= [ revnorm( [-pca(iv)[0]] ) for iv in input_vectors_test ] + desired_vectors= [ revnorm(player_vector[rank]) for rank in test_pl ] + + if True: + for f, out, des in zip(test_files, output_vectors, desired_vectors): + assert len(out) == 1 + assert len(des) == 1 + print f, "%2.3f ; %2.3f"%(out[0], des[0]) + + print + diff = [ abs(x[0] - y[0]) for x,y in zip(output_vectors,desired_vectors) ] + zips = zip(diff, test_files) + zips.sort() + for diff,a in zips: + print a, " %2.3f"%(diff,) + + errs =[] + for o,d in zip(output_vectors, desired_vectors): + err = 0.0 + for x,y in zip(o,d): + e = (1.0*x-1.0*y)**2 + err += e + errs += [err] + + mean = numpy.array(errs).mean() + print "Mean square err: " + "%2.3f ( = sd %2.3f) "%(mean, sqrt(mean)) diff --git a/pdb-gtl/stats.py b/pdb-gtl/stats.py new file mode 100755 index 0000000..b51ccbe --- /dev/null +++ b/pdb-gtl/stats.py @@ -0,0 +1,33 @@ +#!/usr/bin/python + +import os, random, shutil, numpy + +merg= 'rawpat_files_merged' +test=merg+'_test' + +def list_dir(raw): + import os, random, shutil + ranks = os.listdir(raw) + for rank in ranks: + plays = os.listdir(raw + '/'+ rank) + for play in plays: + yield raw + '/' + rank + '/' + play + +def dir_stats(dr): + num = 0 + sizes = [] + + for f in list_dir(dr): + num += 1 + size = os.stat(f).st_size + sizes += [size] + + return (num, numpy.array(sizes).mean(), sum(sizes)) + +num, mean, size = dir_stats(merg) +numt, meant, sizet = dir_stats(test) + +print "Test set X Train Set" +print "Mean filesize:", meant, " X ", mean +print "Number of files:", numt, " X ", num +print "Total size:", sizet, " X ", size diff --git a/style_consensus/style_consensus.tex b/style_consensus/style_consensus.tex index f54c838..3a04226 100644 --- a/style_consensus/style_consensus.tex +++ b/style_consensus/style_consensus.tex @@ -46,8 +46,8 @@ Judged by Strong Players} part of this project, we conducted (partly manual and partly online) a questionnaire, where we ask experts (strong amateur or professional players) to judge styles of several professional Go players. The experts - were asked to judge each professional on four scales traditional - Go knowledge. In this report, we publish these results, along with + were asked to judge each professional on four scales reflecting the + traditional Go knowledge. In this report, we publish these results, along with definitions of styles and acknowledgement of the interviewees. The purpose is to make the data available to the general public. \end{abstract} @@ -99,6 +99,8 @@ reflects the ``trend'' given by names of the boundaries --- in the questionnaire presented so far, the number 1 was unfortunately assigned to Classic style of game and 10 to Novel style of game. +The experts' answers were collected 2010 to 2013. + \section{Results} Are given in the Table~\ref{questionare} along with standard deviation of individual diff --git a/tex/POSUDEK b/tex/POSUDEK dissimilarity index 96% index 5f559ff..a37071a 100644 --- a/tex/POSUDEK +++ b/tex/POSUDEK @@ -1,39 +1,55 @@ -Reviewer 1: ------------------------ -Small comments: -p.6 line 18: rephrase - the dimensions do not approximate a normal distribution - the distributions of values in each dimension do. -P:FIXED - -Alg 4 Train set -> Training -P:FIXED - -What does RbyCc stand for? Better variable name? -Fix the set operation Rby <- Rby u {R} -P: Nevim... :-) - -Fix formatting for sentences ending before [23] and [24] -P: FIXED - -Page 7 k is used for both k-NN and number of games. Use a different variable for number of games. Then use that variable instead of ~ in Table I. -P: FIXED, prejmenovano na G :-) - -line 56: the value 5 for number of games is not shown in the table. Make it consistent. -P: FIXED 5 => 9 - -Can you give any interpretation of the first PCA vector which predicts strength? Does it correspond to any Go concepts that can be expressed in human-understandable terms? That would be really interesting. -P: Hmm, sila odpovida sile.. Asi chce slyset neco jako: "Sila odpovida tomu ze hrajes tehle 5 patternu." ale to je blbost. dobri hraci hraji tyhle patterny protoze jsou dobri, nejsou dobri diky nim. -Otazka: ma cenu na to odpovidat / rozvest tuhle myslenku (pokud jsme to jeste neudelali)? - -The tweaking of data by omitting games and players leaves a bad taste. Is it crucial for obtaining results or can the experiment be done on the full data set? - - -Reviewer: 2 ------------------------------ -Comments to the Author -I still have the feeling that the results will not be of some use to the reader, neither directly, and nor for the long term. The paper shows a research in progress more than clearly established results. - -For estimating the player's strength, Bayesian estimates of the "ELO" rank are more convincing. I see no proof in the paper that the proposed approach can outperform this. - -For the game style, simple statistics on the number of captures, the proportion of wins/losses by resign (instead of by points), the variability of the performance (i.e. frequency of a priori unlikely results, e.g. players with level 10kyu winning with non-negligible probability against 7kyu players but also loosing with non-negligible probability against 13kyu players), average number of moves before the first capture, might be more informative. At least, there's no proof of the efficiency of the advocated approach when compared to simple tools like that. - -I am not in favor of the acceptance of this paper. +Reviewer 1: +----------------------- +> Small comments: +> ... + +We have fixed the typos gratefully provided by this part. + +> What does RbyCc stand for? Better variable name? + +The RbyCc holds all the R's that fall into the c class. We have improved the clarity of the Naive Bayes Algoritm entry. + +> Can you give any interpretation of the first PCA vector which predicts strength? +> Does it correspond to any Go concepts that can be expressed in human-understandable terms? That would be really interesting. + +Yes, this could be interesting. Unfortunately, we have not done this precisely, but I must say, that the interpretation of the components is a +nontrivial task, which has some limitations, as we have suggested in Section V/B. We want to look into the analysis of the PCA strength component +patterns in the future. + +> The tweaking of data by omitting games and players leaves a bad taste. Is it crucial for obtaining results or can the experiment +> be done on the full data set? + +No, it certainly is not necessary, but it is feasible, since we deal with a fairly small set of players, any inconsistency results in errors. +The results on the full dataset would be burdened with larger error because of presence of the outliers and would not therefore be so clear. +Another important thing to notice is that this proccess is done only once and is needed only for training the classifiers, not for using them. + + +Reviewer: 2 +----------------------------- +> Comments to the Author +> I still have the feeling that the results will not be of some use to the reader, neither directly, and nor for the long term. +> The paper shows a research in progress more than clearly established results. + +Partly, our work certainly can be viewed as a work in progress, but this results from the fact, that our work is completely novel and we have +therefore only scratched surface of what can be achieved. We are however convinced, that already the results we have are very interesting. + +> For estimating the player's strength, Bayesian estimates of the "ELO" rank are more convincing. I see no proof in the paper +> that the proposed approach can outperform this. + +As mentioned in our paper, in comparison with any win/loss statistics based strength estimation (be it ELO or standard rating) our method +should work better for small number of game samples. Additionaly, to estimate the rank based on win/loss information, it is needed to know the ELO +rating of some of the players in the game samples, our method does not need any additional information apart from the game record. +Even if we had this additional information, we could still use the pattern analysis to make the results more precise - by joining both approaches +in a hybrid strength classifier. + +> For the game style, simple statistics on the number of captures, the proportion of wins/losses by resign (instead of by points), +> the variability of the performance (i.e. frequency of a priori unlikely results, e.g. players with level 10kyu winning with non-negligible +> probability against 7kyu players but also loosing with non-negligible probability against 13kyu players), average number of moves before the +> first capture, might be more informative. At least, there's no proof of the efficiency of the advocated approach when compared to simple +> tools like that. + +This remark is very similar to the previous one. Our approach can provide the users with the information that might be closer to the nature of a +problem in a game style than a simple statistic. I am convinced, that the player would be more grateful for a hint about bad shapes he plays, +than for average number of moves before first capture. As before, if the statistics worked, both approaches might easily be joined together +with better performance than each of them alone. Looking into new ways to analyse Go games can only be beneficial. + diff --git a/tex/POSUDEK2 b/tex/POSUDEK2 index 51c1da7..94fa85c 100644 --- a/tex/POSUDEK2 +++ b/tex/POSUDEK2 @@ -30,7 +30,3 @@ Although our work might be viewed through the skill and competency theory, we do We believe that we have made the possible implications and uses for playing Go clear in Section VI (Proposed Applications), and Section VII (Future Work). We are currently preparing online application based on the research, which will help to pinpoint patterns to avoid, games to replay and possible study directions. - -TODO reference dodat rok, GoDiscThread, KGS Analytics - - diff --git a/tex/patcountdist.eps b/tex/patcountdist.eps new file mode 100644 index 0000000..3fb77da --- /dev/null +++ b/tex/patcountdist.eps @@ -0,0 +1,1199 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: patcountdist.eps +%%Creator: gnuplot 4.4 patchlevel 3 +%%CreationDate: Tue Mar 27 19:25:11 2012 +%%DocumentFonts: (atend) +%%BoundingBox: 50 50 302 230 +%%EndComments +%%BeginProlog +/gnudict 256 dict def +gnudict begin +% +% The following true/false flags may be edited by hand if desired. +% The unit line width and grayscale image gamma correction may also be changed. +% +/Color false def +/Blacktext false def +/Solid false def +/Dashlength 1 def +/Landscape false def +/Level1 false def +/Rounded false def +/ClipToBoundingBox false def +/TransparentPatterns false def +/gnulinewidth 5.000 def +/userlinewidth gnulinewidth def +/Gamma 1.0 def +% +/vshift -60 def +/dl1 { + 10.0 Dashlength mul mul + Rounded { currentlinewidth 0.75 mul sub dup 0 le { pop 0.01 } if } if +} def +/dl2 { + 10.0 Dashlength mul mul + Rounded { currentlinewidth 0.75 mul add } if +} def +/hpt_ 31.5 def +/vpt_ 31.5 def +/hpt hpt_ def +/vpt vpt_ def +Level1 {} { +/SDict 10 dict def +systemdict /pdfmark known not { + userdict /pdfmark systemdict /cleartomark get put +} if +SDict begin [ + /Title (patcountdist.eps) + /Subject (gnuplot plot) + /Creator (gnuplot 4.4 patchlevel 3) + /Author (jm) +% /Producer (gnuplot) +% /Keywords () + /CreationDate (Tue Mar 27 19:25:11 2012) + /DOCINFO pdfmark +end +} ifelse +/doclip { + ClipToBoundingBox { + newpath 50 50 moveto 302 50 lineto 302 230 lineto 50 230 lineto closepath + clip + } if +} def +% +% Gnuplot Prolog Version 4.4 (August 2010) +% +%/SuppressPDFMark true def +% +/M {moveto} bind def +/L {lineto} bind def +/R {rmoveto} bind def +/V {rlineto} bind def +/N {newpath moveto} bind def +/Z {closepath} bind def +/C {setrgbcolor} bind def +/f {rlineto fill} bind def +/g {setgray} bind def +/Gshow {show} def % May be redefined later in the file to support UTF-8 +/vpt2 vpt 2 mul def +/hpt2 hpt 2 mul def +/Lshow {currentpoint stroke M 0 vshift R + Blacktext {gsave 0 setgray show grestore} {show} ifelse} def +/Rshow {currentpoint stroke M dup stringwidth pop neg vshift R + Blacktext {gsave 0 setgray show grestore} {show} ifelse} def +/Cshow {currentpoint stroke M dup stringwidth pop -2 div vshift R + Blacktext {gsave 0 setgray show grestore} {show} ifelse} def +/UP {dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def + /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def} def +/DL {Color {setrgbcolor Solid {pop []} if 0 setdash} + {pop pop pop 0 setgray Solid {pop []} if 0 setdash} ifelse} def +/BL {stroke userlinewidth 2 mul setlinewidth + Rounded {1 setlinejoin 1 setlinecap} if} def +/AL {stroke userlinewidth 2 div setlinewidth + Rounded {1 setlinejoin 1 setlinecap} if} def +/UL {dup gnulinewidth mul /userlinewidth exch def + dup 1 lt {pop 1} if 10 mul /udl exch def} def +/PL {stroke userlinewidth setlinewidth + Rounded {1 setlinejoin 1 setlinecap} if} def +3.8 setmiterlimit +% Default Line colors +/LCw {1 1 1} def +/LCb {0 0 0} def +/LCa {0 0 0} def +/LC0 {1 0 0} def +/LC1 {0 1 0} def +/LC2 {0 0 1} def +/LC3 {1 0 1} def +/LC4 {0 1 1} def +/LC5 {1 1 0} def +/LC6 {0 0 0} def +/LC7 {1 0.3 0} def +/LC8 {0.5 0.5 0.5} def +% Default Line Types +/LTw {PL [] 1 setgray} def +/LTb {BL [] LCb DL} def +/LTa {AL [1 udl mul 2 udl mul] 0 setdash LCa setrgbcolor} def +/LT0 {PL [] LC0 DL} def +/LT1 {PL [4 dl1 2 dl2] LC1 DL} def +/LT2 {PL [2 dl1 3 dl2] LC2 DL} def +/LT3 {PL [1 dl1 1.5 dl2] LC3 DL} def +/LT4 {PL [6 dl1 2 dl2 1 dl1 2 dl2] LC4 DL} def +/LT5 {PL [3 dl1 3 dl2 1 dl1 3 dl2] LC5 DL} def +/LT6 {PL [2 dl1 2 dl2 2 dl1 6 dl2] LC6 DL} def +/LT7 {PL [1 dl1 2 dl2 6 dl1 2 dl2 1 dl1 2 dl2] LC7 DL} def +/LT8 {PL [2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 2 dl2 2 dl1 4 dl2] LC8 DL} def +/Pnt {stroke [] 0 setdash gsave 1 setlinecap M 0 0 V stroke grestore} def +/Dia {stroke [] 0 setdash 2 copy vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V closepath stroke + Pnt} def +/Pls {stroke [] 0 setdash vpt sub M 0 vpt2 V + currentpoint stroke M + hpt neg vpt neg R hpt2 0 V stroke + } def +/Box {stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V closepath stroke + Pnt} def +/Crs {stroke [] 0 setdash exch hpt sub exch vpt add M + hpt2 vpt2 neg V currentpoint stroke M + hpt2 neg 0 R hpt2 vpt2 V stroke} def +/TriU {stroke [] 0 setdash 2 copy vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V closepath stroke + Pnt} def +/Star {2 copy Pls Crs} def +/BoxF {stroke [] 0 setdash exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V closepath fill} def +/TriUF {stroke [] 0 setdash vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V closepath fill} def +/TriD {stroke [] 0 setdash 2 copy vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V closepath stroke + Pnt} def +/TriDF {stroke [] 0 setdash vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V closepath fill} def +/DiaF {stroke [] 0 setdash vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V closepath fill} def +/Pent {stroke [] 0 setdash 2 copy gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + closepath stroke grestore Pnt} def +/PentF {stroke [] 0 setdash gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + closepath fill grestore} def +/Circle {stroke [] 0 setdash 2 copy + hpt 0 360 arc stroke Pnt} def +/CircleF {stroke [] 0 setdash hpt 0 360 arc fill} def +/C0 {BL [] 0 setdash 2 copy moveto vpt 90 450 arc} bind def +/C1 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 90 arc closepath fill + vpt 0 360 arc closepath} bind def +/C2 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 90 180 arc closepath fill + vpt 0 360 arc closepath} bind def +/C3 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 180 arc closepath fill + vpt 0 360 arc closepath} bind def +/C4 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 180 270 arc closepath fill + vpt 0 360 arc closepath} bind def +/C5 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 90 arc + 2 copy moveto + 2 copy vpt 180 270 arc closepath fill + vpt 0 360 arc} bind def +/C6 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 90 270 arc closepath fill + vpt 0 360 arc closepath} bind def +/C7 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 270 arc closepath fill + vpt 0 360 arc closepath} bind def +/C8 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 270 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C9 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 270 450 arc closepath fill + vpt 0 360 arc closepath} bind def +/C10 {BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill + 2 copy moveto + 2 copy vpt 90 180 arc closepath fill + vpt 0 360 arc closepath} bind def +/C11 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 180 arc closepath fill + 2 copy moveto + 2 copy vpt 270 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C12 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 180 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C13 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 0 90 arc closepath fill + 2 copy moveto + 2 copy vpt 180 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/C14 {BL [] 0 setdash 2 copy moveto + 2 copy vpt 90 360 arc closepath fill + vpt 0 360 arc} bind def +/C15 {BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill + vpt 0 360 arc closepath} bind def +/Rec {newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto + neg 0 rlineto closepath} bind def +/Square {dup Rec} bind def +/Bsquare {vpt sub exch vpt sub exch vpt2 Square} bind def +/S0 {BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare} bind def +/S1 {BL [] 0 setdash 2 copy vpt Square fill Bsquare} bind def +/S2 {BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def +/S3 {BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare} bind def +/S4 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def +/S5 {BL [] 0 setdash 2 copy 2 copy vpt Square fill + exch vpt sub exch vpt sub vpt Square fill Bsquare} bind def +/S6 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare} bind def +/S7 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill + 2 copy vpt Square fill Bsquare} bind def +/S8 {BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare} bind def +/S9 {BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare} bind def +/S10 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill + Bsquare} bind def +/S11 {BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill + Bsquare} bind def +/S12 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare} bind def +/S13 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill + 2 copy vpt Square fill Bsquare} bind def +/S14 {BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill + 2 copy exch vpt sub exch vpt Square fill Bsquare} bind def +/S15 {BL [] 0 setdash 2 copy Bsquare fill Bsquare} bind def +/D0 {gsave translate 45 rotate 0 0 S0 stroke grestore} bind def +/D1 {gsave translate 45 rotate 0 0 S1 stroke grestore} bind def +/D2 {gsave translate 45 rotate 0 0 S2 stroke grestore} bind def +/D3 {gsave translate 45 rotate 0 0 S3 stroke grestore} bind def +/D4 {gsave translate 45 rotate 0 0 S4 stroke grestore} bind def +/D5 {gsave translate 45 rotate 0 0 S5 stroke grestore} bind def +/D6 {gsave translate 45 rotate 0 0 S6 stroke grestore} bind def +/D7 {gsave translate 45 rotate 0 0 S7 stroke grestore} bind def +/D8 {gsave translate 45 rotate 0 0 S8 stroke grestore} bind def +/D9 {gsave translate 45 rotate 0 0 S9 stroke grestore} bind def +/D10 {gsave translate 45 rotate 0 0 S10 stroke grestore} bind def +/D11 {gsave translate 45 rotate 0 0 S11 stroke grestore} bind def +/D12 {gsave translate 45 rotate 0 0 S12 stroke grestore} bind def +/D13 {gsave translate 45 rotate 0 0 S13 stroke grestore} bind def +/D14 {gsave translate 45 rotate 0 0 S14 stroke grestore} bind def +/D15 {gsave translate 45 rotate 0 0 S15 stroke grestore} bind def +/DiaE {stroke [] 0 setdash vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V closepath stroke} def +/BoxE {stroke [] 0 setdash exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V closepath stroke} def +/TriUE {stroke [] 0 setdash vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V closepath stroke} def +/TriDE {stroke [] 0 setdash vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V closepath stroke} def +/PentE {stroke [] 0 setdash gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + closepath stroke grestore} def +/CircE {stroke [] 0 setdash + hpt 0 360 arc stroke} def +/Opaque {gsave closepath 1 setgray fill grestore 0 setgray closepath} def +/DiaW {stroke [] 0 setdash vpt add M + hpt neg vpt neg V hpt vpt neg V + hpt vpt V hpt neg vpt V Opaque stroke} def +/BoxW {stroke [] 0 setdash exch hpt sub exch vpt add M + 0 vpt2 neg V hpt2 0 V 0 vpt2 V + hpt2 neg 0 V Opaque stroke} def +/TriUW {stroke [] 0 setdash vpt 1.12 mul add M + hpt neg vpt -1.62 mul V + hpt 2 mul 0 V + hpt neg vpt 1.62 mul V Opaque stroke} def +/TriDW {stroke [] 0 setdash vpt 1.12 mul sub M + hpt neg vpt 1.62 mul V + hpt 2 mul 0 V + hpt neg vpt -1.62 mul V Opaque stroke} def +/PentW {stroke [] 0 setdash gsave + translate 0 hpt M 4 {72 rotate 0 hpt L} repeat + Opaque stroke grestore} def +/CircW {stroke [] 0 setdash + hpt 0 360 arc Opaque stroke} def +/BoxFill {gsave Rec 1 setgray fill grestore} def +/Density { + /Fillden exch def + currentrgbcolor + /ColB exch def /ColG exch def /ColR exch def + /ColR ColR Fillden mul Fillden sub 1 add def + /ColG ColG Fillden mul Fillden sub 1 add def + /ColB ColB Fillden mul Fillden sub 1 add def + ColR ColG ColB setrgbcolor} def +/BoxColFill {gsave Rec PolyFill} def +/PolyFill {gsave Density fill grestore grestore} def +/h {rlineto rlineto rlineto gsave closepath fill grestore} bind def +% +% PostScript Level 1 Pattern Fill routine for rectangles +% Usage: x y w h s a XX PatternFill +% x,y = lower left corner of box to be filled +% w,h = width and height of box +% a = angle in degrees between lines and x-axis +% XX = 0/1 for no/yes cross-hatch +% +/PatternFill {gsave /PFa [ 9 2 roll ] def + PFa 0 get PFa 2 get 2 div add PFa 1 get PFa 3 get 2 div add translate + PFa 2 get -2 div PFa 3 get -2 div PFa 2 get PFa 3 get Rec + gsave 1 setgray fill grestore clip + currentlinewidth 0.5 mul setlinewidth + /PFs PFa 2 get dup mul PFa 3 get dup mul add sqrt def + 0 0 M PFa 5 get rotate PFs -2 div dup translate + 0 1 PFs PFa 4 get div 1 add floor cvi + {PFa 4 get mul 0 M 0 PFs V} for + 0 PFa 6 get ne { + 0 1 PFs PFa 4 get div 1 add floor cvi + {PFa 4 get mul 0 2 1 roll M PFs 0 V} for + } if + stroke grestore} def +% +/languagelevel where + {pop languagelevel} {1} ifelse + 2 lt + {/InterpretLevel1 true def} + {/InterpretLevel1 Level1 def} + ifelse +% +% PostScript level 2 pattern fill definitions +% +/Level2PatternFill { +/Tile8x8 {/PaintType 2 /PatternType 1 /TilingType 1 /BBox [0 0 8 8] /XStep 8 /YStep 8} + bind def +/KeepColor {currentrgbcolor [/Pattern /DeviceRGB] setcolorspace} bind def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke} +>> matrix makepattern +/Pat1 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 0 M 8 8 L 0 8 M 8 0 L stroke + 0 4 M 4 8 L 8 4 L 4 0 L 0 4 L stroke} +>> matrix makepattern +/Pat2 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 0 M 0 8 L + 8 8 L 8 0 L 0 0 L fill} +>> matrix makepattern +/Pat3 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -4 8 M 8 -4 L + 0 12 M 12 0 L stroke} +>> matrix makepattern +/Pat4 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -4 0 M 8 12 L + 0 -4 M 12 8 L stroke} +>> matrix makepattern +/Pat5 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -2 8 M 4 -4 L + 0 12 M 8 -4 L 4 12 M 10 0 L stroke} +>> matrix makepattern +/Pat6 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop -2 0 M 4 12 L + 0 -4 M 8 12 L 4 -4 M 10 8 L stroke} +>> matrix makepattern +/Pat7 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 8 -2 M -4 4 L + 12 0 M -4 8 L 12 4 M 0 10 L stroke} +>> matrix makepattern +/Pat8 exch def +<< Tile8x8 + /PaintProc {0.5 setlinewidth pop 0 -2 M 12 4 L + -4 0 M 12 8 L -4 4 M 8 10 L stroke} +>> matrix makepattern +/Pat9 exch def +/Pattern1 {PatternBgnd KeepColor Pat1 setpattern} bind def +/Pattern2 {PatternBgnd KeepColor Pat2 setpattern} bind def +/Pattern3 {PatternBgnd KeepColor Pat3 setpattern} bind def +/Pattern4 {PatternBgnd KeepColor Landscape {Pat5} {Pat4} ifelse setpattern} bind def +/Pattern5 {PatternBgnd KeepColor Landscape {Pat4} {Pat5} ifelse setpattern} bind def +/Pattern6 {PatternBgnd KeepColor Landscape {Pat9} {Pat6} ifelse setpattern} bind def +/Pattern7 {PatternBgnd KeepColor Landscape {Pat8} {Pat7} ifelse setpattern} bind def +} def +% +% +%End of PostScript Level 2 code +% +/PatternBgnd { + TransparentPatterns {} {gsave 1 setgray fill grestore} ifelse +} def +% +% Substitute for Level 2 pattern fill codes with +% grayscale if Level 2 support is not selected. +% +/Level1PatternFill { +/Pattern1 {0.250 Density} bind def +/Pattern2 {0.500 Density} bind def +/Pattern3 {0.750 Density} bind def +/Pattern4 {0.125 Density} bind def +/Pattern5 {0.375 Density} bind def +/Pattern6 {0.625 Density} bind def +/Pattern7 {0.875 Density} bind def +} def +% +% Now test for support of Level 2 code +% +Level1 {Level1PatternFill} {Level2PatternFill} ifelse +% +/Symbol-Oblique /Symbol findfont [1 0 .167 1 0 0] makefont +dup length dict begin {1 index /FID eq {pop pop} {def} ifelse} forall +currentdict end definefont pop +/MFshow { + { dup 5 get 3 ge + { 5 get 3 eq {gsave} {grestore} ifelse } + {dup dup 0 get findfont exch 1 get scalefont setfont + [ currentpoint ] exch dup 2 get 0 exch R dup 5 get 2 ne {dup dup 6 + get exch 4 get {Gshow} {stringwidth pop 0 R} ifelse }if dup 5 get 0 eq + {dup 3 get {2 get neg 0 exch R pop} {pop aload pop M} ifelse} {dup 5 + get 1 eq {dup 2 get exch dup 3 get exch 6 get stringwidth pop -2 div + dup 0 R} {dup 6 get stringwidth pop -2 div 0 R 6 get + show 2 index {aload pop M neg 3 -1 roll neg R pop pop} {pop pop pop + pop aload pop M} ifelse }ifelse }ifelse } + ifelse } + forall} def +/Gswidth {dup type /stringtype eq {stringwidth} {pop (n) stringwidth} ifelse} def +/MFwidth {0 exch { dup 5 get 3 ge { 5 get 3 eq { 0 } { pop } ifelse } + {dup 3 get{dup dup 0 get findfont exch 1 get scalefont setfont + 6 get Gswidth pop add} {pop} ifelse} ifelse} forall} def +/MLshow { currentpoint stroke M + 0 exch R + Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def +/MRshow { currentpoint stroke M + exch dup MFwidth neg 3 -1 roll R + Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def +/MCshow { currentpoint stroke M + exch dup MFwidth -2 div 3 -1 roll R + Blacktext {gsave 0 setgray MFshow grestore} {MFshow} ifelse } bind def +/XYsave { [( ) 1 2 true false 3 ()] } bind def +/XYrestore { [( ) 1 2 true false 4 ()] } bind def +end +%%EndProlog +gnudict begin +gsave +doclip +50 50 translate +0.050 0.050 scale +0 setgray +newpath +(Helvetica) findfont 180 scalefont setfont +1.000 UL +LTb +756 576 M +63 0 V +3896 0 R +-63 0 V +stroke +648 576 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 100)] +] -60.0 MRshow +1.000 UL +LTb +756 858 M +31 0 V +3928 0 R +-31 0 V +756 1022 M +31 0 V +3928 0 R +-31 0 V +756 1139 M +31 0 V +3928 0 R +-31 0 V +756 1230 M +31 0 V +3928 0 R +-31 0 V +756 1304 M +31 0 V +3928 0 R +-31 0 V +756 1367 M +31 0 V +3928 0 R +-31 0 V +756 1421 M +31 0 V +3928 0 R +-31 0 V +756 1469 M +31 0 V +3928 0 R +-31 0 V +756 1512 M +63 0 V +3896 0 R +-63 0 V +stroke +648 1512 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 1000)] +] -60.0 MRshow +1.000 UL +LTb +756 1793 M +31 0 V +3928 0 R +-31 0 V +756 1958 M +31 0 V +3928 0 R +-31 0 V +756 2075 M +31 0 V +3928 0 R +-31 0 V +756 2166 M +31 0 V +3928 0 R +-31 0 V +756 2240 M +31 0 V +3928 0 R +-31 0 V +756 2302 M +31 0 V +3928 0 R +-31 0 V +756 2357 M +31 0 V +3928 0 R +-31 0 V +756 2405 M +31 0 V +3928 0 R +-31 0 V +756 2447 M +63 0 V +3896 0 R +-63 0 V +stroke +648 2447 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 10000)] +] -60.0 MRshow +1.000 UL +LTb +756 2729 M +31 0 V +3928 0 R +-31 0 V +756 2894 M +31 0 V +3928 0 R +-31 0 V +756 3011 M +31 0 V +3928 0 R +-31 0 V +756 3101 M +31 0 V +3928 0 R +-31 0 V +756 3175 M +31 0 V +3928 0 R +-31 0 V +756 3238 M +31 0 V +3928 0 R +-31 0 V +756 3292 M +31 0 V +3928 0 R +-31 0 V +756 3340 M +31 0 V +3928 0 R +-31 0 V +756 3383 M +63 0 V +3896 0 R +-63 0 V +stroke +648 3383 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 100000)] +] -60.0 MRshow +1.000 UL +LTb +756 576 M +0 63 V +0 2744 R +0 -63 V +stroke +756 396 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 0)] +] -60.0 MCshow +1.000 UL +LTb +1746 576 M +0 63 V +0 2744 R +0 -63 V +stroke +1746 396 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 125)] +] -60.0 MCshow +1.000 UL +LTb +2736 576 M +0 63 V +0 2744 R +0 -63 V +stroke +2736 396 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 250)] +] -60.0 MCshow +1.000 UL +LTb +3725 576 M +0 63 V +0 2744 R +0 -63 V +stroke +3725 396 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 375)] +] -60.0 MCshow +1.000 UL +LTb +4715 576 M +0 63 V +0 2744 R +0 -63 V +stroke +4715 396 M +[ [(Helvetica) 180.0 0.0 true true 0 ( 500)] +] -60.0 MCshow +1.000 UL +LTb +1.000 UL +LTb +756 3383 N +756 576 L +3959 0 V +0 2807 V +-3959 0 V +Z stroke +LCb setrgbcolor +2735 126 M +[ [(Helvetica) 180.0 0.0 true true 0 (pattern id)] +] -60.0 MCshow +LTb +1.000 UP +1.000 UL +LTb +% Begin plot #1 +1.000 UP +1.000 UL +LT0 +756 2708 Crs +764 2687 Crs +772 2632 Crs +780 2357 Crs +788 2168 Crs +796 2117 Crs +804 2085 Crs +811 1948 Crs +819 1900 Crs +827 1865 Crs +835 1774 Crs +843 1774 Crs +851 1700 Crs +859 1698 Crs +867 1697 Crs +875 1681 Crs +883 1654 Crs +891 1652 Crs +899 1645 Crs +906 1638 Crs +914 1605 Crs +922 1597 Crs +930 1565 Crs +938 1562 Crs +946 1553 Crs +954 1544 Crs +962 1523 Crs +970 1508 Crs +978 1503 Crs +986 1502 Crs +994 1490 Crs +1001 1488 Crs +1009 1469 Crs +1017 1455 Crs +1025 1449 Crs +1033 1447 Crs +1041 1439 Crs +1049 1432 Crs +1057 1426 Crs +1065 1410 Crs +1073 1407 Crs +1081 1394 Crs +1089 1391 Crs +1096 1388 Crs +1104 1379 Crs +1112 1354 Crs +1120 1346 Crs +1128 1337 Crs +1136 1335 Crs +1144 1333 Crs +1152 1328 Crs +1160 1325 Crs +1168 1311 Crs +1176 1309 Crs +1184 1308 Crs +1191 1303 Crs +1199 1303 Crs +1207 1303 Crs +1215 1299 Crs +1223 1298 Crs +1231 1296 Crs +1239 1285 Crs +1247 1280 Crs +1255 1280 Crs +1263 1272 Crs +1271 1271 Crs +1279 1261 Crs +1287 1252 Crs +1294 1248 Crs +1302 1248 Crs +1310 1240 Crs +1318 1240 Crs +1326 1239 Crs +1334 1237 Crs +1342 1236 Crs +1350 1236 Crs +1358 1235 Crs +1366 1233 Crs +1374 1232 Crs +1382 1229 Crs +1389 1229 Crs +1397 1219 Crs +1405 1218 Crs +1413 1217 Crs +1421 1209 Crs +1429 1207 Crs +1437 1207 Crs +1445 1203 Crs +1453 1203 Crs +1461 1202 Crs +1469 1201 Crs +1477 1198 Crs +1484 1194 Crs +1492 1193 Crs +1500 1191 Crs +1508 1185 Crs +1516 1185 Crs +1524 1181 Crs +1532 1179 Crs +1540 1176 Crs +1548 1175 Crs +1556 1172 Crs +1564 1162 Crs +1572 1161 Crs +1579 1161 Crs +1587 1159 Crs +1595 1159 Crs +1603 1153 Crs +1611 1153 Crs +1619 1149 Crs +1627 1149 Crs +1635 1147 Crs +1643 1146 Crs +1651 1145 Crs +1659 1143 Crs +1667 1138 Crs +1674 1136 Crs +1682 1135 Crs +1690 1134 Crs +1698 1133 Crs +1706 1132 Crs +1714 1131 Crs +1722 1130 Crs +1730 1130 Crs +1738 1130 Crs +1746 1128 Crs +1754 1127 Crs +1762 1126 Crs +1770 1122 Crs +1777 1122 Crs +1785 1121 Crs +1793 1117 Crs +1801 1114 Crs +1809 1111 Crs +1817 1111 Crs +1825 1110 Crs +1833 1109 Crs +1841 1108 Crs +1849 1105 Crs +1857 1105 Crs +1865 1104 Crs +1872 1103 Crs +1880 1100 Crs +1888 1099 Crs +1896 1098 Crs +1904 1097 Crs +1912 1097 Crs +1920 1094 Crs +1928 1093 Crs +1936 1092 Crs +1944 1091 Crs +1952 1090 Crs +1960 1090 Crs +1967 1090 Crs +1975 1089 Crs +1983 1089 Crs +1991 1085 Crs +1999 1085 Crs +2007 1083 Crs +2015 1082 Crs +2023 1080 Crs +2031 1079 Crs +2039 1076 Crs +2047 1074 Crs +2055 1072 Crs +2062 1070 Crs +2070 1068 Crs +2078 1068 Crs +2086 1068 Crs +2094 1065 Crs +2102 1062 Crs +2110 1062 Crs +2118 1060 Crs +2126 1060 Crs +2134 1056 Crs +2142 1054 Crs +2150 1050 Crs +2157 1046 Crs +2165 1045 Crs +2173 1045 Crs +2181 1044 Crs +2189 1041 Crs +2197 1040 Crs +2205 1040 Crs +2213 1037 Crs +2221 1036 Crs +2229 1034 Crs +2237 1034 Crs +2245 1032 Crs +2253 1030 Crs +2260 1029 Crs +2268 1029 Crs +2276 1029 Crs +2284 1028 Crs +2292 1026 Crs +2300 1024 Crs +2308 1024 Crs +2316 1022 Crs +2324 1021 Crs +2332 1020 Crs +2340 1020 Crs +2348 1018 Crs +2355 1018 Crs +2363 1017 Crs +2371 1016 Crs +2379 1014 Crs +2387 1013 Crs +2395 1013 Crs +2403 1011 Crs +2411 1011 Crs +2419 1011 Crs +2427 1010 Crs +2435 1010 Crs +2443 1010 Crs +2450 1009 Crs +2458 1007 Crs +2466 1007 Crs +2474 1006 Crs +2482 1002 Crs +2490 1002 Crs +2498 1002 Crs +2506 1002 Crs +2514 1002 Crs +2522 999 Crs +2530 999 Crs +2538 999 Crs +2545 999 Crs +2553 999 Crs +2561 997 Crs +2569 997 Crs +2577 996 Crs +2585 994 Crs +2593 994 Crs +2601 993 Crs +2609 993 Crs +2617 993 Crs +2625 991 Crs +2633 989 Crs +2640 989 Crs +2648 989 Crs +2656 987 Crs +2664 987 Crs +2672 987 Crs +2680 986 Crs +2688 986 Crs +2696 984 Crs +2704 984 Crs +2712 984 Crs +2720 984 Crs +2728 984 Crs +2736 983 Crs +2743 983 Crs +2751 981 Crs +2759 981 Crs +2767 981 Crs +2775 980 Crs +2783 978 Crs +2791 978 Crs +2799 977 Crs +2807 977 Crs +2815 977 Crs +2823 977 Crs +2831 975 Crs +2838 972 Crs +2846 972 Crs +2854 970 Crs +2862 970 Crs +2870 969 Crs +2878 969 Crs +2886 969 Crs +2894 967 Crs +2902 967 Crs +2910 967 Crs +2918 967 Crs +2926 966 Crs +2933 966 Crs +2941 966 Crs +2949 966 Crs +2957 963 Crs +2965 963 Crs +2973 963 Crs +2981 963 Crs +2989 961 Crs +2997 961 Crs +3005 961 Crs +3013 960 Crs +3021 960 Crs +3028 960 Crs +3036 960 Crs +3044 958 Crs +3052 956 Crs +3060 956 Crs +3068 956 Crs +3076 956 Crs +3084 955 Crs +3092 955 Crs +3100 955 Crs +3108 955 Crs +3116 955 Crs +3123 955 Crs +3131 955 Crs +3139 955 Crs +3147 953 Crs +3155 953 Crs +3163 953 Crs +3171 952 Crs +3179 952 Crs +3187 952 Crs +3195 952 Crs +3203 950 Crs +3211 948 Crs +3218 948 Crs +3226 948 Crs +3234 948 Crs +3242 947 Crs +3250 947 Crs +3258 947 Crs +3266 945 Crs +3274 945 Crs +3282 945 Crs +3290 945 Crs +3298 943 Crs +3306 943 Crs +3314 943 Crs +3321 943 Crs +3329 943 Crs +3337 942 Crs +3345 942 Crs +3353 942 Crs +3361 942 Crs +3369 942 Crs +3377 942 Crs +3385 940 Crs +3393 940 Crs +3401 940 Crs +3409 940 Crs +3416 938 Crs +3424 937 Crs +3432 937 Crs +3440 937 Crs +3448 937 Crs +3456 935 Crs +3464 935 Crs +3472 935 Crs +3480 933 Crs +3488 933 Crs +3496 933 Crs +3504 933 Crs +3511 932 Crs +3519 932 Crs +3527 932 Crs +3535 932 Crs +3543 930 Crs +3551 930 Crs +3559 930 Crs +3567 928 Crs +3575 928 Crs +3583 928 Crs +3591 927 Crs +3599 927 Crs +3606 925 Crs +3614 925 Crs +3622 923 Crs +3630 923 Crs +3638 921 Crs +3646 920 Crs +3654 920 Crs +3662 920 Crs +3670 920 Crs +3678 916 Crs +3686 914 Crs +3694 914 Crs +3701 914 Crs +3709 914 Crs +3717 913 Crs +3725 913 Crs +3733 911 Crs +3741 911 Crs +3749 911 Crs +3757 911 Crs +3765 911 Crs +3773 911 Crs +3781 911 Crs +3789 911 Crs +3797 909 Crs +3804 909 Crs +3812 909 Crs +3820 909 Crs +3828 909 Crs +3836 909 Crs +3844 907 Crs +3852 907 Crs +3860 907 Crs +3868 907 Crs +3876 906 Crs +3884 906 Crs +3892 906 Crs +3899 904 Crs +3907 904 Crs +3915 904 Crs +3923 902 Crs +3931 902 Crs +3939 902 Crs +3947 902 Crs +3955 902 Crs +3963 902 Crs +3971 900 Crs +3979 900 Crs +3987 900 Crs +3994 900 Crs +4002 900 Crs +4010 898 Crs +4018 898 Crs +4026 898 Crs +4034 898 Crs +4042 898 Crs +4050 898 Crs +4058 896 Crs +4066 896 Crs +4074 896 Crs +4082 896 Crs +4089 896 Crs +4097 895 Crs +4105 895 Crs +4113 895 Crs +4121 895 Crs +4129 895 Crs +4137 893 Crs +4145 893 Crs +4153 893 Crs +4161 893 Crs +4169 893 Crs +4177 891 Crs +4184 889 Crs +4192 889 Crs +4200 887 Crs +4208 887 Crs +4216 887 Crs +4224 887 Crs +4232 887 Crs +4240 887 Crs +4248 885 Crs +4256 885 Crs +4264 885 Crs +4272 885 Crs +4280 885 Crs +4287 885 Crs +4295 883 Crs +4303 883 Crs +4311 883 Crs +4319 883 Crs +4327 883 Crs +4335 881 Crs +4343 881 Crs +4351 881 Crs +4359 881 Crs +4367 879 Crs +4375 879 Crs +4382 877 Crs +4390 877 Crs +4398 877 Crs +4406 877 Crs +4414 877 Crs +4422 877 Crs +4430 876 Crs +4438 876 Crs +4446 876 Crs +4454 876 Crs +4462 876 Crs +4470 874 Crs +4477 874 Crs +4485 874 Crs +4493 874 Crs +4501 874 Crs +4509 874 Crs +4517 874 Crs +4525 874 Crs +4533 872 Crs +4541 872 Crs +4549 872 Crs +4557 872 Crs +4565 872 Crs +4572 870 Crs +4580 870 Crs +4588 870 Crs +4596 868 Crs +4604 868 Crs +4612 866 Crs +4620 866 Crs +4628 866 Crs +4636 866 Crs +4644 866 Crs +4652 866 Crs +4660 866 Crs +4667 864 Crs +4675 864 Crs +4683 864 Crs +4691 864 Crs +4699 864 Crs +4707 864 Crs +% End plot #1 +1.000 UL +LTb +756 3383 N +756 576 L +3959 0 V +0 2807 V +-3959 0 V +Z stroke +1.000 UP +1.000 UL +LTb +stroke +grestore +end +showpage +%%Trailer +%%DocumentFonts: Helvetica -- 2.11.4.GIT