doRNG/0000755000176200001440000000000014361227063011227 5ustar liggesusersdoRNG/NAMESPACE0000644000176200001440000000041613621322014012435 0ustar liggesusers# Generated by roxygen2: do not edit by hand export("%dorng%") export(doRNGversion) export(registerDoRNG) import(foreach) import(rngtools) import(stats) importFrom(iterators,iter) importFrom(utils,compareVersion) importFrom(utils,head) importFrom(utils,packageVersion) doRNG/demo/0000755000176200001440000000000013556560424012161 5ustar liggesusersdoRNG/demo/00Index0000644000176200001440000000006413556341614013311 0ustar liggesusersdoRNG Some features of the %dorng% foreach operator doRNG/demo/doRNG.R0000644000176200001440000000257513556560424013266 0ustar liggesuserslibrary(doRNG) library(doParallel) if( .Platform$OS.type == "unix" ){ registerDoParallel(2) # single %dorng% loops are reproducible r1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } r2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } identical(r1, r2) # the sequence os RNG seed is stored as an attribute attr(r1, 'rng') # sequences of %dorng% loops are reproducible set.seed(1234) s1 <- foreach(i=1:4) %dorng% { runif(1) } s2 <- foreach(i=1:4) %dorng% { runif(1) } # two consecutive (unseed) %dorng% loops are not identical identical(s1, s2) # But the whole sequence of loops is reproducible set.seed(1234) s1.2 <- foreach(i=1:4) %dorng% { runif(1) } s2.2 <- foreach(i=1:4) %dorng% { runif(1) } identical(s1, s1.2) && identical(s2, s2.2) # it gives the same result as with .options.RNG identical(r1, s1) } # Works with SNOW-like and MPI clusters # SNOW-like cluster cl <- makeCluster(2) registerDoParallel(cl) s1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } s2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } identical(s1, s2) stopCluster(cl) registerDoSEQ() # MPI cluster library(doMPI) cl <- startMPIcluster(2) registerDoMPI(cl) s1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } s2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } identical(s1, s2) closeCluster(cl) registerDoSEQ() doRNG/.Rinstignore0000644000176200001440000000002713556341614013536 0ustar liggesusersvignettes/cleveref.sty doRNG/man/0000755000176200001440000000000014360256113011777 5ustar liggesusersdoRNG/man/doRNG-package.Rd0000644000176200001440000000514414360256113014634 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/doRNG-package.R \docType{package} \encoding{UTF-8} \name{doRNG-package} \alias{doRNG-package} \title{Generic Reproducible Parallel Backend for foreach Loops} \description{ The \emph{doRNG} package provides functions to perform reproducible parallel foreach loops, using independent random streams as generated by L'Ecuyer's combined multiple-recursive generator (L'Ecuyer (1999)). It enables to easily convert standard \%dopar\% loops into fully reproducible loops, independently of the number of workers, the task scheduling strategy, or the chosen parallel environment and associated foreach backend. It has been tested with the following foreach backend: doMC, doSNOW, doMPI. } \examples{ # register parallel backend library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) ## standard \%dopar\% loop are not reproducible set.seed(123) r1 <- foreach(i=1:4) \%dopar\%{ runif(1) } set.seed(123) r2 <- foreach(i=1:4) \%dopar\%{ runif(1) } identical(r1, r2) \dontshow{ stopifnot(!identical(r1, r2)) } ## \%dorng\% loops _are_ reproducible set.seed(123) r1 <- foreach(i=1:4) \%dorng\%{ runif(1) } set.seed(123) r2 <- foreach(i=1:4) \%dorng\%{ runif(1) } identical(r1, r2) \dontshow{ stopifnot(identical(r1, r2)) } # alternative way of seeding a1 <- foreach(i=1:4, .options.RNG=123) \%dorng\%{ runif(1) } a2 <- foreach(i=1:4, .options.RNG=123) \%dorng\%{ runif(1) } identical(a1, a2) && identical(a1, r1) \dontshow{ stopifnot(identical(a1, a2) && identical(a1, r1)) } ## sequences of \%dorng\% loops _are_ reproducible set.seed(123) s1 <- foreach(i=1:4) \%dorng\%{ runif(1) } s2 <- foreach(i=1:4) \%dorng\%{ runif(1) } identical(s1, r1) && !identical(s1, s2) \dontshow{ stopifnot(identical(s1, r1) && !identical(s1, s2)) } set.seed(123) s1.2 <- foreach(i=1:4) \%dorng\%{ runif(1) } s2.2 <- foreach(i=1:4) \%dorng\%{ runif(1) } identical(s1, s1.2) && identical(s2, s2.2) \dontshow{ stopifnot(identical(s1, s1.2) && identical(s2, s2.2)) } ## Non-invasive way of converting \%dopar\% loops into reproducible loops registerDoRNG(123) s3 <- foreach(i=1:4) \%dopar\%{ runif(1) } s4 <- foreach(i=1:4) \%dopar\%{ runif(1) } identical(s3, s1) && identical(s4, s2) \dontshow{ stopifnot(identical(s3, s1) && identical(s4, s2)) } stopCluster(cl) } \references{ L'Ecuyer P (1999). “Good Parameters and Implementations for Combined Multiple Recursive Random Number Generators.” _Operations Research_, *47*(1), 159-164. ISSN 0030-364X, doi:10.1287/opre.47.1.159 . } \seealso{ \code{\link{doRNG}}, \code{\link{RNGseq}} } \keyword{package} doRNG/man/grapes-dorng-grapes.Rd0000644000176200001440000000405514360233466016147 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/doRNG.R \name{\%dorng\%} \alias{\%dorng\%} \title{Reproducible Parallel Foreach Backend} \usage{ obj \%dorng\% ex } \arguments{ \item{obj}{a foreach object as returned by a call to \code{\link{foreach}}.} \item{ex}{the \code{R} expression to evaluate.} } \value{ \verb{\%dorng\%} returns the result of the foreach loop. See \link[foreach:foreach]{foreach::\%dopar\%}. The whole sequence of RNG seeds is stored in the result object as an attribute. Use \code{attr(res, 'rng')} to retrieve it. } \description{ \verb{\%dorng\%} is a foreach operator that provides an alternative operator \verb{\%dopar\%}, which enable reproducible foreach loops to be performed. } \section{Global options}{ These options are for advanced users that develop `foreach backends: \itemize{ \item 'doRNG.rng_change_warning_skip': if set to a single logical \code{FALSE/TRUE}, it indicates whether a warning should be thrown if the RNG seed is changed by the registered parallel backend (default=FALSE). Set it to \code{TRUE} if you know that running your backend will change the RNG state and want to disable the warning. This option can also be set to a character vector that specifies the name(s) of the backend(s) for which the warning should be skipped. } } \examples{ library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) # standard \%dopar\% loops are _not_ reproducible set.seed(1234) s1 <- foreach(i=1:4) \%dopar\% { runif(1) } set.seed(1234) s2 <- foreach(i=1:4) \%dopar\% { runif(1) } identical(s1, s2) # single \%dorng\% loops are reproducible r1 <- foreach(i=1:4, .options.RNG=1234) \%dorng\% { runif(1) } r2 <- foreach(i=1:4, .options.RNG=1234) \%dorng\% { runif(1) } identical(r1, r2) # the sequence os RNG seed is stored as an attribute attr(r1, 'rng') # stop cluster stopCluster(cl) # More examples can be found in demo `doRNG` \dontrun{ demo('doRNG') } } \seealso{ \code{\link{foreach}}, \code{\link[doParallel]{doParallel}} , \code{\link[doParallel]{registerDoParallel}}, \code{\link[doMPI]{doMPI}} } doRNG/man/infoDoRNG.Rd0000644000176200001440000000176514360232706014066 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/doRNG.R \name{infoDoRNG} \alias{infoDoRNG} \alias{doRNG} \title{Getting Information About doRNG Foreach Backend} \usage{ infoDoRNG(data, item) doRNG(obj, ex, envir, data) } \arguments{ \item{data}{configuration data of the doRNG backend} \item{item}{the data item requested, as a character string (e.g. 'name', 'workers', 'version')} \item{obj}{a foreach description of the loop arguments} \item{ex}{the lopp expression} \item{envir}{the loop's evaluation environment} } \value{ \code{infoDoRNG} returns the requested info (usually as a character string or a numeric value). } \description{ \code{infoDoRNG} returns information about the doRNG backend, e.g., version, number of workers. It is not meant to be called by the user. } \author{ Renaud Gaujoux } \keyword{internal} \section{Functions}{ \itemize{ \item \code{doRNG}: implements the generic reproducible foreach backend. It should not be called directly by the user. }} doRNG/man/doRNGversion.Rd0000644000176200001440000000446614360233070014654 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/doRNG.R \name{doRNGversion} \alias{doRNGversion} \title{Back Compatibility Option for doRNG} \usage{ doRNGversion(x) } \arguments{ \item{x}{version number to switch to, or missing to get the currently active version number, or \code{NULL} to reset to the default behaviour, i.e. of the latest version.} } \value{ a character string If \code{x} is missing this function returns the version number from the current behaviour. If \code{x} is specified, the function returns the old value of the version number (invisible). } \description{ Sets the behaviour of \%dorng\% foreach loops from a given version number. } \section{Behaviour changes in versions}{ \describe{ \item{1.4}{ The behaviour of \code{doRNGseed}, and therefore of \verb{\%dorng\%} loops, changed in the case where the current RNG was L'Ecuyer-CMRG. Using \code{set.seed} before a non-seeded loop used not to be identical to seeding via \code{.options.RNG}. Another bug was that non-seeded loops would share most of their RNG seed! } \item{1.7.4}{Prior to this version, in the case where the RNG had not been called yet, the first seeded \verb{\%dorng\%} loops would not give the identical results as subsequent loops despite using the same seed (see \url{https://github.com/renozao/doRNG/issues/12}). This has been fixed in version 1.7.4, where the RNG is called once (\code{sample(NA)}), whenever the .Random.seed is not found in global environment. } } } \examples{ \dontshow{ registerDoSEQ() } ## Seeding when current RNG is L'Ecuyer-CMRG RNGkind("L'Ecuyer") doRNGversion("1.4") # in version >= 1.4 seeding behaviour changed to fix a bug set.seed(123) res <- foreach(i=1:3) \%dorng\% runif(1) res2 <- foreach(i=1:3) \%dorng\% runif(1) stopifnot( !identical(attr(res, 'rng')[2:3], attr(res2, 'rng')[1:2]) ) res3 <- foreach(i=1:3, .options.RNG=123) \%dorng\% runif(1) stopifnot( identical(res, res3) ) # buggy behaviour in version < 1.4 doRNGversion("1.3") res <- foreach(i=1:3) \%dorng\% runif(1) res2 <- foreach(i=1:3) \%dorng\% runif(1) stopifnot( identical(attr(res, 'rng')[2:3], attr(res2, 'rng')[1:2]) ) res3 <- foreach(i=1:3, .options.RNG=123) \%dorng\% runif(1) stopifnot( !identical(res, res3) ) # restore default RNG RNGkind("default") # restore to current doRNG version doRNGversion(NULL) } doRNG/man/registerDoRNG.Rd0000644000176200001440000000507414360233410014745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/doRNG.R \name{registerDoRNG} \alias{registerDoRNG} \title{Registering doRNG for Persistent Reproducible Parallel Foreach Loops} \usage{ registerDoRNG(seed = NULL, once = TRUE) } \arguments{ \item{seed}{a numerical seed to use (as a single or 6-length numerical value)} \item{once}{a logical to indicate if the RNG sequence should be seeded at the beginning of each loop or only at the first loop.} } \value{ The value returned by \link[foreach:setDoPar]{foreach::setDoPar} } \description{ \code{registerDoRNG} registers the doRNG foreach backend. Subsequent \verb{\%dopar\%} loops are then performed using the previously registered foreach backend, but are internally performed as \link{\%dorng\%} loops, making them fully reproducible. } \details{ Briefly, the RNG is set, before each iteration, with seeds for L'Ecuyer's CMRG that overall generate a reproducible sequence of statistically independent random streams. Note that (re-)registering a foreach backend other than doRNG, after a call to \code{registerDoRNG} disables doRNG -- which then needs to be registered. } \examples{ library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) # One can make reproducible loops using the \%dorng\% operator r1 <- foreach(i=1:4, .options.RNG=1234) \%dorng\% { runif(1) } # or convert \%dopar\% loops using registerDoRNG registerDoRNG(1234) r2 <- foreach(i=1:4) \%dopar\% { runif(1) } identical(r1, r2) stopCluster(cl) # Registering another foreach backend disables doRNG cl <- makeCluster(2) registerDoParallel(cl) set.seed(1234) s1 <- foreach(i=1:4) \%dopar\% { runif(1) } set.seed(1234) s2 <- foreach(i=1:4) \%dopar\% { runif(1) } identical(s1, s2) \dontshow{ stopifnot(!identical(s1, s2)) } # doRNG is re-nabled by re-registering it registerDoRNG() set.seed(1234) r3 <- foreach(i=1:4) \%dopar\% { runif(1) } identical(r2, r3) # NB: the results are identical independently of the task scheduling # (r2 used 2 nodes, while r3 used 3 nodes) # argument `once=FALSE` reseeds doRNG's seed at the beginning of each loop registerDoRNG(1234, once=FALSE) r1 <- foreach(i=1:4) \%dopar\% { runif(1) } r2 <- foreach(i=1:4) \%dopar\% { runif(1) } identical(r1, r2) # Once doRNG is registered the seed can also be passed as an option to \%dopar\% r1.2 <- foreach(i=1:4, .options.RNG=456) \%dopar\% { runif(1) } r2.2 <- foreach(i=1:4, .options.RNG=456) \%dopar\% { runif(1) } identical(r1.2, r2.2) && !identical(r1.2, r1) \dontshow{ stopifnot(identical(r1.2, r2.2) && !identical(r1.2, r1)) } stopCluster(cl) } \seealso{ \link{\%dorng\%} } doRNG/DESCRIPTION0000644000176200001440000000240714361227063012740 0ustar liggesusersPackage: doRNG Type: Package Title: Generic Reproducible Parallel Backend for 'foreach' Loops Version: 1.8.6 Encoding: UTF-8 Authors@R: person("Renaud", "Gaujoux", email = "renozao@protonmail.com", role = c("aut", "cre")) Description: Provides functions to perform reproducible parallel foreach loops, using independent random streams as generated by L'Ecuyer's combined multiple-recursive generator [L'Ecuyer (1999), ]. It enables to easily convert standard '%dopar%' loops into fully reproducible loops, independently of the number of workers, the task scheduling strategy, or the chosen parallel environment and associated foreach backend. License: GPL (>= 2) LazyLoad: yes URL: https://renozao.github.io/doRNG/ BugReports: https://github.com/renozao/doRNG/issues VignetteBuilder: knitr Depends: R (>= 3.0.0), foreach, rngtools (>= 1.5) Imports: stats, utils, iterators Suggests: doParallel, doMPI, doRedis, rbenchmark, devtools, knitr, rbibutils (>= 1.3), testthat, pkgmaker (>= 0.32.7), covr RoxygenNote: 7.2.3 NeedsCompilation: no Packaged: 2023-01-13 15:12:10 UTC; renaud Author: Renaud Gaujoux [aut, cre] Maintainer: Renaud Gaujoux Repository: CRAN Date/Publication: 2023-01-16 11:00:03 UTC doRNG/build/0000755000176200001440000000000014360272312012322 5ustar liggesusersdoRNG/build/vignette.rds0000644000176200001440000000036714360272312014667 0ustar liggesusersmQM@ Mt[Kkqpؘ%p44DT[4) Z j']hJfFԸBT߻:5vX";w'$.Wc宆OcSua+wBa.l1;ofdoRNG/tests/0000755000176200001440000000000013556341614012375 5ustar liggesusersdoRNG/tests/testthat/0000755000176200001440000000000014361227063014231 5ustar liggesusersdoRNG/tests/testthat/test-doRepro.r0000644000176200001440000000353013556341614017010 0ustar liggesusers# Unit tests for the package doRepro # # Author: renaud gaujoux # Creation: 30 Jun 2011 ############################################################################### context("Reproducibility") test_that('xapply', { skip("Development is not finished yet") .seed <- 1:6 n <- 3 expect_identical(xapply(1:n, .seed, function(i) i), 1:n, "Main argument is correctly passed") expect_identical(xapply(1:n, .seed, function(i, b){b}, b='a'), rep('a',n), "Other arguments are correctly passed (1)") expect_identical(xapply(1:n, .seed, function(i, b, c){c}, c='a'), rep('a',n), "Other arguments are correctly passed (2)") rngs <- sapply(RNGseq(n, .seed), RNGdigest) expect_identical(xapply(1:n, .seed, function(i){ RNGdigest() }), rngs, "RNG are correctly set") # check that the stream seed is restored rngs <- sapply(RNGseq(n-1, .seed), RNGdigest) res <- xapply(1:n, .seed, function(i){ RNGdigest() }) rngs <- cbind(rngs, RNGdigest()) expect_identical(res, rngs, "RNG are correctly set") # results are reproducible orng <- getRNG() res <- xapply(1:n, .seed, function(i) runif(i) ) expect_true( rng.equal(orng), "RNG is restored") expect_identical(res, xapply(1:n, .seed, function(i) runif(i) ), "Results are reproducible") expect_identical(sapply(res, length), 1:n, "Test results have correct dimension") }) test_that("reproduce", { skip("Development is not finished yet") .seed <- 1:6 n <- 3 p <- 5 rngs <- sapply(RNGseq(3, .seed), RNGdigest) expect_identical(reproduce(n, .seed, RNGdigest()), rngs, "RNG are correctly set") # results are reproducible orng <- getRNG() res <- reproduce(n, .seed, runif(p) ) expect_true( rng.equal(orng), "RNG is restored") expect_identical(res, reproduce(n, .seed, runif(p) ), "Results are reproducible") expect_identical(dim(res), as.integer(c(p,n)), "Test results have correct dimension") }) doRNG/tests/testthat/test-dorng-nonattached.r0000644000176200001440000000037214360231303020761 0ustar liggesuserscontext('dorng - foreach not attached') test_that("%dorng% works also when foreach is not attached", { `%dorng%` <- doRNG::`%dorng%` y <- foreach::foreach(x = 1:2) %dorng% { x } stopifnot(all.equal(y, list(1, 2), check.attributes = FALSE)) }) doRNG/tests/testthat/test-dorng.r0000644000176200001440000004072613760210061016503 0ustar liggesusers# Unit test for doRNG # # Author: Renaud Gaujoux # Creation: 28 Mar 2012 ############################################################################### context('dorng') #test.CMRGseed <- function(){ # # msg <- function(...) paste(.msg, ':', ...) # # # Unit tests # .msg <- "Call CMRGseed without argument" # rs <- .Random.seed # expect_identical( length(CMRGseed()), 7L, msg("Seed is of length 7") ) # expect_identical(rs, .Random.seed, msg("does not change .Random.seed")) # # .msg <- "Call CMRGseed with a single argument" # rs <- .Random.seed # expect_identical( length(CMRGseed(1)), 7L, msg("Seed is of length 7") ) # expect_identical(rs, .Random.seed, msg("does not change .Random.seed")) # expect_true( all(!is.na(CMRGseed(1))), msg("No NA in the returned seed") ) # #} checkRNG <- function(x, y, msg = NULL, ...){ expect_true(rng.equal(x, y), info = msg, ...) } # 1-length loop test_that("dorng1", { set.seed(1234) # needed to avoid weird behaviors in checks rng_seed <- RNGseq(n = 1, seed = 123, simplify = FALSE)[[1L]] set.seed(123) x <- foreach(i=1) %dorng% { runif(1) } y <- foreach(i=1, .options.RNG = 123) %dorng% { runif(1) } expect_identical(x, y) # check attributes on results result_attributes <- attributes(x) expect_true(setequal(names(result_attributes), c("rng", "doRNG_version")), info = "Result has all the expected attributes") expect_identical(result_attributes[["rng"]][[1L]], rng_seed, info = "Attribute 'rng' does not have the expected value") expect_identical(result_attributes[["doRNG_version"]], doRNGversion(), info = "Attribute 'doRNG_version' does not have the expected value") }) test_that("dorng", { test_dopar <- function(.msg, s.seq){ orng <- RNGseed() on.exit({ doRNGversion(NULL); RNGseed(orng); registerDoSEQ()} ) msg <- function(...) paste(.msg, ':', ...) noattr <- function(x){ attributes(x) <- NULL; x} # RNG restoration after %dorng% rng0 <- getRNG() foreach(i=1:4, .options.RNG = 123) %dorng% { runif(1) } checkRNG(rng0, msg = "RNG is restored after seeded %dorng%") # foreach(i=1:4) %dorng% { runif(1) } expect_identical(RNGtype(), RNGtype(rng0), info = "RNG kind is restored after unseeded %dorng%") # standard %dopar% loops are _not_ reproducible set.seed(1234) s1 <- foreach(i=1:4) %dopar% { runif(1) } set.seed(1234) s2 <- foreach(i=1:4) %dopar% { runif(1) } if( !missing(s.seq) ) expect_true( !identical(s1, s2), msg("Standard %dopar% loop is not reproducible")) # %dorng% loops ensure reproducibility local({ set.seed(1234) s1 <- foreach(i=1:4) %dorng% { runif(1) } runif(10) set.seed(1234) s2 <- foreach(i=1:4) %dorng% { runif(1) } expect_identical(s1, s2, msg("%dorng% loop is reproducible with set.seed")) # check RNG settings in result set.seed(1234) ref <- RNGseq(4) rngs <- attr(s1, 'rng') expect_true(!is.null(rngs), msg("Results contains RNG data")) expect_identical(rngs, ref, msg("Results contains whole sequence of RNG seeds")) }) # or local({ s1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } s2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } expect_identical(s1, s2, msg("%dorng% loop is reproducible with .options.RNG")) ref <- RNGseq(4, 1234) rngs <- attr(s1, 'rng') expect_true(!is.null(rngs), msg("Results of seed loop contains RNG data")) expect_identical(rngs, ref, msg("Results of seeded loop contains whole sequence of RNG seeds")) }) # check with unamed foreach arguments (issue #8) local({ on.exit( registerDoSEQ() ) registerDoRNG() set.seed(567) res <- foreach(a = 1:4, .combine = 'c') %dopar% {rnorm(1, mean = 0, sd = 1)} set.seed(567) res2 <- foreach(1:4, .combine = 'c') %dopar% {rnorm(1, mean = 0, sd = 1)} expect_identical(res, res2, info = "First argument named or unamed is equivalent") # set.seed(567) res <- foreach(a = 1:4, 1:2, .combine = 'c') %dopar% {rnorm(1, mean = 0, sd = 1)} set.seed(567) res2 <- foreach(1:4, 1:2, .combine = 'c') %dopar% {rnorm(1, mean = 0, sd = 1)} expect_identical(res, res2, info = "First argument named or unamed, with second unamed argument is equivalent") }) ## ## check extra arguments to .options.RNG # Normal RNG parameter is taken into account s.unif.noNk <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(5) } s.unif.wNk <- foreach(i=1:4, .options.RNG=list(1234, normal.kind="Ahrens-Dieter")) %dorng% { runif(5) } expect_identical(noattr(s.unif.noNk), noattr(s.unif.wNk) , msg("%dorng% loop (runif) with normal.kind in .options.RNG is identical as without")) s.norm.noNk <- foreach(i=1:4, .options.RNG=1234) %dorng% { rnorm(5) } s.norm.wNk <- foreach(i=1:4, .options.RNG=list(1234, normal.kind="Ahrens-Dieter")) %dorng% { rnorm(5) } expect_true(!isTRUE(all.equal(noattr(s.norm.noNk), noattr(s.norm.wNk))) , msg("%dorng% loop (rnorm) with normal.kind in .options.RNG is different as without")) # reproduce previous loop runif(10) s1 <- foreach(i=1:4) %dorng% { runif(5) } s2 <- foreach(i=1:4, .options.RNG=s1) %dorng% { runif(5) } expect_identical(s1, s2, "Seeding using .options.RNG={result from other loop} give identical results") # directly set sequence of seeds sL <- list(c(407,1:6), c(407,11:16), c(407,21:26), c(407, 31:36)) s1.L <- foreach(i=1:4, .options.RNG=sL) %dorng% { runif(5) } runif(10) s2.L <- foreach(i=1:4, .options.RNG=sL) %dorng% { runif(5) } expect_identical(s1.L, s2.L, "Seeding using .options.RNG=list twice give identical results") # directly set sequence of seeds as a matrix sM <- sapply(sL, identity) s1.M <- foreach(i=1:4, .options.RNG=sM) %dorng% { runif(5) } runif(10) s2.M <- foreach(i=1:4, .options.RNG=sM) %dorng% { runif(5) } expect_identical(s1.M, s2.M, "Seeding using .options.RNG=matrix twice give identical results") expect_identical(s1.M, s1.L, "Seeding using .options.RNG=matrix gives identical results as the same seed in list") # separate %dorng% loops are different set.seed(1234) rs1 <- .Random.seed s1 <- foreach(i=1:4) %dorng% { runif(1) } rs1_2 <- .Random.seed s2 <- foreach(i=1:4) %dorng% { runif(1) } expect_true( !identical(rs1, rs1_2), msg("unseed %dorng% loop changes .Random.seed")) expect_true( !identical(s1, s2), msg("two consecutive (unseeded) %dorng% loops are not identical")) expect_true( !identical(unlist(s1), unlist(s2)), msg("two consecutive (unseeded) %dorng% loops are not identical (values)")) # But the whole sequence of loops is reproducible set.seed(1234) s1.2 <- foreach(i=1:4) %dorng% { runif(1) } s2.2 <- foreach(i=1:4) %dorng% { runif(1) } expect_true( identical(s1, s1.2) && identical(s2, s2.2), msg("set.seed + two consecutive %dorng% loops are reproducible")) s <- list(s1, s2) if( !missing(s.seq) ) expect_identical(s, s.seq, msg("result is identical to sequential computation")) # check behaviour with set.seed set.seed(789) s1 <- foreach(i=1:6) %dorng%{ runif(1) } s2 <- foreach(i=1:6, .options.RNG=789) %dorng%{ runif(1) } expect_identical(s1, s2, "set.seed before %dorng% is identical to using .options.RNG") # current RNG is CRMG set.seed(789, "L'Ec") s1 <- foreach(i=1:6) %dorng%{ runif(1) } s2 <- foreach(i=1:6, .options.RNG=789) %dorng%{ runif(1) } expect_identical(s1, s2, "set.seed before %dorng% is identical to using .options.RNG, if current RNG is CRMG") s } orng <- RNGseed() on.exit({ doRNGversion(NULL); RNGseed(orng); registerDoSEQ()} ) library(doParallel) # Sequential computation registerDoSEQ() s.seq <- test_dopar("Sequential") # Multicore cluster if( .Platform$OS.type != 'windows'){ # Note: for some reason, running this test in RStudio fails when checking that the standard # %dopar% loop is not reproducible registerDoParallel(cores=2) s <- test_dopar("Multicore", s.seq) } # SNOW-like cluster cl <- makeCluster(2) on.exit( if( !is.null(cl) ) stopCluster(cl), add = TRUE) registerDoParallel(cl) test_dopar("SNOW-like cluster", s.seq) stopCluster(cl); cl <- NULL skip("doMPI test because doMPI::startMPIcluster hangs inexplicably") # Works with doMPI if( require(doMPI) ){ cl_mpi <- startMPIcluster(2) on.exit( if( !is.null(cl_mpi) ) closeCluster(cl_mpi), add = TRUE) registerDoMPI(cl_mpi) test_dopar("MPI cluster", s.seq) closeCluster(cl_mpi); cl_mpi <- NULL } }) test_that("registerDoRNG", { orng <- RNGseed() on.exit({ doRNGversion(NULL); RNGseed(orng); registerDoSEQ()} ) # RNG restoration after %dorng% over doSEQ registerDoSEQ() registerDoRNG() set.seed(123) rng0 <- getRNG() res1 <- foreach(i=1:4) %dorng% { runif(1) } expect_identical(RNGtype(), RNGtype(rng0), "RNG kind is restored after unseeded %dorng%") set.seed(123) res2 <- foreach(i=1:4) %dorng% { runif(1) } expect_identical(res1, res2, "%dorng% loop over doSEQ are reproducible") on.exit( if( !is.null(cl) ) stopCluster(cl), add = TRUE) library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) # One can make existing %dopar% loops reproducible using %dorng% loops or registerDoRNG set.seed(1234) r1 <- foreach(i=1:4) %dorng% { runif(1) } registerDoRNG() set.seed(1234) r2 <- foreach(i=1:4) %dopar% { runif(1) } expect_identical(r1, r2, "registerDoRNG + set.seed: makes a %dopar% loop behave like a set.seed + %dorng% loop") stopCluster(cl); cl <- NULL # Registering another foreach backend disables doRNG cl2 <- makeCluster(2) on.exit( if( !is.null(cl2) ) stopCluster(cl2), add = TRUE) registerDoParallel(cl2) set.seed(1234) s1 <- foreach(i=1:4) %dopar% { runif(1) } set.seed(1234) s2 <- foreach(i=1:4) %dorng% { runif(1) } expect_true( !identical(s1, s2), "Registering another foreach backend disables doRNG") # doRNG is re-nabled by re-registering it registerDoRNG() set.seed(1234) r3 <- foreach(i=1:4) %dopar% { runif(1) } expect_identical(r2, r3, "doRNG is re-nabled by re-registering it") r4 <- foreach(i=1:4) %dopar% { runif(1) } # NB: the results are identical independently of the task scheduling # (r2 used 2 nodes, while r3 used 3 nodes) # Reproducibility of sequences of loops # pass seed to registerDoRNG runif(10) registerDoRNG(1234) r1 <- foreach(i=1:4) %dopar% { runif(1) } r2 <- foreach(i=1:4) %dopar% { runif(1) } registerDoRNG(1234) r3 <- foreach(i=1:4) %dopar% { runif(1) } r4 <- foreach(i=1:4) %dopar% { runif(1) } expect_identical(r3, r1, "registerDoRNG(1234) allow reproducing sequences of %dopar% loops (1)") expect_identical(r4, r2, "registerDoRNG(1234) allow reproducing sequences of %dopar% loops (2)") # use set.seed runif(10) registerDoRNG() set.seed(1234) s1 <- foreach(i=1:4) %dopar% { runif(1) } s2 <- foreach(i=1:4) %dopar% { runif(1) } set.seed(1234) s3 <- foreach(i=1:4) %dopar% { runif(1) } s4 <- foreach(i=1:4) %dopar% { runif(1) } expect_identical(s3, s1, "registerDoRNG + set.seed(1234) allow reproducing sequences of %dopar% loops (1)") expect_identical(s4, s2, "registerDoRNG + set.seed(1234) allow reproducing sequences of %dopar% loops (2)") runif(5) registerDoRNG() set.seed(1234) s5 <- foreach(i=1:4) %dopar% { runif(1) } s6 <- foreach(i=1:4) %dopar% { runif(1) } expect_identical(s5, r3, "registerDoRNG() + set.seed give same results as registerDoRNG(1234) (1)") expect_identical(s6, r4, "registerDoRNG() + set.seed give same results as registerDoRNG(1234) (2)") # argument `once=FALSE` reseed doRNG's seed at the beginning of each loop registerDoRNG(1234, once=FALSE) r1 <- foreach(i=1:4) %dopar% { runif(1) } r2 <- foreach(i=1:4) %dopar% { runif(1) } r3 <- foreach(i=1:4, .options.RNG=1234) %dopar% { runif(1) } expect_identical(r1, r2, "argument `once=FALSE` reseed doRNG's seed at the beginning of each loop") expect_identical(r1, r3, "argument `once=FALSE` reseed %dorng% loop as .options.RNG") # Once doRNG is registered the seed can also be passed as an option to %dopar% r1.2 <- foreach(i=1:4, .options.RNG=456) %dopar% { runif(1) } r2.2 <- foreach(i=1:4, .options.RNG=456) %dopar% { runif(1) } expect_identical(r1.2, r2.2, "Once doRNG is registered the seed can also be passed as an option to %dopar%") expect_true(!identical(r1.2, r1), "The seed passed as an option is really taken into account") }) # Test the use-case discussed in https://github.com/renozao/doRNG/issues/12 # Note: when run under RStudio, this test_that("Initial RNG state is properly handled", { # write script that loads the package being tested .run_test_script <- function(version){ pkg_path <- path.package("doRNG") lib_path <- dirname(pkg_path) # determine if the package is a development or installed package if( dir.exists(file.path(pkg_path, "Meta")) ) load_cmd <- sprintf("library(doRNG, lib = '%s')", lib_path) else load_cmd <- sprintf("devtools::load_all('%s')", pkg_path) # results are saved in a temporary .rds file (substitute backslashes with forward slash for Windows) tmp_res <- gsub("\\", "/", tempfile("rscript_res_", fileext = ".rds"), fixed = TRUE) on.exit( unlink(tmp_res) ) r_code <- paste0(collapse = "; ", c(load_cmd, if( !is.null(version) ) sprintf("doRNGversion('%s')", version), "r1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) }", "r2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) }", "r3 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) }", sprintf("saveRDS(list(r1 = r1, r2 = r2, r3 = r3), '%s')", tmp_res) ) ) # run code in an independent fresh session rscript <- file.path(R.home("bin"), "Rscript") system(sprintf('"%s" -e "%s"', rscript, r_code), ignore.stdout = TRUE, ignore.stderr = TRUE) # load result readRDS(tmp_res) } # pre-1.7.4: results are not-reproducible res <- .run_test_script("1.7.3") expect_true(is.list(res) && identical(names(res), paste0("r", 1:3))) expect_true(all(sapply(res, attr, 'doRNG_version') == "1.7.3"), info = "doRNG version is correctly stored") expect_true(!identical(res[["r1"]], res[["r2"]]), info = "Version pre-1.7.3: results 1 & 2 are not identical") expect_identical(res[["r3"]], res[["r2"]], info = "Version pre-1.7.3: results 2 & 3 are identical") # post-1.7.4: results are reproducible res <- .run_test_script("1.7.4") expect_true(is.list(res) && identical(names(res), paste0("r", 1:3))) expect_true(all(sapply(res, attr, 'doRNG_version') == "1.7.4"), info = "doRNG version is correctly stored") expect_identical(res[["r1"]], res[["r2"]], info = "Version 1.7.4: results 1 & 2 are identical") expect_identical(res[["r3"]], res[["r2"]], info = "Version 1.7.4: results 3 & 3 are identical") # current version: results are reproducible res <- .run_test_script(NULL) expect_true(is.list(res) && identical(names(res), paste0("r", 1:3))) expect_true(all(sapply(res, attr, 'doRNG_version') == doRNGversion()), info = "doRNG version is correctly stored") expect_identical(res[["r1"]], res[["r2"]], info = "Current version: results 1 & 2 are identical") expect_identical(res[["r3"]], res[["r2"]], info = "Current version: results 2 & 3 are identical") }) test_that("RNG warnings", { .local <- function(){ orng <- RNGseed() oo <- options() on.exit({ options(oo); doRNGversion(NULL); RNGseed(orng); registerDoSEQ()} ) registerDoSEQ() registerDoRNG() expect_warning(y <- foreach(x = 1:2) %dorng% { rnorm(1); x }, NA) options(doRNG.rng_change_warning_force = TRUE) expect_warning(y <- foreach(x = 1:2) %dorng% { rnorm(1); x }, "Foreach loop \\(doSEQ\\) .* changed .* RNG type") options(doRNG.rng_change_warning_force = NULL) on.exit( if( !is.null(cl) ) stopCluster(cl), add = TRUE) library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) options(doRNG.rng_change_warning_force = TRUE) options(doRNG.rng_change_warning_skip = "doParallelSNOW") expect_warning(y <- foreach(x = 1:2) %dorng% { rnorm(1); x }, "Foreach loop \\(doParallelSNOW\\) .* changed .* RNG type") options(doRNG.rng_change_warning_force = NULL) expect_warning(y <- foreach(x = 1:2) %dorng% { rnorm(1); x }, NA) options(doRNG.rng_change_warning_skip = NULL) } .local() }) doRNG/tests/testthat.R0000644000176200001440000000006613556341614014362 0ustar liggesuserslibrary(testthat) library(doRNG) test_check("doRNG") doRNG/vignettes/0000755000176200001440000000000014360272312013233 5ustar liggesusersdoRNG/vignettes/doRNG.Rnw0000644000176200001440000005003513556341614014707 0ustar liggesusers% \documentclass[a4paper,12pt]{article} %\VignetteIndexEntry{Using the package doRNG} %\VignetteDepends{doRNG,doParallel,knitr,doRedis,rbenchmark} %\VignetteCompiler{knitr} %\VignetteEngine{knitr::knitr} \usepackage{a4wide} \usepackage{xspace} \usepackage[colorlinks]{hyperref} % for hyperlinks \usepackage{tocloft} \renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}} \usepackage[toc]{multitoc} % add preamble from pkgmaker <>= pkgmaker::latex_preamble() @ % REFERENCES \usepackage[minnames=1,maxnames=2,backend=bibtex]{biblatex} \AtEveryCitekey{\clearfield{url}} <>= pkgmaker::latex_bibliography('doRNG') @ \newcommand{\citet}[1]{\citeauthor{#1}~\cite{#1}} %% \newcommand{\graphwidth}{0.9\columnwidth} % clever references \usepackage[noabbrev, capitalise, nameinlink]{cleveref} \newcommand{\dorng}{\code{\%dorng\%}\xspace} \title{Using the \code{doRNG} package\\ {\small \Rpkg{doRNG} -- Version \Sexpr{packageVersion('doRNG')}}} \author{Renaud Gaujoux} \begin{document} \maketitle \tableofcontents \section*{Introduction} \addcontentsline{toc}{section}{Introduction} Research reproducibility is an issue of concern, e.g. in bioinformatics \cite{Hothorn2011,Stodden2011,Ioannidis2008}. Some analyses require multiple independent runs to be performed, or are amenable to a split-and-reduce scheme. For example, some optimisation algorithms are run multiple times from different random starting points, and the result that achieves the least approximation error is selected. The \citeCRANpkg{foreach} provides a very convenient way to perform parallel computations, with different parallel environments such as MPI or Redis, using a transparent loop-like syntax: <>= options(width=90) library(pkgmaker) library(knitr) opts_chunk$set(size = "footnotesize") knit_hooks$set(try = pkgmaker::hook_try) @ <>= # load and register parallel backend for multicore computations library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) # perform 5 tasks in parallel x <- foreach(i=1:5) %dopar% { i + runif(1) } unlist(x) @ For each parallel environment a \emph{backend} is implemented as a specialised \code{\%dopar\%} operator, which performs the setup and pre/post-processing specifically required by the environment (e.g. export of variable to each worker). The \code{foreach} function and the \code{\%dopar\%} operator handle the generic parameter dispatch when the task are split between worker processes, as well as the reduce step -- when the results are returned to the master worker. When stochastic computations are involved, special random number generators must be used to ensure that the separate computations are indeed statistically independent -- unless otherwise wanted -- and that the loop is reproducible. In particular, standard \code{\%dopar\%} loops are not reproducible: <>= # with standard %dopar%: foreach loops are not reproducible set.seed(123) res <- foreach(i=1:5) %dopar% { runif(3) } set.seed(123) res2 <- foreach(i=1:5) %dopar% { runif(3) } identical(res, res2) @ A random number generator commonly used to achieve reproducibility is the combined multiple-recursive generator from \citet{Lecuyer1999}. This generator can generate independent random streams, from a 6-length numeric seed. The idea is then to generate a sequence of random stream of the same length as the number of iteration (i.e. tasks) and use a different stream when computing each one of them. The \citeCRANpkg{doRNG} provides convenient ways to implement reproducible parallel \code{foreach} loops, independently of the parallel backend used to perform the computation. We illustrate its use, showing how non-reproducible loops can be made reproducible, even when tasks are not scheduled in the same way in two separate set of runs, e.g. when the workers do not get to compute the same number of tasks or the number of workers is different. The package has been tested with the \CRANpkg*{doParallel} and \CRANpkg*{doMPI} packages \citepkg{Rpackage:doMPI,Rpackage:doParallel}, but should work with other backends such as provided by the \citeCRANpkg{doRedis}. \section{The \texttt{\%dorng\%} operator} The \Rpkg{doRNG} defines a new generic operator, \code{\%dorng\%}, to be used with foreach loops, instead of the standard {\%dopar\%}. Loops that use this operator are \emph{de facto} reproducible. <>= # load the doRNG package library(doRNG) # using %dorng%: loops _are_ reproducible set.seed(123) res <- foreach(i=1:5) %dorng% { runif(3) } set.seed(123) res2 <- foreach(i=1:5) %dorng% { runif(3) } identical(res, res2) @ \subsection{How it works} For a loop with $N$ iterations, the \code{\%dorng\%} operator internally performs the following tasks: \begin{enumerate} \item generate a sequence of random seeds $(S_i)_{1\leq i\leq N}$ for the \proglang{R} random number generator \code{"L'Ecuyer-CMRG"} \cite{Lecuyer1999}, using the function \code{nextRNGStream} from the \citeCRANpkg{parallel}, which ensure the different RNG streams are statistically independent; \item modify the loop's \proglang{R} expression so that the random number generator is set to \code{"L'Ecuyer-CMRG"} at the beginning of each iteration, and is seeded with consecutive seeds in $(S_n)$: iteration $i$ is seeded with $S_i$, $1\leq i\leq N$; \item call the standard \code{\%dopar\%} operator, which in turn calls the relevant (i.e. registered) foreach parallel backend; \item store the whole sequence of random seeds as an attribute in the result object: <>= attr(res, 'rng') @ \end{enumerate} \subsection{Seeding computations} Sequences of random streams for \code{"L'Ecuyer-CMRG"} are generated using a 6-length integer seed, e.g.,: <>= nextRNGStream(c(407L, 1:6)) @ However, the \code{\%dorng\%} operator provides alternative -- convenient -- ways of seeding reproducible loops. \begin{description} \item[\code{set.seed}:] as shown above, calling \code{set.seed} before the loop ensure reproducibility of the results, using a single integer as a seed. The actual 6-length seed is then generated with an internal call to \code{RNGkind("L'Ecuyer-CMRG")}. \item[\code{.options.RNG} with single integer:] the \dorng operator support options that can be passed in the \code{foreach} statement, containing arguments for the internal call to \code{set.seed}: <>= # use a single numeric as a seed s <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } s2 <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } identical(s, s2) @ \noindent \textbf{Note}: calling \code{set.seed} before the loop is equivalent to passing the seed in \code{.options.RNG}. See \cref{sec:set_seed} for more details. \medskip The kind of Normal generator may also be passed in \code{.options.RNG}: <>= ## Pass the Normal RNG kind to use within the loop # results are identical if not using the Normal kind in the loop optsN <- list(123, normal.kind="Ahrens") resN.U <- foreach(i=1:5, .options.RNG=optsN) %dorng% { runif(3) } identical(resN.U[1:5], res[1:5]) # Results are different if the Normal kind is used and is not the same resN <- foreach(i=1:5, .options.RNG=123) %dorng% { rnorm(3) } resN1 <- foreach(i=1:5, .options.RNG=optsN) %dorng% { rnorm(3) } resN2 <- foreach(i=1:5, .options.RNG=optsN) %dorng% { rnorm(3) } identical(resN[1:5], resN1[1:5]) identical(resN1[1:5], resN2[1:5]) @ \item[\code{.options.RNG} with 6-length:] the actual 6-length integer seed used for the first RNG stream may be passed via \code{options.RNG}: <>= # use a 6-length numeric s <- foreach(i=1:5, .options.RNG=1:6) %dorng% { runif(3) } attr(s, 'rng')[1:3] @ \item[\code{.options.RNG} with 7-length:] a 7-length integer seed may also be passed via \code{options.RNG}, which is useful to seed a loop with the value of \code{.Random.seed} as used in some iteration of another loop\footnote{Note that the RNG kind is then always required to be the \code{"L'Ecuyer-CMRG"}, i.e. the first element of the seed must have unit 7 (e.g. 407 or 107).}: <>= # use a 7-length numeric, used as first value for .Random.seed seed <- attr(res, 'rng')[[2]] s <- foreach(i=1:5, .options.RNG=seed) %dorng% { runif(3) } identical(s[1:4], res[2:5]) @ \item[\code{.options.RNG} with complete sequence of seeds:] the complete description of the sequence of seeds to be used may be passed via \code{options.RNG}, as a list or a matrix with the seeds in columns. This is useful to seed a loop exactly as desired, e.g. using an RNG other than \code{"L'Ecuyer-CMRG"}, or using different RNG kinds in each iteration, which probably have different seed length, in order to compare their stochastic properties. It also allows to reproduce \code{\%dorng\%} loops without knowing their seeding details: <>= # reproduce previous %dorng% loop s <- foreach(i=1:5, .options.RNG=res) %dorng% { runif(3) } identical(s, res) ## use completely custom sequence of seeds (e.g. using RNG "Marsaglia-Multicarry") # as a matrix seedM <- rbind(rep(401, 5), mapply(rep, 1:5, 2)) seedM sM <- foreach(i=1:5, .options.RNG=seedM) %dorng% { runif(3) } # same seeds passed as a list seedL <- lapply(seq(ncol(seedM)), function(i) seedM[,i]) sL <- foreach(i=1:5, .options.RNG=seedL) %dorng% { runif(3) } identical(sL, sM) @ \end{description} \subsection{Difference between \texttt{set.seed} and \texttt{.options.RNG}} \label{sec:set_seed} While it is equivalent to seed \dorng loops with \code{set.seed} and \code{.options.RNG}, it is important to note that the result depends on the current RNG kind \footnote{See \cref{sec:issues} about a bug in versions < 1.4 on this feature.}: <>= # default RNG kind RNGkind('default') def <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } # Marsaglia-Multicarry RNGkind('Marsaglia') mars <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } identical(def, mars) # revert to default RNG kind RNGkind('default') @ This is a ``normal'' behaviour, which is a side-effect of the expected equivalence between \code{set.seed} and \code{.options.RNG}. This should not be a problem for reproducibility though, as R RNGs are stable across versions, and loops are most of the time used with the default RNG settings. In order to ensure seeding is independent from the current RNG, one has to pass a 7-length numeric seed to \code{.options.RNG}, which is then used directly as a value for \code{.Random.seed} (see below). \section{Parallel environment independence} An important feature of \code{\%dorng\%} loops is that their result is independent of the underlying parallel physical settings. Two separate runs seeded with the same value will always produce the same results. Whether they use the same number of worker processes, parallel backend or task scheduling does not influence the final result. This also applies to computations performed sequentially with the \code{doSEQ} backend. The following code illustrates this feature using 2 or 3 workers. <>= # define a stochastic task to perform task <- function() c(pid=Sys.getpid(), val=runif(1)) # using the previously registered cluster with 2 workers set.seed(123) res_2workers <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # stop cluster stopCluster(cl) # Sequential computation registerDoSEQ() set.seed(123) res_seq <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # # Using 3 workers # NB: if re-running this vignette you should edit to force using 3 here cl <- makeCluster( if(isManualVignette()) 3 else 2) length(cl) # register new cluster registerDoParallel(cl) set.seed(123) res_3workers <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # task schedule is different pid <- rbind(res1=res_seq[,1], res_2workers[,1], res2=res_3workers[,1]) storage.mode(pid) <- 'integer' pid # results are identical identical(res_seq[,2], res_2workers[,2]) && identical(res_2workers[,2], res_3workers[,2]) @ \section{Reproducible \texttt{\%dopar\%} loops} The \Rpkg{doRNG} also provides a non-invasive way to convert \code{\%dopar\%} loops into reproducible loops, i.e. without changing their actual definition. It is useful to quickly ensure the reproducibility of existing code or functions whose definition is not accessible (e.g. from other packages). This is achieved by registering the \code{doRNG} backend: <>= set.seed(123) res <- foreach(i=1:5) %dorng% { runif(3) } registerDoRNG(123) res_dopar <- foreach(i=1:5) %dopar% { runif(3) } identical(res_dopar, res) attr(res_dopar, 'rng') @ \section{Reproducibile sets of loops} Sequences of multiple loops are reproducible, whether using the \code{\%dorng\%} operator or the registered \code{doRNG} backend: <>= set.seed(456) s1 <- foreach(i=1:5) %dorng% { runif(3) } s2 <- foreach(i=1:5) %dorng% { runif(3) } # the two loops do not use the same streams: different results identical(s1, s2) # but the sequence of loops is reproducible as a whole set.seed(456) r1 <- foreach(i=1:5) %dorng% { runif(3) } r2 <- foreach(i=1:5) %dorng% { runif(3) } identical(r1, s1) && identical(r2, s2) # one can equivalently register the doRNG backend and use %dopar% registerDoRNG(456) r1 <- foreach(i=1:5) %dopar% { runif(3) } r2 <- foreach(i=1:5) %dopar% { runif(3) } identical(r1, s1) && identical(r2, s2) @ \section{Nested and conditional loops} \label{sec:nested} Nested and conditional foreach loops are currently not supported and generate an error: <>= # nested loop try( foreach(i=1:10) %:% foreach(j=1:i) %dorng% { rnorm(1) } ) # conditional loop try( foreach(i=1:10) %:% when(i %% 2 == 0) %dorng% { rnorm(1) } ) @ In this section, we propose a general work around for this kind of loops, that will eventually be incorporated in the \code{\%dorng\%} operator -- when I find out how to mimic its behaviour from the operator itself. \subsection{Nested loops} The idea is to create a sequence of RNG seeds before the outer loop, and use each of them successively to set the RNG in the inner loop -- which is exactly what \code{\%dorng\%} does for simple loops: <>= # doRNG must not be registered registerDoParallel(cl) # generate sequence of seeds of length the number of computations n <- 10; p <- 5 rng <- RNGseq( n * p, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% foreach(j=1:p, r=rng[(i-1)*p + 1:p]) %dopar% { # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 res2 <- foreach(i=1:n) %:% foreach(j=1:p) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(res, res2) ) @ The following is a more complex example with unequal -- but \textbf{known \emph{a priori}} -- numbers of iterations performed in the inner loops: <>= # generate sequence of seeds of length the number of computations n <- 10 rng <- RNGseq( n * (n+1) / 2, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% foreach(j=1:i, r=rng[(i-1)*i/2 + 1:i]) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 res2 <- foreach(i=1:n) %:% foreach(j=1:i) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(res, res2) ) @ \subsection{Conditional loops} The work around used for nested loops applies to conditional loops that use the \code{when()} clause. It ensures that the RNG seed use for a given inner iteration does not depend on the filter, but only on its index in the unconditional-unfolded loop: <>= # un-conditional single loop resAll <- foreach(i=1:n, .options.RNG=1234) %dorng%{ # do your own computation ... c(i, rnorm(1)) } # generate sequence of RNG rng <- RNGseq(n, 1234) # conditional loop: even iterations resEven <- foreach(i=1:n, r=rng) %:% when(i %% 2 == 0) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # conditional loop: odd iterations resOdd <- foreach(i=1:n, r=rng) %:% when(i %% 2 == 1) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # conditional loop: only first 2 and last 2 resFL <- foreach(i=1:n, r=rng) %:% when(i %in% c(1,2,n-1,n)) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # compare results stopifnot( identical(resAll[seq(2,n,by=2)], resEven) ) stopifnot( identical(resAll[seq(1,n,by=2)], resOdd) ) stopifnot( identical(resAll[c(1,2,n-1,n)], resFL) ) @ \subsection{Nested conditional loops} Conditional nested loops may use the same work around, as shown in this intricate example: <>= # generate sequence of seeds of length the number of computations n <- 10 rng <- RNGseq( n * (n+1) / 2, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% when(i %% 2 == 0) %:% foreach(j=1:i, r=rng[(i-1)*i/2 + 1:i]) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 resAll <- foreach(i=1:n) %:% foreach(j=1:i) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(resAll[seq(2,n,by=2)], res) ) @ \section{Performance overhead} The extra setup performed by the \code{\%dorng\%} operator leads to a slight performance over-head, which might be significant for very quick computations, but should not be a problem for realistic computations. The benchmarks below show that a \code{\%dorng\%} loop may take up to two seconds more than the equivalent \code{\%dopar\%} loop, which is not significant in practice, where parallelised computations typically take several minutes. <>= # load rbenchmark library(rbenchmark) # comparison is done on sequential computations registerDoSEQ() rPar <- function(n, s=0){ foreach(i=1:n) %dopar% { Sys.sleep(s) } } rRNG <- function(n, s=0){ foreach(i=1:n) %dorng% { Sys.sleep(s) } } # run benchmark cmp <- benchmark(rPar(10), rRNG(10) , rPar(25), rRNG(25) , rPar(50), rRNG(50) , rPar(50, .01), rRNG(50, .01) , rPar(10, .05), rRNG(10, .05) , replications=5) # order by increasing elapsed time cmp[order(cmp$elapsed), ] @ \section{Known issues} \label{sec:issues} \begin{itemize} \item Nested and/or conditional foreach loops using the operator \code{\%:\%} are not currently not supported (see \cref{sec:nested} for a work around). \item An error is thrown in \code{doRNG} 1.2.6, when the package \code{iterators} was not loaded, when used with \code{foreach} >= 1.4. \item There was a bug in versions prior to \code{1.4}, which caused \code{set.seed} and \code{.options.RNG} not to be equivalent when the current RNG was \code{"L'Ecuyer-CMRG"}. This behaviour can still be reproduced by setting: <>= doRNGversion('1.3') @ To revert to the latest default behaviour: <>= doRNGversion(NULL) @ \end{itemize} \section{News and changes} {\scriptsize \begin{verbatim} <>= cat(paste(readLines(system.file('NEWS', package='doRNG')), collapse="\n")) @ \end{verbatim} } \section*{Cleanup} <>= stopCluster(cl) @ \section*{Session information} \addcontentsline{toc}{section}{Session information} <>= sessionInfo() @ \printbibliography[heading=bibintoc] \end{document} doRNG/R/0000755000176200001440000000000014360256113011425 5ustar liggesusersdoRNG/R/utils.R0000644000176200001440000000054513612460574012723 0ustar liggesusers # from pkgmaker 0.31 ns_get <- function (x, ns = NULL, ...){ if (is.null(ns)) { ns <- gsub("^([^:]+)::.*", "\\1", x) x <- gsub(".*::([^:]+)$", "\\1", x) } if (!isNamespace(ns)) { ns <- tryCatch(asNamespace(ns), error = function(e) NULL) if (is.null(ns)) return() } get0(x, envir = ns, ...) }doRNG/R/doRNG-package.R0000644000176200001440000000500614360256113014113 0ustar liggesusers#' The \emph{doRNG} package provides functions to perform #' reproducible parallel foreach loops, using independent random streams #' as generated by L'Ecuyer's combined multiple-recursive generator \citep{Lecuyer1999}. #' It enables to easily convert standard %dopar% loops into fully reproducible loops, #' independently of the number of workers, the task scheduling strategy, #' or the chosen parallel environment and associated foreach backend. #' It has been tested with the following foreach backend: doMC, doSNOW, doMPI. #' #' @encoding UTF-8 #' @name doRNG-package #' @docType package #' @title Generic Reproducible Parallel Backend for foreach Loops #' @keywords package #' @seealso \code{\link{doRNG}}, \code{\link{RNGseq}} #' @bibliography ~/Documents/articles/library.bib #' @cite Lecuyer1999 #' #' @import stats rngtools foreach #' @examples #' #' # register parallel backend #' library(doParallel) #' cl <- makeCluster(2) #' registerDoParallel(cl) #' #' ## standard %dopar% loop are not reproducible #' set.seed(123) #' r1 <- foreach(i=1:4) %dopar%{ runif(1) } #' set.seed(123) #' r2 <- foreach(i=1:4) %dopar%{ runif(1) } #' identical(r1, r2) #' \dontshow{ stopifnot(!identical(r1, r2)) } #' #' ## %dorng% loops _are_ reproducible #' set.seed(123) #' r1 <- foreach(i=1:4) %dorng%{ runif(1) } #' set.seed(123) #' r2 <- foreach(i=1:4) %dorng%{ runif(1) } #' identical(r1, r2) #' \dontshow{ stopifnot(identical(r1, r2)) } #' #' # alternative way of seeding #' a1 <- foreach(i=1:4, .options.RNG=123) %dorng%{ runif(1) } #' a2 <- foreach(i=1:4, .options.RNG=123) %dorng%{ runif(1) } #' identical(a1, a2) && identical(a1, r1) #' \dontshow{ stopifnot(identical(a1, a2) && identical(a1, r1)) } #' #' ## sequences of %dorng% loops _are_ reproducible #' set.seed(123) #' s1 <- foreach(i=1:4) %dorng%{ runif(1) } #' s2 <- foreach(i=1:4) %dorng%{ runif(1) } #' identical(s1, r1) && !identical(s1, s2) #' \dontshow{ stopifnot(identical(s1, r1) && !identical(s1, s2)) } #' #' set.seed(123) #' s1.2 <- foreach(i=1:4) %dorng%{ runif(1) } #' s2.2 <- foreach(i=1:4) %dorng%{ runif(1) } #' identical(s1, s1.2) && identical(s2, s2.2) #' \dontshow{ stopifnot(identical(s1, s1.2) && identical(s2, s2.2)) } #' #' ## Non-invasive way of converting %dopar% loops into reproducible loops #' registerDoRNG(123) #' s3 <- foreach(i=1:4) %dopar%{ runif(1) } #' s4 <- foreach(i=1:4) %dopar%{ runif(1) } #' identical(s3, s1) && identical(s4, s2) #' \dontshow{ stopifnot(identical(s3, s1) && identical(s4, s2)) } #' #' stopCluster(cl) #' NULL `_PACKAGE` <- "doRNG" doRNG/R/doRNG.R0000644000176200001440000004621214360233461012527 0ustar liggesusers# Development of a dorng equivalent to dopar for reproducible loops # # Author: Renaud Gaujoux # Creation: 17 Aug 2011 ############################################################################### #library(foreach) # or-NULL operator (borrowed from Hadley Wickham) '%||%' <- function(x, y) if( !is.null(x) ) x else y #' @importFrom utils head .collapse <- function(x, n=length(x), sep=', '){ res <- paste(if( missing(n) ) x else head(x, n), collapse=', ') if( length(x) > n ) res <- paste(res, '...', sep=', ') res } #' Back Compatibility Option for doRNG #' #' Sets the behaviour of %dorng% foreach loops from a #' given version number. #' #' @section Behaviour changes in versions: #' #' \describe{ #' \item{1.4}{ The behaviour of \code{doRNGseed}, and therefore of #' `%dorng%` loops, changed in the case where the current RNG was #' L'Ecuyer-CMRG. #' Using \code{set.seed} before a non-seeded loop used not to be identical #' to seeding via \code{.options.RNG}. #' Another bug was that non-seeded loops would share most of their RNG seed! #' } #' \item{1.7.4}{Prior to this version, in the case where the RNG had not been called yet, #' the first seeded `%dorng%` loops would not give the identical results as #' subsequent loops despite using the same seed #' (see \url{https://github.com/renozao/doRNG/issues/12}). #' #' This has been fixed in version 1.7.4, where the RNG is called once (\code{sample(NA)}), #' whenever the .Random.seed is not found in global environment. #' } #' } #' #' @param x version number to switch to, or missing to get the currently #' active version number, or \code{NULL} to reset to the default behaviour, #' i.e. of the latest version. #' #' @return a character string #' If \code{x} is missing this function returns the version number from the #' current behaviour. #' If \code{x} is specified, the function returns the old value of the #' version number (invisible). #' #' @importFrom utils packageVersion #' @export #' @examples #' #' \dontshow{ registerDoSEQ() } #' #' ## Seeding when current RNG is L'Ecuyer-CMRG #' RNGkind("L'Ecuyer") #' #' doRNGversion("1.4") #' # in version >= 1.4 seeding behaviour changed to fix a bug #' set.seed(123) #' res <- foreach(i=1:3) %dorng% runif(1) #' res2 <- foreach(i=1:3) %dorng% runif(1) #' stopifnot( !identical(attr(res, 'rng')[2:3], attr(res2, 'rng')[1:2]) ) #' res3 <- foreach(i=1:3, .options.RNG=123) %dorng% runif(1) #' stopifnot( identical(res, res3) ) #' #' # buggy behaviour in version < 1.4 #' doRNGversion("1.3") #' res <- foreach(i=1:3) %dorng% runif(1) #' res2 <- foreach(i=1:3) %dorng% runif(1) #' stopifnot( identical(attr(res, 'rng')[2:3], attr(res2, 'rng')[1:2]) ) #' res3 <- foreach(i=1:3, .options.RNG=123) %dorng% runif(1) #' stopifnot( !identical(res, res3) ) #' #' # restore default RNG #' RNGkind("default") #' # restore to current doRNG version #' doRNGversion(NULL) #' doRNGversion <- local({ currentV <- "1.7.4" #as.character(packageVersion('doRNG')) cache <- currentV function(x){ if( missing(x) ) return(cache) if( is.null(x) ) x <- currentV # update cache and return old value old <- cache cache <<- x invisible(old) } }) #' @importFrom utils compareVersion checkRNGversion <- function(x){ compareVersion(doRNGversion(), x) } doRNGseq <- function(n, seed=NULL, ...){ # compute sequence using rngtools::RNGseq # library(rngtools) res <- RNGseq(n, seed, ..., version=if( checkRNGversion('1.4') >=0 ) 2 else 1, simplify = FALSE) } #' Getting Information About doRNG Foreach Backend #' #' \code{infoDoRNG} returns information about the doRNG backend, e.g., version, #' number of workers. #' It is not meant to be called by the user. #' #' #' @param data a list of data used by the backend #' @param item the data item requested, as a character string #' (e.g. 'name', 'workers', 'version') #' #' @return \code{infoDoRNG} returns the requested info (usually as a character #' string or a numeric value). #' #' @keywords internal #' @author Renaud Gaujoux #' infoDoRNG <- function (data, item) { switch(item , workers = data$backend$info(data$backend$data, "workers") , name = "doRNG" , version = "doRNG 1.7.3" , NULL) } #' @describeIn infoDoRNG implements the generic reproducible foreach backend. It should #' not be called directly by the user. #' #' @param obj a foreach description of the loop arguments #' @param ex the lopp expression #' @param envir the loop's evaluation environment #' @param data configuration data of the doRNG backend #' doRNG <- function (obj, ex, envir, data){ if( is.null(obj$options) ) obj$options <- list() if( !'RNG' %in% names(obj$options) ){ obj$options$RNG <- if( !data$once || data$nseed==0 ){ #message("doRNG backend - use seed ", if( data$once ) "only once" else "for every loop", ":") data$seed } else NULL } # data$nseed <- data$nseed + 1 # assign('data', data, pos=foreach:::.foreachGlobals) rngBackend <- getDoBackend() # increment number of calls to doRNG rngBackend$data$nseed <- rngBackend$data$nseed + 1 # directly register (temporarly) the computing backend on.exit({setDoBackend(rngBackend)}, add=TRUE) setDoBackend(rngBackend$data$backend) do.call(doRNG::`%dorng%`, list(obj, ex), envir = envir) } ##% Get/Sets the registered foreach backend's data getDoBackend <- function(){ # one has to get the complete set of backend data from within the foreach Namespace foreach_ns <- asNamespace('foreach') # .foreachGlobals <- get('.foreachGlobals', foreach_ns) .foreachGlobals <- ns_get('.foreachGlobals', foreach_ns) # getDoPar <- get('getDoPar', foreach_ns) getDoPar <- ns_get('getDoPar', foreach_ns) c(getDoPar() , info= if( exists("info", where = .foreachGlobals, inherits = FALSE) ) .foreachGlobals$info else function(data, item) NULL) } setDoBackend <- function(backend){ ob <- getDoBackend() do.call(setDoPar, backend) invisible(ob) } .getDoParName <- function(backend = getDoBackend(), version = FALSE) { if ( !is.null(backend[['info']]) ){ res <- backend[['info']](backend[['data']], "name") if( version ) paste0(res, '(', backend[['info']](backend[['data']], "version"), ')') res } } #' Reproducible Parallel Foreach Backend #' #' `%dorng%` is a foreach operator that provides an alternative operator #' `%dopar%`, which enable reproducible foreach loops to be performed. #' #' @param obj a foreach object as returned by a call to \code{\link{foreach}}. #' @param ex the \code{R} expression to evaluate. #' #' @return `%dorng%` returns the result of the foreach loop. See [foreach::%dopar%]. #' The whole sequence of RNG seeds is stored in the result object as an attribute. #' Use \code{attr(res, 'rng')} to retrieve it. #' #' @section Global options: #' #' These options are for advanced users that develop `foreach backends: #' #' * 'doRNG.rng_change_warning_skip': if set to a single logical `FALSE/TRUE`, it indicates #' whether a warning should be thrown if the RNG seed is changed by the registered #' parallel backend (default=FALSE). #' Set it to `TRUE` if you know that running your backend will change the RNG state and #' want to disable the warning. #' This option can also be set to a character vector that specifies the name(s) of the backend(s) #' for which the warning should be skipped. #' #' @importFrom iterators iter #' @export #' @usage obj \%dorng\% ex #' @seealso \code{\link{foreach}}, \code{\link[doParallel]{doParallel}} #' , \code{\link[doParallel]{registerDoParallel}}, \code{\link[doMPI]{doMPI}} #' @examples #' #' library(doParallel) #' cl <- makeCluster(2) #' registerDoParallel(cl) #' #' # standard %dopar% loops are _not_ reproducible #' set.seed(1234) #' s1 <- foreach(i=1:4) %dopar% { runif(1) } #' set.seed(1234) #' s2 <- foreach(i=1:4) %dopar% { runif(1) } #' identical(s1, s2) #' #' # single %dorng% loops are reproducible #' r1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' r2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' identical(r1, r2) #' # the sequence os RNG seed is stored as an attribute #' attr(r1, 'rng') #' #' # stop cluster #' stopCluster(cl) #' #' # More examples can be found in demo `doRNG` #' \dontrun{ #' demo('doRNG') #' } #' #' @demo Some features of the %dorng% foreach operator #' #' library(doRNG) #' library(doParallel) #' #' if( .Platform$OS.type == "unix" ){ #' registerDoParallel(2) #' #' # single %dorng% loops are reproducible #' r1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' r2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' identical(r1, r2) #' # the sequence os RNG seed is stored as an attribute #' attr(r1, 'rng') #' #' # sequences of %dorng% loops are reproducible #' set.seed(1234) #' s1 <- foreach(i=1:4) %dorng% { runif(1) } #' s2 <- foreach(i=1:4) %dorng% { runif(1) } #' # two consecutive (unseed) %dorng% loops are not identical #' identical(s1, s2) #' #' # But the whole sequence of loops is reproducible #' set.seed(1234) #' s1.2 <- foreach(i=1:4) %dorng% { runif(1) } #' s2.2 <- foreach(i=1:4) %dorng% { runif(1) } #' identical(s1, s1.2) && identical(s2, s2.2) #' # it gives the same result as with .options.RNG #' identical(r1, s1) #' #' } #' #' # Works with SNOW-like and MPI clusters #' # SNOW-like cluster #' cl <- makeCluster(2) #' registerDoParallel(cl) #' #' s1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' s2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' identical(s1, s2) #' #' stopCluster(cl) #' registerDoSEQ() #' #' # MPI cluster #' library(doMPI) #' cl <- startMPIcluster(2) #' registerDoMPI(cl) #' #' s1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' s2 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' identical(s1, s2) #' #' closeCluster(cl) #' registerDoSEQ() #' #' `%dorng%` <- function(obj, ex){ #library(rngtools) # str(obj) # dump verbose messages if not in verbose mode verbose <- !is.null(obj$verbose) && obj$verbose if( !verbose ) message <- function(...) NULL # exit if nested or conditional loop if( any(c('xforeach', 'filteredforeach') %in% class(obj)) ) stop("nested/conditional foreach loops are not supported yet.\nSee the package's vignette for a work around.") # if an RNG seed is provided then setup random streams # and add the list of RNGs to use as an iterated arguments for %dopar% # library(parallel) N_elem <- length(as.list(iter(obj))) obj$argnames <- c(obj$argnames, '.doRNG.stream') obj$args$.doRNG.stream <- rep(NA_integer_, N_elem) # make sure the RNG seed is initialized by calling getRNG() if( is.null(RNGseed()) ){ if( checkRNGversion("1.7.4") >= 0 ){ message("NOTE -- .Random.seed is not initialized: sampling once to ensure reproducibility.") getRNG() }else{ warning(paste0(".Random.seed is not initialized: results might not be reproducible.\n ", "Update to doRNG version >= 1.7.4 to get a fix for this issue.")) } } ## # restore current RNG on exit if a seed is passed rngSeed <- if( !is.null(obj$options$RNG) ){ # setup current RNG restoration RNG.old <- RNGseed() on.exit({RNGseed(RNG.old)}, add=TRUE) # extract RNG setting from object if possible (do not resolve single seed) rngSeed <- getRNG(obj$options$RNG, num.ok=TRUE) %||% obj$options$RNG # ensure it is a list # NB: unnamed lists are sequences of seeds if( !is.list(rngSeed) || is.null(names(rngSeed)) ){ rngSeed <- list(rngSeed) } rngSeed } # message("* Seed specification: ", str_out(rngSeed, 6, total = length(rngSeed) > 6)) # generate a sequence of streams # print("before RNGseq") # showRNG() obj$args$.doRNG.stream <- do.call(doRNGseq, c(list(n=N_elem, verbose=obj$verbose), rngSeed)) # print("after RNGseq") # showRNG() #print(obj$args$.doRNG.stream) message("* Registered backend: ", .getDoParName(version = TRUE)) dp <- getDoParName() # directly register (temporarly) the computing backend if( !is.null(dp) && dp == 'doRNG' ){ rngBackend <- getDoBackend() message("* Registering computing backend: ", .getDoParName(rngBackend$data$backend, version = TRUE)) on.exit({ message("* Restoring previous backend: ", .getDoParName(rngBackend)) setDoBackend(rngBackend) }, add=TRUE) setDoBackend(rngBackend$data$backend) dp <- getDoParName() } ## SEPCIAL CASE FOR doSEQ or doMPI # TODO: figure out why doMPI draws once from the current RNG (must be linked # to using own code to setup L'Ecuyer RNG) # restore RNG settings as after RNGseq if doSEQ is the backend and no seed was passed if( is.null(obj$options$RNG) ){ RNG.old <- RNGseed() on.exit({ rng_type_changed <- !identical(RNGtype(), RNGtype(RNG.old)) warning_skip <- getOption("doRNG.rng_change_warning_skip", FALSE) force_warning <- getOption("doRNG.rng_change_warning_force", FALSE) known_changing_cases <- is.null(dp) || dp %in% c("doSEQ", "doMPI") || (is.logical(warning_skip) && !is.na(warning_skip) && warning_skip) || (is.character(warning_skip) && dp %in% warning_skip) if( known_changing_cases || rng_type_changed ){ if( force_warning || (rng_type_changed && !known_changing_cases) ){ warning(sprintf("Foreach loop (%s) had changed the current RNG type: RNG was restored to same type, next state", dp %||% "unknown")) }else{ message("* Detected known RNG side effect: ", dp) } message("* Restoring RNG as after RNG sequence generation") if( verbose ) showRNG(RNG.old, indent = " -") RNGseed(RNG.old) message("OK") } }, add=TRUE) } ## # export package doRNG if not already exported if( !('doRNG' %in% obj$packages) ) obj$packages <- c(obj$packages, 'doRNG') # append code to the loop expression to set the RNG ex <- as.call(list(as.name('{'), quote({rngtools::RNGseed(.doRNG.stream);}), substitute(ex))) # call the standard %dopar% operator res <- do.call(`%dopar%`, list(obj, ex), envir=parent.frame()) # add seed sequence as an attribute (skip this for NULL results) if( !is.null(res) ){ attr(res, 'rng') <- obj$args$.doRNG.stream attr(res, 'doRNG_version') <- doRNGversion() } # return result res } #' Registering doRNG for Persistent Reproducible Parallel Foreach Loops #' #' \code{registerDoRNG} registers the doRNG foreach backend. #' Subsequent `%dopar%` loops are then performed using the previously #' registered foreach backend, but are internally performed as [%dorng%] loops, #' making them fully reproducible. #' #' Briefly, the RNG is set, before each iteration, with seeds for L'Ecuyer's CMRG #' that overall generate a reproducible sequence of statistically independent #' random streams. #' #' Note that (re-)registering a foreach backend other than doRNG, after a call #' to \code{registerDoRNG} disables doRNG -- which then needs to be registered. #' #' @param seed a numerical seed to use (as a single or 6-length numerical value) #' @param once a logical to indicate if the RNG sequence should be seeded at the #' beginning of each loop or only at the first loop. #' #' @return The value returned by [foreach::setDoPar] #' #' @seealso [%dorng%] #' @export #' @examples #' #' library(doParallel) #' cl <- makeCluster(2) #' registerDoParallel(cl) #' #' # One can make reproducible loops using the %dorng% operator #' r1 <- foreach(i=1:4, .options.RNG=1234) %dorng% { runif(1) } #' # or convert %dopar% loops using registerDoRNG #' registerDoRNG(1234) #' r2 <- foreach(i=1:4) %dopar% { runif(1) } #' identical(r1, r2) #' stopCluster(cl) #' #' # Registering another foreach backend disables doRNG #' cl <- makeCluster(2) #' registerDoParallel(cl) #' set.seed(1234) #' s1 <- foreach(i=1:4) %dopar% { runif(1) } #' set.seed(1234) #' s2 <- foreach(i=1:4) %dopar% { runif(1) } #' identical(s1, s2) #' \dontshow{ stopifnot(!identical(s1, s2)) } #' #' # doRNG is re-nabled by re-registering it #' registerDoRNG() #' set.seed(1234) #' r3 <- foreach(i=1:4) %dopar% { runif(1) } #' identical(r2, r3) #' # NB: the results are identical independently of the task scheduling #' # (r2 used 2 nodes, while r3 used 3 nodes) #' #' # argument `once=FALSE` reseeds doRNG's seed at the beginning of each loop #' registerDoRNG(1234, once=FALSE) #' r1 <- foreach(i=1:4) %dopar% { runif(1) } #' r2 <- foreach(i=1:4) %dopar% { runif(1) } #' identical(r1, r2) #' #' # Once doRNG is registered the seed can also be passed as an option to %dopar% #' r1.2 <- foreach(i=1:4, .options.RNG=456) %dopar% { runif(1) } #' r2.2 <- foreach(i=1:4, .options.RNG=456) %dopar% { runif(1) } #' identical(r1.2, r2.2) && !identical(r1.2, r1) #' \dontshow{ stopifnot(identical(r1.2, r2.2) && !identical(r1.2, r1)) } #' #' stopCluster(cl) #' registerDoRNG <- function(seed=NULL, once=TRUE){ backend <- getDoBackend() # use stored backend if registerDoRNG was called repeatedly if( identical(getDoParName(), 'doRNG') ) backend <- backend$data$backend # set the current RNG with seed immediately if only used once if( once && !is.null(seed) ){ if( !is.numeric(seed) || length(seed)!=1L ) stop("Invalid seed: must be a single numeric value.") set.seed(seed) seed <- NULL } setDoPar(doRNG, list(seed=seed, once=once, nseed=0, backend=backend), infoDoRNG) } ###% Reproducibly Apply a Function over a List or Vector ###% ###% @aliases xapply reproduce ###% ###% \code{reproduce} and \code{xapply} are a reproducible versions ###% of \code{\link{replicate}} and \code{\link{sapply}} respectively, ###% that ensures the reproducibility of the results, when stochastic computations ###% are involved. ###% ###% The reproducibility is achieved by using LEcuyer's RNG provided by R core ###% since R-2.14.0, to generate independent random streams ###% that are used as the random number generator for each replicate. ###% ###% @param n the number of replication as a single numeric (integer) ###% @param seed the main numerical seed used to initialize the sequence of random ###% streams ###% @param expr the expression (language object, usually a call) to evaluate repeatedly ###% @param simplify logical; should the result be simplified to a vector or ###% matrix if possible? ###% ###% ###% #reproduce <- function (n, expr, seed=NULL, simplify = TRUE){ # f <- eval.parent(substitute(function(...) expr)) # xapply(integer(n), seed, f, simplify = simplify) #} # #xapply <- function (X, FUN, seed=NULL, ..., simplify = TRUE, USE.NAMES = TRUE){ # # # generate a sequence of streams # .RNG.stream <- RNGseq(length(X), seed, packed=TRUE) # # # keep current RNG and restore it on exit (useful for the sequential backend doSEQ) # RNG.old <- rstream.RNG() # on.exit(rstream.RNG(RNG.old), add=TRUE) # # # append code to the loop expression to set the RNG # expr <- as.call(list(as.name('{'), # quote({doRNGseed(.rng);}), # quote(do.call(FUN, list(...))))) # # env <- environment(FUN) # f <- eval(substitute(function(.rng, ..., FUN) expr), env) # mapply(f, .RNG.stream, X, MoreArgs=c(list(...), FUN=FUN), # SIMPLIFY = simplify, USE.NAMES= USE.NAMES) #} doRNG/NEWS.md0000644000176200001440000001072014360232665012330 0ustar liggesusers# Changes in version 1.8.4 There is no changes in this version, which was published to reclaim ownership and take the package out of ORPHANED state (issue #23). # Changes in version 1.8 ## Changes o Unit tests are now run through testthat o Minor fixes in man pages and README file o Now depends on rngtools >= 1.3 o The result list gains an attribute 'doRNG_version' that contains the version of doRNG that was used, based on doRNGversion(). NB: this is not necessarily the same as the version of the installed package. o Added the following global option 'doRNG.rng_change_warning_skip'. See ?`%dorng%` (issue #14). o Moved dependency on pkgmaker to Suggests to make installation lighter (issue #10). ## Bug fixes o Enabled running %dorng% loops within a package (incorporating the solution proposed by Elizabeth Byerly in PR#3) o Fixed error with NULL iteration results when setting 'rng' attribute (issue #9) o Fixed error when using unamed foreach arguments (issue #8) o Fixed non-reproducibility issue when the .Random.seed is not yet initialized, e.g., when the session starts and the RNG has not been used yet (issue #12) o Fixed runtime error when package is not attached (issue #13) # Changes in version 1.6.2 ## Bug fixes o Non reproducible %dorng% loop when doRNG is registered over doSEQ (Issue #1 reported by Brenton Kenkel). Actually due to %dorng% not restoring the RNG (to state + 1) when doRNG is registered over doSEQ. o %dorng% was not working properly on loops of length one (Issue #2) # Changes in version 1.6 ## Changes o doRNG now depends on the package pkgmaker (>= 0.20) ## Bug fixes o Check error due number of cores used. Now limited to 2 in examples, vignette and unit test. # Changes in version 1.5 ## Changes o doRNG now depends on the package pkgmaker (>= 0.9) o improved vignette o most of the general RNG utilities have been incorporated in a new package called rngtools. # Changes in version 1.4.1 ## Changes o when the current RNG was L'Ecuyer-CMRG, unseeded loops now use the current RNG stream as for the first stream in the RNG sequence and # Changes the current RNG to the next RNG stream of the last stream in the sequence. ## Bug fixes o fix error "'iter' not found" due to # Changes in foreach package dependencies -- that was announced by Rich Calaway. o loops seeded with set.seed and .options.RNG were not reproducible when current RNG was L'Ecuyer-CMRG (reported by Zhang Peng) o separate unseeded loops were sharing most of their streams, when current RNG was L'Ecuyer-CMRG the RNG seed. o nested/conditional loops were crashing with a bad error. They are still not supported but the error message is nicer and a work around has been added to the vignette (reported by Chanhee Yi and Zhang Peng). # Changes in version 1.2.3 ## Bug fixes o fixed error when running a %dorng% loop on a fresh session, with no parallel backend registered. ## Changes o improved vignette o added more unit tests o changed the name of the RNG attribute on result of %dorng% looops from 'RNG' to 'rng'. It now contains the whole sequence of RNG seeds, instead of only the first one. o RNGseq now accepts a list or a matrix describing the whole sequence of seeds. See vignette for more details. o %dorng% loops can be seeded with a complete sequence of seeds passed as a list, a matrix, or an object with attribute 'rng', e.g. the results of %dorng% loops. See vignette for more details. # Changes in version 1.2.2 ## Bug fixes o separate %dorng% loops were using the same seed. ## New features o add unit tests o first seed is set as an attribute of the loop's result ## Changes o function doRNGseed now returns the seed to use for the first iteration. o RNGseq now change the current RNG state if called with no seed specific. ## Defunct o removed function CMRGseed # Changes in version 1.2 ## Bug fixes o An error was thrown if using %dorng% loops before using any random generator. Thanks to Eric Lehmann for reporting this. ## Changes o add vignette o use package doParallel in examples # Changes in version 1.1 ## Changes o use R core RNG "L'Ecuyer-CMRG" and the parallel package, instead of the implementation provided by the rstream package. doRNG/MD50000644000176200001440000000227314361227063011543 0ustar liggesusersfc02fe29f44b58c518ec1f581cc66550 *DESCRIPTION 914736c637844ee4cc539cb363543c7a *NAMESPACE 8a66ba24ab8e03cee4fef43bf771e004 *NEWS.md c67a9d318dac7ce62b418bc2292e32e8 *R/doRNG-package.R 01bfea5d27934410581e132292d92ce4 *R/doRNG.R adaad203188938606a548fed7fb7982d *R/utils.R b36eab767d8fc4746b84b8e1e7b13e0a *build/vignette.rds 00e481a40fea9fb88bdd71c6e39fd332 *demo/00Index 3c7f2a369c28a79e7b45d8b4b0cda2d6 *demo/doRNG.R df50a05cd6bae38ad219a7146f68d147 *inst/REFERENCES.bib a64d2932a0800f4d39c4806c095876b2 *inst/doc/doRNG.R 01e38c562116701d37c64642b42cc41f *inst/doc/doRNG.Rnw 99a12084adb4b357cdc740b4d8568e6d *inst/doc/doRNG.pdf 82ab6f44b8492bb865476203eec00e19 *man/doRNG-package.Rd 96ef81fe164e010f76c2501714e1c1ed *man/doRNGversion.Rd ac2481cec965412e06b391194868845c *man/grapes-dorng-grapes.Rd 7ef8b4537de4e040468b4cb6df8ad53b *man/infoDoRNG.Rd 9c95362f6151e58b5dd40bb5767643cc *man/registerDoRNG.Rd afa2c0e8da402461ed281edb4f4e20f7 *tests/testthat.R 7978c192923a6be825d7a6d4cb9f2cea *tests/testthat/test-doRepro.r 0bc30863de386f57746f7cdd31a24d3d *tests/testthat/test-dorng-nonattached.r 979bef80cc9c2ac61f77720925044b42 *tests/testthat/test-dorng.r 01e38c562116701d37c64642b42cc41f *vignettes/doRNG.Rnw doRNG/inst/0000755000176200001440000000000014360272312012200 5ustar liggesusersdoRNG/inst/REFERENCES.bib0000644000176200001440000000713214360256116014306 0ustar liggesusers@Article{Hothorn2011, abstract = {Reproducible research is a concept of providing access to data and software along with published scientific findings. By means of some case studies from different disciplines, we will illustrate reasons why readers should be given the possibility to look at the data and software independently from the authors of the original publication. We report results of a survey comprising 100 papers recently published in Bioinformatics. The main finding is that authors of this journal share a culture of making data available. However, the number of papers where source code for simulation studies or analyzes is available is still rather limited.}, author = {Torsten Hothorn and Friedrich Leisch}, doi = {10.1093/bib/bbq084}, file = {::}, issn = {1477-4054}, journal = {Briefings in bioinformatics}, keywords = {reproducible research,software,statistical analyzes,sweave}, month = {jan}, pmid = {21278369}, title = {{Case studies in reproducibility.}}, url = {http://www.ncbi.nlm.nih.gov/pubmed/21278369}, year = {2011}, } @Misc{Stodden2011, author = {Victoria C Stodden}, booktitle = {AAAS Annual Meeting}, title = {{The Digitization of Science: Reproducibility and Interdisciplinary Knowledge Transfer}}, url = {http://aaas.confex.com/aaas/2011/webprogram/Session3166.html}, year = {2011}, } @Article{Ioannidis2008, abstract = {Given the complexity of microarray-based gene expression studies, guidelines encourage transparent design and public data availability. Several journals require public data deposition and several public databases exist. However, not all data are publicly available, and even when available, it is unknown whether the published results are reproducible by independent scientists. Here we evaluated the replication of data analyses in 18 articles on microarray-based gene expression profiling published in Nature Genetics in 20052006. One table or figure from each article was independently evaluated by two teams of analysts. We reproduced two analyses in principle and six partially or with some discrepancies; ten could not be reproduced. The main reason for failure to reproduce was data unavailability, and discrepancies were mostly due to incomplete data annotation or specification of data processing and analysis. Repeatability of published microarray studies is apparently limited. More strict publication rules enforcing public data availability and explicit description of data processing and analysis should be considered.}, author = {John P A Ioannidis and David B Allison and Catherine A Ball and Issa Coulibaly and Xiangqin Cui and Aed\'in C Culhane and Mario Falchi and Cesare Furlanello and Laurence Game and Giuseppe Jurman and Jon Mangion and Tapan Mehta and Michael Nitzberg and Grier P Page and Enrico Petretto and Vera {Van Noort}}, doi = {10.1038/ng.295}, issn = {10614036}, journal = {Nature Genetics}, number = {2}, pages = {149--155}, publisher = {Nature Publishing Group}, title = {{The reproducibility of lists of differentially expressed genes in microarray studies}}, url = {http://www.nature.com/doifinder/10.1038/ng.295}, volume = {41}, year = {2008}, } @Article{Lecuyer1999, author = {Pierre L'Ecuyer}, doi = {10.1287/opre.47.1.159}, file = {::}, issn = {0030-364X}, journal = {Operations Research}, month = {feb}, number = {1}, pages = {159--164}, title = {{Good Parameters and Implementations for Combined Multiple Recursive Random Number Generators}}, url = {http://www.jstor.org/stable/10.2307/222902 http://pubsonline.informs.org/doi/abs/10.1287/opre.47.1.159}, volume = {47}, year = {1999}, } doRNG/inst/doc/0000755000176200001440000000000014360272312012745 5ustar liggesusersdoRNG/inst/doc/doRNG.pdf0000644000176200001440000077215114360272312014426 0ustar liggesusers%PDF-1.5 % 97 0 obj << /Length 1858 /Filter /FlateDecode >> stream xڽXKs6Wj&B&^iiu^KBRv<@a&EX]d=1~wuGAN\%W 51:OtIdrU$?ڲOqW>fFQ/}PW9Vv6:(yOW$ Lӄfy2&c|{KC }OasbDꚶ0!:Ҕ DH T("sH_ʮ 8 3Mq6#ƨ&8"^iPxeƲd%aBnxhFA]e؜t]E,DzO ]3 gF}m_7UbhqQm:ߗJP >4.@2>Sk~/4R;J{ζ l\npr_ 4^Jr- 64M7UP-#F.2Q~xʼnش wd4ɠ,v:K$K[?s9a[D{(E0ܡ+!ۼѪ]Š).VwBJpi= *7C{p[sDXzm +Gdg/>< KW > stream x[oܸ_!8Tviõw ޝ{AһFqJ+i ۗMb0"!93=FzRߟ?x=D&(|{& ϑNOJ'PJđrP$i!#8g?=w Dk )@˳@: 0B%D2}rvX*p*]q)-m6iM͵]ZEa ]ٍ +K6Ϫp5Mۼ*ś$aGX |MFxR$AU<})a߼[Uꁚ|ldX&9Iz]1d`r/.ͪ_&]pLm0_vIU|<^2X@ 2f%dfmd-OvmzcQtwNWTOxj'7y#N̼<:9SK z-Hp&3\ ѦMż퉧ݛܗN辏! e5)!S`0҂  |s0R_sTNMZ^4 BR_Nm̯B~qGRKkzX+bWp9]?~\&_7\BZG הT/XqM@k/{2н'H^@E08RB){XŬJ< (5Swy][."E9/t2*E'}Z-&BzʘB8vW~.l r5Y ]M?F+5 +>rMMU.wuE4 ө o rk a6k,7d><XV5>c"ܴӫpK[1ޠv7 R rc bO&>cK஻2lO F6GgcKyp ̬-$|wS2Yظ6@޵54DU* o7D=w+~ֶy+6mm?cs3  + C+鮺V 2C+Z;B!]]#}5M9jB rW>]xЉcg`azNR#rvaA쓑 BԱ寶ܗv[.O/8G- 0JF(QGXBtlھm7iۗ n \k &)gz}re$A5qA:|m%AkUPUF o!Pc5`J1lGZ[YcJ.s__2lytmN3d'B43>Yn:'yC"7%n?_ߎ*!g$Dh5))#wU)̙F}i\;w~yy5wl0Kwܚ_ `fv[rzUF0pl4O4+ tvKn‘fKGB@{ʏpYr>? >9o_Dॶ3 Q#mgc!7-u,3\s_[[rJroOv,} گSհ¼tsAIv? <5vD+2}Sp ;Bd`Ʈe4t/nU[-04B.7fds!m9^U?>cNo*]?s_> stream xkoFCP 'Izi:$(hq%, I3cP r w&^蝟˓ӗ\xDJ]0eo,/zA^BP?+(;Xk7hܓe&ݠNfoj .)Wq ?^H B(Kb )ו65r_V&.]B ?]봶;kn]7:ۼv 6Sz*H{`}ZdGarE"ۋKH֎^hy~3/""uLuߠjv* )AS*;j@*ٛY(!~36;;>DWDw@YW$d?yO@}~bH_D0X'b#80cFQ)f `( ΄$ܹA(ex!מeeUABEr )m##LKV$ $|n vip@^ }kk]*_$X$yϛ^J;6rkTtYѪtÚ#p$B4p] 9IL38p)=ߥ3 :h{icGa;#V٩%9IDz AI%pcqSJ?, /& @jh7L $Qft/.O>8@=E0A" "\^ax R!1h6Q+.rɘ w)8M#8n`7=͈Gx+9'2ɔDg_UVwؕŻP^C7jCP؇ˁP$_bmo*aW: " skrSP=C6H#Emh\kp<ιIWZgֿ#CdKuVI2~dOQ "Ll2:hvyWQ#JalWN&-wT[C h螾;fLv +E+ǂ/ &9s,\)A"MS@:{O)EtRHO!/"XcɗRX=Qa՜0 [./~y< g_I=1mjj#獽\YsH8 N^.bf,bXJ`; Qy'$BvsQNbK@3طkEn T$tX#=CîEٹvdhi٪H}s}6.O,"0i2a" E$Zb&MiUZdDZum[7jX_~A.=uN}9^rcݯ}ը(0׮RUww!3)" a$#u}Dʔwxҫߴv{q^۶Y&@|f*sZHHͫo_,w 7s$~*g つ9"վXhs7N…x}};d,@j{l[R0wދjZZm+q!PHԣ+TAH}걕r@}StIS2 F`M!t; ^69NE焑KF;F.ͺĭf5PB iRNö) ݆mEjl6vG,n]Sll866z6X<6`u 5nJ<,0+e]{:/ kWUJX&7K-UdYmxc qWO%k;;qhjZFkf"JB^XS1 | Wu}b9Wt& w$N'{VH#fgP!#bLdh2{ (Jg>BrSB3Y gofT( K ľ{Ȥp0gcgj7ʍz\7iJ=/3mY眵ɖm)b2[ذy=fmex\BLhg].7iWi32W+˂> rMp `MSWd;M@W/aW)ۓC/rݚU[ {sm5=0\lF(&!l1n+H,iv8H2DIYO]wJ}[p ,|aDlH98CqПDރU۞nN|`X%$ H! ,n~ endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 824 /Length 2529 /Filter /FlateDecode >> stream xZr8}W1IՂh1LRΤRvjf?(2h.I&)H$Ji j6N_nN^" 2wB;[dAP#/4 hɂZD(6xa1$ vf(y+,aLid#,* ~Qa^с_ I8S #$m EFF^ m@4 >103z@!X}` R$!(@$X8ܸY Kv2P Q LU%"э"l|q*9ǒDhx<)  3"AD JIP/>@5JRCS,QS yV=@&/|X 0WD#@ƶ[H0`X0DV#VQYa4sD ]$z $d><vMvƊ4_$IJP1n`؟a0:g7s&ȱ' [ TL,w# BCތNNDq&ջJţE9YNI?}0/|PJˌ/KR/7|_}_>eU;58k8v1cۅUdx[,̧;Y*k&216e\f֓-+~oYIeK*Xя3kt|y×}vfk7ʗ2߭@o3ڥ[nyZԲ~ذ~vzbf^hh2zt7[؊-w-G{<ݘt[x,u\pq"d;ܳd!9}zvخrZXom<̓V[Z}.#=κa:GUO#Q\(q*93?9.EvϪٲ-Ginryb:SW\%,ldђ|>s0@bjY~﹞ᩃi ju)S?C$6\c56\c56b%6\b%5\R%5\R%5ZQqvqY?:}Tr^kDųT? 'KּT2m2q$* ւ8 [UTP4E@4\DlDQim؍b;qN*.ԊCCFZZPZcdp03(Dxn$iT@1QI~D!zx>JK0!@A$*֏{4Hm܊0c^v8F9i.^*Qڇ?ஒ ^*nQܩXppCSRzR9܃hl-up`K$8*ba cG&Z*!2Elm{Mυ7 y٪ ˞-)A8ü b(^'=8z%&e)?^U|Lӌ' p euqQ{IiU[w x6^LZe=z>i;mH= 청TPӓzi-Yry(&Lv= d5T\'_PC{$GR^]Vr@3j o8 THkV7?t~"iⵏ$Ji zoOCn߿o> 9X곛}T%3"tJm/'t( 钾@_*v 1'WՃ哭sLU\=0> W"Z԰0(>!w۵ީkGu/|ٌn]*1J!CbQγ=.__RJwtD''#8:n}\To^u|Jcע }qN`1.Bp5>qr;5{15pfS7Xϊ7||D됭F3q_\v(^?{X+uO' E?}mE}imǶQnivliѵcǶMulymnE([{96w?q`OWW`AK|1l *+bFjgH'`:H5rґ:JA@qzlbV`vF e7q2I!c &wDpVw;>sCެ2d^ ;vZ9$2tbZ%QY .:yIs=q>Zҙ3bybzGUX7r#7(Ou8x;4WMG|pXnC}/g:asT/Ct቏na z;H]@ikݤ$~ endstream endobj 172 0 obj << /Length 2016 /Filter /FlateDecode >> stream xZo6_!(n~3{CC pA]kZGҦ-w䕼NF7qpF-5QřL*]kݦW'> /vsJ< ;&)p/'DWR3 P4&[qQc+5 y =Rj=Rp9Р40+t#1R*'PUzq'0 ==zȊǸCM0 )-5R^9IYj)U.NtrFbyQp`8\j, wsKZ@p\> WSZyE'Wod#(SʠP ݈%,^Pfە][RE2\4Yx 26Wembsmׄ:3\n<+ :fꇿ\n _y}rxи ePEݶI1,֡^uW鮪κ Q,1bJ:b/=yyw Bqż1xk]!3R?+ɈkF<P=q8\S>4?)e`'=/ibsl p0f7nTseqt 8b-EQ"Rcb(xL,cWXQ3ZOQոR2N'i'* ]‡pМ䬻 ʭ8o.6f_UI?1nnpef7M RrV >DyrmzRW!U([()0%i>v0G F@␈e:NM˒nM h#3XHs}3ō삀ր/ӎ n0aqWgu-N"f o̖zHX5<w$eM B:EI Jn=ĶպꢓӄLohHwn]K1`3#6ӈ]dxT[Rry-mNwp^NL'vpipbheЕ:TE>>: :?Hoă4e>ePCuIoVw>gK3x7GKN8?ϩ7 endstream endobj 177 0 obj << /Length 1898 /Filter /FlateDecode >> stream x\o6_!(&1oR M 4}J%r"̶RIN~ٖevV9qC0)"wI^/Z #p7P"hߖ&nT*pDD#debdϤs&",BD̢ǍHY>JJQhRKW%;;<  tL"Rİ΢,~j0TUbu"E9I.~rEΧ\Yu>T2.^x!<32fڳ2EQ>rᴨ>"Xp}\$"^M]o.`3`?iN3=2ܸ~yU&?,21F,Y~BYl^93B d'q!ȎuIB'fkW*s]fj1D3a;Fw}Z2K/^vIVn&kP@ŴB4sX gEZ__u X* *(-mVD06͖i>emCu+:y^m熌q{ϽYX{Q bQ̽ڬY4AZ6pF]\N%]=\G | .P!vļ*dO618%) KwJvMeWJHDzW])Wj#"^˅'AEy@w]uMJ\[%WO%ҠJ *1ĠJ|YVĭ~p ߁Anћⶶ!tKN [ZshtXs?p֢9|#F"gPkXGá{#QKz%ò)8^~,ok(=gI^33=Uq]eˮ2-d &.˜W^V{! EWeN *IjZ 3pV9S/8 0w_-IH;OJBTDŽ:K<%1CޗQk&Y_<ޔ*w FDؘ gVCȧD݁Diii]}_u)I;Y0Lw_p׹r~-}Nw9VyJ!!!l,5?] &՜"0r H"2J`xS aSJu@eN3ؿ0k];C0D<۔ C#D)nH` \g -l\pťf>V2T$P"<4?4G=^غ&P> stream xkoF{~a {4l_ņf bc!ꑬ~dq;;@ EQ<#O<Wp#OXAbnxB  U2P!Fy0]8g(2(t0? N[Q)` ΂Yp~h"Ʌk}a(__e$ BPƖ (_Wi*чY۴Z&Lp/יcWS Tp7,.yK4XvțL"  >RyILGKGb:r4*xugLFc&M{b\(V 'T@-g3q,#*zIU:i0Q_N4N|40 ,]94%p D.~|u28@[qk&*% 7- 0 5򯬛a쀰p#IW&ehi9pjww(`Ewo']L~>..E vowKX4zn+3AWDe$6]Ɏ5\H,Vnkxlw]4KkvLPMue Zy{`?ndՇ^pnG`/n0 p2 |8}fx mt!pcѩhf6'nܵ?G!9CvJ@^xw٤RzO{XY\B0s^8&O+r۟wk?bt< X&5́mtp]aKA|=LHƈ Ǵmۖh7T!Cۍ$v֗(RCaι+W_Eܢ4U7ՎjMfW缭&$:nm{ǘH(sL&KS c#K0?E<{n=R|쯦[s="W!n:QaڊcXgzqzcqL<1x=gp/3?ܹw6XAlj~i f;o֝͟ a$"RW*6#Tvc"PE"14(="0SS,7}NoF”/o*7Je!{U*+>S |j􅂅K|F=!.N)v10DpCFݒK֝DmՊaUJJ'Ur{:Bug{4'I uѩۧM>4,YP_8P(VX;c0GyDx/0 +1° ija* L| /D\рH-G.gzXkU]ߧk7)ּBFE&4"ytLI1IFBSsOf+h=nE Yˈ[ HĐG³Om ڤ Y3H"$$CЮX!I|֟IV`ʁ:> stream x]6}~Ex-ɶ=tvw>+왛_RM<@h")"%b/K^Qu|SlSݣeYsCi4A Uj]cmdFdKI dl?-^7²*_*eM+7D9BrJyC09}b$槮\WSJ8i{=WXm[6 \'m=ˢ}2}$)L;c%:, 8c峋'8أh"R&\=A,a:R*Sb)A᜕7UViPЇA h,6 -d `r]KYɥ4r3,&św"h& dC449zǩ߷951Ս+DHW|mtܿ[zG| ī h ,m׊-B+ogq番ZJ*:5eQE4 RÁebH>c\9"ӌ1G4-#76@ft3Wu%iH?o* ZRݢ)Қ޶aD- x4#\熠\+2rnA  F*َm `n -7r|vd}h(!4e2`IЍ.vBt;E1(kBmA9OT͆v-pJ> ^; jGv16V(Fa 1gwaYi #nR$ᙎ88vK' h^ ԰{¡*!N7bJm7ԐA[-e (Aq ; ف ő!Y f"`cO3Rp1uSofd;@)l(;".fX8|VJ75~\Whz\0-0ucCr]}pzEs!׹&`$En@D %˃Vgpowm36!aHBnM 8p6ԱTP`AI/*7;ޕ 9IvG}X/3( ;#bZQ9,! \W Cj eӁ͏ĭ܆b&DԶ Ut&-PnH[֘u[6v NLpm+uVe݆Xhf~Lm' jHr9.2eM!XR||5)a݉P[*kT.5@Ar/GٙV2fh"At Clϩ3 ouiiO` o|s8Vh88$ѴgOYἿij.a1L0R{Jُ:vs>;> stream xko6{~!Ԭ%n+ݗ((RG%'m-o,ԑ;Nj(~r9=P#T ?`s#aPFNr}"g( Bh v*,Nm r~5Nkp#?rs.55= <r qo^ϙa"y6PnO|mK]u[VW(y\~WbVEq^j]- 4B>.ԫS]i֘n~/*1={4D%F-$,|<>ϨcMBl Oٸ:?m݈Q<=>Հ*)ԀmkB@Kfq_L _|)~7׫B.kaby-=V(ʶh%NDYGLp_*c.3+oʝ5]fJCZ!nu!Qoo+?_}R?U"d]jѠZtrve{bB'ߡ.phE&'ZJ\9WxCYCn4&/n# mbSR%eVXub_T0Cy`1֧iYS'AȂ0]+ž8> <ЭM\_̱/F!C" cHV"ms"#iXJ́7YͪR ٨Mpj ?EbMes "e~+eSVR2;[ϲv*%B}4H~JqhWV|e2|{u"72!|_Wm1ҵKr,@LfZzpjp`g> stream x[]o6}ϯPZ7aÒ0`ADc9b~":j^!uu.%ubqc}ugH2ob`XVT*10m( c`LM`QByvoU-c,lHi% Ҍt|hʨ܌6( *)|p)B*l6|+^Z+"٠fL+N]ڵ& |0|L0f@ʽ{C$ MrBct:QkP pcZ-(9 &~֠(2\TNApj 3+t,+Ɨ?]7:ÎyǒTT.&͐z*sY6gbPBܾs!knŕ/bc۸c9u8}p*cbj>Ū ϊl4%L)s&Fmfek; F iu0cn GwC,!{vO_2X7"r\^\1媂gk97'E/` }%Vp.b(>gП5?MN.tZGZa&[x¬m1~v Ʋi>T9{u]m>6U6(G"^G,R묷Y 0z^L19ܠ^0w"/+CC$H#+o+^GAxrZ/||a^R+NCӢZºۚ&V4r6]k۬汒SRNy0ח^;)o ?G{&m-dzͿ S:9\61u|"q'%~*)> stream xZs6 _]nr"/^ۺyPeV#K> ('{mN钳$A X3˱^}tY^f)1+.J&XD \붪d-Fu\gf3Y*idG:zuY7\uWjtaAŭ fEq|+]CKaZ.Fl%Ghe;̏< Zo܊Aph @)aɰ~O|0BezC7GXK'BKǹ%9=IT-[w:S#?a.Jn="L lkhs|?s>meߪ?brbTBǰeCʐN_|𳽚󴬊ّ1yfc>CNš6T<]UQV^&tղ7u^ VZ|E{L b>DTuB 4Z{NX{A=]/n/+LWӭ C75Oa8>8zWj\,2ɟuW٬M#Wrr]V$e[L)6U`J~=DsYvlo82h/Q=3-c `yλagcz@m/du_!01LBͯ \}V㝰k)k,GHjJ]%MrXWzteF/] MuDK̓Fs\M 4ycsMrUENiYu)12F3_]xZo}-Å8Pk 38z3uP""[{m8/qOUV.rAw]uAӃ,<\:Ugc oぎOQv6 E6 RpV ]jE*R߼~IZʩfU:jYme|`{Q _ dv̬mʺ@QF.Mfh Q㍲ɛ7h.N1;1Ikt/FGw3])I"kb^,/ݞbB卫?*%!UJ̉gEJ[x\OLKexh릷{f%g  οh;}*i;OPovp)Y/  PhP.ZDUmR.Y3A]zô\,&APCW>V3&mb:46UǮ%ߜzރS֖'X׍Gɴ,+Ġ]YC}~TmwpqRM7*mD1T~~<\_~1,8|p> stream xn6_!m,Z<$JFM>h7ʊQ,q|,r"1, gܪ竗'q <|]^N0P8Hscx%h09p2,H9|FKa%gs^Hp $ .ANPNp1A8H\?̢$afwEW)@ͮ.܋hN&T?sG.0xb"$0FyX{PL8S;Ϸަ\ٻ-?1- @?L<8{Ra1-mczDaNTcOJ!j^Ծ'҈[(#7Uvze0sHR⍘E,BյVrA,ZTjf8OZ|CkXp/ŻVT>׽3 -$ `jFqje 1T .WMu5$A'/3:cN?I=F?_"x8@I}Whv2pVQ:iߝFj՞{b~q 9FA1 Dǫ N$O63^Bjւ 9w5-%*vT B`o^fqj=*]uZ<{L֧OG+c `W\[{jW1;9t[GzU^g<ɉS'llK`ݹ8 69mWȲhJw 6(NGJIIԏc(մsh|ƹCΟMvgoӯN=^^W|K >Y#Ͻ?Y)K*gǺb<;sHuq,?!ΈsVt!=/Ru{9> stream x\n8}WX1#CH ,-Ah[2Hf%E9$),W0 Ԙ3s(6$ c6J=@ lKZw=$^m==x 8 ]B@F*'$8GkP<{`4>>"Ů5fQAU*wKUOߵWRhiohiB0t]dƦ\V-q<r|DZVٮד)6]Y5{fah~ T4[ -Ks'EYP](SWI!EH2 _Im:nhO3UN/5ڳUJ(?UPg\3'!L=ʞ tPiIcqTԭ6b|egkIq qN] pC*؎2rZ*Ƨ Ӽw0ԈPt 1W*vV|,Se@UX82ƳyO1\]?ꁃ`^6=%y~c 4lаA{6 | P)ؠClE 2԰A <ܟ A]M,S*F;Gw)^4DEC QMb´W؄/(sF*5gM ߠ- B]W99>'oM?/X2-[^E2ah]itILYT;]uIw]͑NFn}` ֍ڷ珈% ^f{ΨA,XNX؀eBT5sPu?"iXp^&@pEK:~.$}BO4/h盹H8#/J,tBUC$eY_8XU%a,LϣH>2"YYuͩ}/EyJE_X4v|++KI S,'rD!E&൨GEJ[m_ɫM PzO6-C_:$i: |;+l*Yy]*yٶƘ oxݩW:' To]9VMjͼoKj7M8-oy8ʪ鎫`ms-3>vxJ")Zvl}`/킁`1ەF^ endstream endobj 215 0 obj << /Length 1774 /Filter /FlateDecode >> stream x\Y6~_dZ밍[M16#:`͂kıqQePZZ-Q9ULkE}u~l^UmA50H:Cb0@^0p3ܙ\} _,˨LrC@YS&=IOwK"bn;C "ד˫6Q- ͉g}h5ڦ'ôׅh%Ya,x93TLgeePH-Ndarmўg}W,|{Qůizv:ЮMrxvvR)+^"rV6yCeYCJ߄ֺ*aP7(]=c:}ↈ`Z_cS~ MݝUo8ܿM6Цڴ4jF)Y"dyyv8X&A(6"pɳlMNĶ3 e/U崰f߼mx̘;Cnjsww@e1i^$>v)'3`c6Ԥ2>"2+KC)EoE_.- nQH+}/ dyxA.qX&W2A (,eIi2IQJ]ըUO,Wc&ty%L6wldq$+u.4`86L0^R7|ynzT-b/ӱ'׶5@$m!Qʕo.))ܣ4)`m"vk|QHhP݊Ef;HgRnJX!*5=څ 6#GϪP2LQ`e8F]PF.€:$|0F'+C(xgd0acYuj͋:rC7@^E$U.vW%BR"H!3QBJj](=}!u<͸ܱ(M9EvRGZtUҕ!aPB qR #iDI,%d[! ,^dS6Qب`F@Jd9,zIq7MF"+󺟧8P=j5) :'ɫx >?o?ćH]XHh{F,Vi 4*ͰMO؋\ۑvjY(G4 ^vvhÈcy1'`byU"|a{_3~pM/5=>oRc[F-hhh*?zm4=6^zKWj{n|qf)s:cݏ.G)/{_y_#^;!8ڮK;ݾ1^}CNXlU\ܚBg!{}F|# IZ;:6Gi[B7M\Vo.ܼ:Z3Y,xT$ٟiP$s~ iUX 7vm4hSN3zmS~ga*a(t+깝WBU;/+T& > stream xX[o6~ϯZUtVt. {ht,ԑ\]A!)Y1,@`<<%Bh'G4 i0|Z]8?0Ђ,SBQE2$I8#˃?ך Rx2ϟG3UȢ䔆Y*(< hƔV!%Th1өÞ0M5G#RcbG2WӒ%'Bl%x_ɼ|r|%KD m{۬vlVwHN;b,/9K;6mh/9u/yK;lf h_,-`L('Plb_-d۽ZĽ|_-/@ 6ENTfa-N>B ) "|(ڝ5~p$,X(Z;w (#«" 0"N~#U,@3 O>;9zMAI~l,;: bx^"[!ᲬLpqm]"sXnjR[Ź6+xʍ1UޠÆ yЋk5:;2NLQ6NTk^.cK d$RzW)WV!fZm;!n >ߛ9ƽF޶1z+cOnUe[,0;qvW|-ü!JTʳTxKEߢr PJ# P2x 6)p SdIjXA{Y$Ake0>&">KMެK NjJ5[h `Dy 䐰DӕeKp 2uZCf{M[KT}$!+x۹ %2g24x06 \LQ!8'\"&wL(jM)!89s/#l}vk+:Xxr 'Ax&]̦~T7=$B$A(XPe^UW'$~#/kW'[2ʝ vUڍ! /^,MKq i ^ M3D4Vq88UpZ]d#(89ăvp?xRJx&oLfPB95t S鏗(a*2XjB'8Y7G0ˬ]7nPT\$\_]6>;ЭuETRY]nHS,'>!h_oneKyjh?p3J[A_-KV)7om c| !bM],5ɚ=;?qA$R-ru endstream endobj 234 0 obj << /Length 1816 /Filter /FlateDecode >> stream xڵW[s۶~%!oɃȮZ9q:$wq%ʌ=s"^X(VΧ]\_,8;P<+?/0D;؉RcaH:BiDQ'N( 6l.B5?,/WƉa8˵s.^HBW( AAؼ~Cx~/1J͙ܔlzNzE<{ۢ9Z߳hB[k^YWM!nwϙijQ:Tdm5a6-ơ;ljpZ'K@ 7v4Xk:5 +ޘ,W} ^1 ԻBD3Do!싏I|7/;GFrʝ-1zcC[gUL 'eƔvwl0^v;yϋlQ7t~Mn6>/&Ͳud-L?lz5O7k,ٷb5GŶE*<%%ζbj>'S)Vs$]v)w-]xz|Gu. IPd!,˼!@gK.omz7$괕UmC/H(<+Wh/XV#qY3eKPC'[k5L}yм)5 HnV{H17yy^LA0@P"@vh"O"#uJ[+.fD9M0!9A/s@%/TUg~0SU?ϝϸ72/8c=Z6kF8! HU~%r!$q~2:SW㜻ԏQGm Ammh.7n]R$4oicRUu]#[:4=ꇴp!L $zR# tssᧁժJ~6+u?<5=a$3ut)P'&Kr8+*@EGءm4<4=_ >J$7&AN#ć{S\dGkv&ˋr endstream endobj 248 0 obj << /Length 1796 /Filter /FlateDecode >> stream xXKo8W{ 4"`i@ɶ =(6%Cfw+'9E$pH~Ptt5WON#ƈU/GZHsFSI~9N}Xmu>piS,tr['E~tu,%Lkn]Pb3~14n2Z",gʄ+]k-q.]UExrW#. Mh$2{EV٘:Ȫz VirKi'Ƅ"*p2AޔcEܠ+X*NQvdDž?SE9J4k+LڟL))3[•*U- wk,ێ:lҔI*A94ݢC!w#tp=:6 Xz*F 4f~VkVpa)gFIVu]чM4Dyjx>_s`L?xRJ{xɉb4Z"nXC Ʃwe&o^7迮 ^EBhh!=QY\*ʲB#FLY>|lʷWd*,'v#r~ 8nxrrIەͯFȖ ht(> u%V#V됧A堷A\yWT/ @VY a3,XȜ& d,/xD.(uH(p>ĉB;DҮgU=M ;,n^v v;\>lqe(Z{|ZzZ0ːmdYڀcL.Žix k/BOa/_ Ǝad#@x .7z .d[!ED4:}4 D(IaL?"䯂ne1"mq~"3"8hv𒭆B)Q]x?8x>˯6ѵ#^}Q\{C3L*xIm) @ZԈ!0+;Aۛͼ-|>Rv7![*ځ i<ϞΣ b b{$@  c.S,x,XBx[ۓAu1z/(5p<0i!Q2jvpA,ɰp{kBeKd'<+_FIn3@FImI6/c|?ϳyQN١7yqD0'9hyu *~ t!A)„x$)b.րв,ȼ/(V= ]m7mN'NUH܂)W 79AuBJx};=9DgP2:M Rk2\% a*-Cu.~NL a??)Dϻ%~k?z޶{5EYlɽa/Ο% endstream endobj 257 0 obj << /Length 161 /Filter /FlateDecode >> stream x337U0P0U0S01CB.c I$r9yr\`W4K)YKE!P E? 00(?;h0a$>z A?$h LF N8\ù\=Y endstream endobj 168 0 obj << /Type /ObjStm /N 100 /First 870 /Length 2355 /Filter /FlateDecode >> stream xZms_ɇⰋ[G͌ԉgbGv/>-1x.In}=mRz2[<˳XQ(Έ^d qJhK1Lm2B}o"p+Pr&IbJ􄥈 ^d&XiTtPijCa@W2 {\;hG`m0w$,ˆO 'ÙVc\6bCz/'vc'I PKNP~.hi`ay)0BTTũ0 U*0p 2 xL+aUWcHʐHQQP UW`^`0OȮ,P`"!RGe D$бg4ZĬXI&F l*%f0PT ޒ8`h[)6t<4}82YFRH0*5D#dh=FTHO::pXTVn NN oQ\ Lnn} ].xudD q]6zz0''z9fsh`r^$mTCm&&"@XvNZ FUdPӘv9VgśœMvqa>I?Ǹ1gh<]̛؇tFvYnXs7Ú N|/g2zFKtrD gyv˪UW 2fnj@샍I=Ãr }Bnaojll/[ed *6DDK s kMؠO~@]%KGcӴsl|Qff>f.ߤD5YNdE.6_¾I#nO=xY(%5_K@xouu>^\ [-D3_dtxfDo] zB2qyҪoAUb7HTwM~Ŏ0z31Szq{jQ|$z)RG||kCA?MBCⱋ_ܫ"ӫM/ߣpK ki?aPX@{'(>+j3MpĐ&` Qk~Ufk<`Ќ'8pjife;.g-,V;/￿=_ onv.p1h uiSW`qD_-Ssxlxv=xO_'k-> stream xڍP[ Cp'ݝ%L܂'݃;s^yYݫ{<-& $ sfdH)Kqr8888Pii쨴: G'0N )G& t~!*C .'7O_!"Q t[ P;*ler>,$lA`s@l }9hB gI ll/ub:Z2 dK2@h 46TZ5_Mx1@ ;; #t@d/ҿ,7t+`9hX! 3 hgq]`w@:NlN`_Jf; )- {rmnv^A`; ˿dXسkہ\@漘Pج@^~>Ar7f-{Nο/| %pvtx`6wvA/vp'㯟~2z0 +fWWWf:%%/V^n+/'[dÿT/ 0tC^_SWAɺ@ Em3^&e /`$b}]`'Y;B lnq]E@jP'_+'le&v^3Ze\|#exy^/hr{lvP8%KeKsAv?ĩ p,K?_$R zi x_|PKcwv> ^ʲ_˲[]/6/%%-g N?-pe9tz1^CfpHTkl8iM@_9γ(,iQ+#U?=1U- ܊Zwv ^(6MF}R贃V̮4|U GlT6M ˦'ihuQpF.4V3Y4 EE|q;cOX% jiί%ܺ 9al# l|3)ZʃTD ĥ*ߒ7E?t^Yg2h?Ukes|_.4_7h K Q/30ܘaQEA_?ۜ*6`0P6T&.U&1!z#|bKjϳ/QtzY,[`!N˪ C1lũS *sH OїX#OPA m-k͎5|<:I3KI uYZ.ys՜qdt:~ܠ]_SH,"M:;ܽܕ6hև&Gf] ;؟+~vӬT 9\G{e-^rs܁Qߵ'LyeSµ0\JKAQ dܢ2QĺmdrO+mdf#[b¢,|ˍK`E+|_2 &e3|DLiM܎}qJ,:/9YPӇ݋EI]L~k"|+*b-ar HRKh;'S̋Ks'X)F*paH/ )MMy"afT=Y-( UbR}M~)~ei8NE*g{u'* S}I[ Ж;+|K Áy}7ƔBM"ʎvlj٥/Aj،1\"cRQwU'rJy֢Ш¯$0K oO mQ(lqlUS=k>7/ ,sarW,5SLNɇ}eeQ vQ~&BȿO)oF_SlOL>Wa`9>7`=,:PWalO}uRc]} +L.J-c"f~&+ (Rb¼д= j|\Lۨ)M_6mx"bs3]MtIqg-$*$졤X%M8u&l^xK¨1B*: x6Ty)nT=e\!/DEW|5[yd5ve$z,t9@> D4cgv]vRuE/EQ] f( hI?~1 WWz6.GkWTPij>upHKhʅR`Pю`=:eDk4zDj@D׾;F_ dx~RŒYd1FK@u!d'RL7L&B R|rdyn);^ 3=6b\ؐw6 4l%LG(]ȓ{w@|WD0 >/7p⥏6:P}c?=?_X!6 SJg#E7]|-瓧KKcA=`,*f*s>5YMYU+BI!eX3 >Uk\8\T_dВuysь$_KTホW``eJ$ GN'9Wbׯ 3ei?dՠr7{q W*3*7[P-sTJf,j%)H֑>IpӐКCeȦK9'4|aNr|c0}6VN'@D1SM!FH ֍߆`z Ldk亲WxkmsA4wT Yr4ߖYI1h8E5jn24*Kُϟ>!49=*;,T~Uzڣɵ`p:WR{`-hHnSCZ'"ҡݤ#wiSpPb(z l hYygpR% kts۝~H{~5|M47tv3m}*}j]^ ޔ˘,]/ Rz[lk lPxݒv7"+lx*v|iQG z>_gB▎X#j]1GiPRj\C,Md'Ah#Lf7S(nJ֌%"T,HtHǓaҚPԇS@ DTP#eܠ[u0~BBhclǻK*b 2T^^Dʼn1ȹl*W*VC*ya+bvncsNos7ufplTuqM[;d,m 5+jcVtݨ<U"5ɕ,%TW?٥`VJۦa7ޤ5ר ]kg kPpz&]C̓v$,2.b(~tTQ=>a8jD5!,&PFs=_`ߟv)u߆K& LxD2ʻ8vHB1 ՗i瀫aJNj?ʏt&`0iw<ۨ:b;KA+]B D"X-+ kf͇63Ԡ> M?; [7oصNѢ) /lxU L$v5=it*[>Zք!MԺ Pc)7Mg狻|0V򸗳hT"+NFq~-%mξ4Ӑ_wN$<$=E=ܳq o@Zۂϛk})jxP5ֿJ`֎`0Tb/NG-+D1LN vD1g@[%,➈nI!rٯ1"$8'0H4r z@- ĺ(3~S2ES XV[RDR?w2\\~D0޳VO" Q1r9وlv;[2Bh f-SJZW4lrv++7ϝ wBs"r[#~H>LmجR!k5jؚ-\s]a5zUi/|]C =ڿ-~4 ߭it ܻ{h4PR^7O,eRӰ~CL&TuZJ$4)\K et% CJVW0[wxWN-;9됶V+n/k3b;-^#Рo9$[7Ԯ]I9,(y?՛d޲ j),y.ؙfz%e/Τ_K{Tn, ]Ĩ EA'Υ5 p&S*[ b;}ǣ_gaL}Oe` sMleTSUNF9oر 49`K>7LخHNnFvY<6)ɾآӌ_(Z8"w Q ]]Ah,KUl\M!r~όJZGU GQQ}.pzx = Nvj\ɓj*_ 5t_cLs xJ߳ }JTo'>%yB'k~2VP++3;aɀmK3rZ'Z'|Ï%Ⴙ~*}-ǖ> \7Z[دJ}7F>y;^7NgY 0f*T/԰zzM*5JΆ W$RFo:C.]lcl H`n]퇤 F8ACՓ!Ot>BA+vDvvJB, 2?m9JSnH;`ua//1 Ҋ۶V$lNQAK)*G&.hNsS᮵jCfl.*>ԓm~:x/s<qDc9}Ǝkaل{m:Y>i#e5f CT{HpШ:DWF&oq>O1E<>$; E\"Zo XH/eADN0L niK3'8J.yvt.~nX:g>p7)! gabyZ(F靾UE\7xhd{LKecepUbJϋ!!^:O^onu eG<]&B0`Dy٢'D+˵ДX#ƒx#|wpt趷5 F|[UPDt~0 d 5?uv{rLS]d,<XaR]>$bײ3-Zyrr500zF&bi=JUٲ5$^ Ht`$ihX ufAx/\W̚%tcf3-m/#E['P!+?,Suz[ {r (_V=F|2FDqEa5T+}PGb8m::ySZߥEj5.dl37K2}=F|'d#dr>DY UׯrGJvrg 6I_)lA߸1i@. ɐOYUB8 >J#ܲ){~|1OdcB%Wn۳\ekg@L0W,N::%$X2K! W :FlpYU?"khJa™#ŏ1rAh#L2N=9#= &4fQ` -Df8t7W[LYްk'9 (),؉C r w 3eM K> 1#?5[As7p?aiNC։tJn_V?SK960 L9kw<;g|ɵ8\akD_p8Do=J'+b4эO !-:t%ߵVfCF[X1Wd'y.#r̐v?aa8PbFܶ>[2oҠ$r#-Nu때1h%D3,፝%ʿtf8'-wm7.eI`o]Y14G/Fٷ[#~kƖm^RJY0-R=&PL)g 'ϖwV}\dB]| dKY[B]K`*F@)~c(bw0Q3 oEj~9(\/ක&Z̽`)X+[ş)32YerF5 חvD5wn|lJW2$d< ݔdo=- k2S(v#8oJ]cXzov_pzm+T#̭'|loW8jտ.E)cJ'|Iqa`5PT5S.Ω NiٔOȽ&ф3#S|~n[^YV[-[T'ӓW8Flz"vݾ1Ml6_$Gqj žH̰x.4[Fk]8yFk"Xn0&Fܩ4bٜBX[>QF<}fK P00o葁_-^i͠ğvCgtj 9#}64:HClM06 5W5 =}:-8ս;Ʀ^U(Xm' )uc#}N|=46r&CYLLly{G:.gr.d8=;[QEP RhrG'fFmP ;PnnUT[ (bnrzYAsj5>8ʏgM8ce6WWiaFiGgRTU.bv lc!kkSKRM,L:jGrb7~L~\nۜxl8T[q!a pWOocS;PN-:ѫVi[;#2wJbrS0JT@R{庐0b{UH8R-y^oje R ]լC*ўGn,GCZ$UhBTv8H"ԬeXtcZҟd[ͼuuO*2]]Y<< xI1q1|~2hM9~NtN@h['#>v+k8"j͘O,io=W~|N jVA /[& rU\kZ)! _I N=7"xyU`hg#.1>% (uj~|Ƿ6XrzcMnfpǝ@)Mq‶~H[R[\jM_d?3Ȩz҃߈EF7A$ioHJ3dr)PNym9ŧQ+_PhVrF-zA7`#4R?XEy4 UbZbDw}WRիXI1 F oR#W d]{ =\ m^XNd g~@3ZNǧqa|R[n& < grSƂ$]j+hgGoMDg(AnX(9 rS]`I ዯ nxUΰзz[ͤby`g#e\Hz-~-(971,l\;2;] z@ƺV=wl{@i9Nq[ѓ묜^qj)VOw,S8aɅouxt~PMFԨ'ebrԯ)z O60ȶf-QYSuG3bX7_eu c~g6[K=T\Z:!B BoI Q^£ ArZaG <]ʯ=FshI;'L?]=TQm.υNI똧kuԇB˳Ez*q(E¢-շ\ߊ h*4spK.r:Ztx~%2Nl1FRֱޣ~6oayw'ˀwYϲ; GJ?./Z*6=F?iK`TX⿦k~N!I w}^Ǖњ2IW[>a;¶?bF rC$c5aPזl*GuDPVc:%/0Ҧ)Pc3jb飨{_TSE=5':an!5 $3aVmo.Ȼv;w0Q,Vlc.v權WOH:| bجz~ ՇCz ~UYFDA&ϐQ],ScJ~D:эpm)-?3>:!s"‧vwicOL[Iw» ^ w=F7<'JNBQ{|˯s?ŧQ}IKkjRa&>B~?vJX'2^Bk*PvfƬN7lkmH_t"bڢt0a>ܑISq(\*8"}0`,%gh n5>Rsל$42{#a*==*Kk+v[fblݣDe|qXK/4Kţ #o o~f:vAN"amZ F"mNo%M`` 7KJteKѨOWBQϛ{:M,B ə:  = K> "#i?~Vu乻^MuJ[;D֯3J0D'PO)A1^Db S_}[:b<Q׾ >,`V)]էSTzוb>@R6< 4dϣW.8~9/5w+kP5)({n ggNP` mVE/zGUoy}A.UcOloװ. Ak::wDocP3PN+`ۓE1A<* `ڢ\VY*uAy\"=6dH w],)fAޭAݤNЊT-޸>y/Kj;4A¯vedDKqu#y|ޣ}&\97 ɖj=Ù&0Ӻ{3&}"UPғ Cu~XV@axF$X[/;Z Ѳ c,8%3$z}n5~ endstream endobj 280 0 obj << /Length1 1450 /Length2 6910 /Length3 0 /Length 7888 /Filter /FlateDecode >> stream xڍWgTS]I:"5*zU!$t* H)REz*JUtѷ}wZ9gf=}d!la)*#6a"fPDB0X "@-8PT(*%'*-'" DU 0{P IH7 ]O PTVVZw:PA@] ]#`PϿJ*8a0nr^^^BWxO8h(j56P {8!Rn G񂠠@"$=Į4AuU CA>0#t0A a |'bՔ NC0h!4 kJ_e#]] W*0> v!~b&l{@5UDa](z9 Z ;ۍ# t 9@/R?4 Ġ<~E** aPGX7h-%(P gHZ=aee7$& IEŤ%ҒW2?r5H cwꯦ=dB`(i;b*wOjpo_>b`ĪPs2VF;`Up2$*!$"VyC `;?0kY""Ê{buUvH_BP()$ON{oBH 60D:ZYi0%*" cbY+S (PWKv(Vك/zCHGveu%J,^b{U E$juU #o [GiV i'SjmKYџN3 B8b5X0lҗARohSnςƎ/>.ƶ})wi-` )v ~:ѓ4u(uFCTؗf ]j\C;)P.@HxWbU^1Q{ TvݾȦWZGO L_|ZE< ㏯bx8~hb/sl\XBJ^eW4AG]. KAwGfiŹ 8>{v֮@yU{@rO \dolmpBﵔI]`}'? eA[?‘VMM}Z#ɮ|$B$'gGژ_A/9o5^O3/k%,'5+H)E3mipas7ڞf-FX5# ӧ$iw7⃩> Ai |FE4w*^M|Z}})DVr-,_&OfBkj܌[fPSI׽c&kwݙ"ar6KnqdUG$J>KilHk賆Os)!Ymeʪ|f(^;ulBqJvjRd\}+[$v&;IYN7w-og)LcSKݍڵTRk7:殙ݪƤ&J9 ;ƥXrMhz¡l|ѐ_ߐOV٤jFZda)'>4sKPhhu;$6IZ[>wNhnST_; 8Ew{ k5襳y9 ͇^~/5ȋ+rS% n0񘄰<;ETS]tli!O[I&lQn0T+O@2Ak78PSΑEu P/-~z3/Ǻ :q!dhk ]-kY1w^Yݱ VNPNhv=m]!;Y@#rCP{7 M9iF|}ؠTQ5^p܁\[)* s[N͖OU(qI7{w.7l%L $M2yz闔+LoWj/ {ootV}l +))U锥:|o_6΍[?n OTۍZ4i}Az|XC8JԹ[KТ,"M|kvnUo!p 'npFZJiZsQDp1jq~d%%,7e$sKNɑ$bW,we|t~BTD8-ʫ_ l%xqV ssTcۇT1omdkғnP~ͶmQK8Sɑ{wҕQ@8&jrf؃`a t`ݴުf ;T*a 7B._i 8V2TrrTU멛W_6\a1~OF!?L6puoS'·s,Ec+1ak$MBs]ES6ppXKW$f,Az%K30P+U-,^}`<#n|I[5}6q{khw"l3fذP\RMro^)ǥ7jkTӱ^uK7Fvnٷ6# \[7Rb}"Ye\\kl91!<蝜sSc5iξpghu,zZK۝*jAӹόKt{.5:Jp b=EՖ~ĀkީELN߹yQ7ܤ.N-њ=N9;.!YeH9yC@ܺ SKy]7Zr.XQ  >&dw#-'9׊f֔tW&>j_S!n K$n>y<_X!Р'S1ڝB߈EL#Ylg(UHNJ=?zc_>Ry2mRԮ?@Ok:'k_,+^Mv۰ջ!w9eɭKzCI8 PN7bevTwx܌}]B/( d 6E+ pe?9  fୢ3(PԶ! !ksAsn{2\}_ٌ'{S}~C (d3i;:IfD,S`/}Q_ȌNgOާ˟}6`nzku8_>F[Cޑut*/@εSD]%WHm:Ut{^'2x+)'u tyzޏ Gᲃ l{jIE iy>/yj(f 9rͽP),LaX}?PEq{s^%N!?m?K-{Imb&z4ٟ% u1JɒxL:ȳ]=+%-+i!ޫԭ njٷz:h 5lH,am66m*)"2O?ij6;1`[N>xj}4j6 |Ћqد^fs *WnQ40GCǍ΃:?Q E͛vG.O\O5gZKIwDJy M{ P}AkJcIdWx뾿*OuqD,:kS~2_YkPjI|ҸV=HfbJ :G2eVUcsc{mւBnfw״h~Ɨ~%k727W15gWΒsEq&Bu!|ꪄO_LSֺ:pu>yqo:2T֤NֻRJtQ- EM~h9fu[`˴SL2~yaΩ|# jM#͟RZz n@3 kTe`$V f\V;&쨾*;L#H[}dXfநKi*T(̍vγԿsG-}θ<2;9.WT/]ңxp'*Dq)`Jul|z橝]VIPE\֣xxCl֛tmY&\ nhS/+ETKxS,HHntukC*3]8zֹ'n/)?=3wޫ{KIZӻm~hħFmqw4ZMZ|oW?a^x8y_kaUTHTp?2D[Z%[uJ68T0R\i]f&IxNDK\PKsQ2̄Fiu}$|>|˔Oy_xo9QߺY释|fNԷƉ1FWX,_,ls|{\Q`2.w*A y }ߚcȢa1՛.Ǐ->n< 20Fmݽb Tf=lX18zH0{[A2ɀrGy5]j$wE?4w)ؼ]uVZʰ$v{#?Y3.1)[ɱ#ܡjڙ[FINw:pӝ9x' %j4su NOw~RG4_OJ[S.<{4*k#Z be]uNBzydaĕAzy7]WixtJAY)q8a@ehFy#'K{>_t\V.(ا敯<0HTO5H>ٵam/|NG:zfWdvT?2TyvIeESqlKgrbnWXޣ g'yIP}Ӓy;0@C[.X^]3@Mp0m)/XZp ];""O:8GɆW ~gxf`W|$tI8+[;TFܜwq_Π:` ]x+HEd>/e4b1TKl>7ұ]Y{f+63ՑrfX~7B0XIO%9x T2N[,_۪4ì)~n!^70?q mޏ/O;h?!QrWfFMi2u+A29ņ.p[17MgI|X_4F׭xmOi-OiY`܉zQ,{bd@sKIF\Yj$v9 a;~zfm? z9ի뢅ʷ{59!ųY [|etۡv]*@Z대Oܘ?=MZ{p:43|' ȿpl?5(v(C]/}ɥa)޵9gDa3$:A(;O>iT{`kw0>x/N:/+σH_SqqqeJ^*BG% &n79Jbyޒ+= "zzzv2<O8szMΰ{c#mQ`Wnw1(p=@_]'IWDp`Fxv[ DED.;mw-ՠrs;n;9+#Hgg>%+ rwDmF.@C;L]/ҝ ?6;  `0/=.?w] ]wᶰ<_7 ruCPwfNp/N?+;A'e1=UP@z| ?HT z<:`_u+Ta޻fdאp;6N0nA%GgKRqwrC;Ýu7 ZȻ@/kpww#!s,n*p/T?톿 ࿟/??!8=#nwv8fKPF@' ,n nJ0? !][+[]b>A~/( ޥr?dwAf !MtRi&y8nqE*]* 3|U1U{ 2l1}u6{F^qDk=H6\Y]>ߣΙDOvQ&햶tM*zqU='>uVc֢S߰T>-͞T*ƙel׽rv67A|JQ$NgNձh}TLpt4a s63m{mA?S[\a6gb+dA:T7{!ti=?:ז ${¯;Z~X# c_ qVO4p wJH:vwH & 55fp`d]NEAu`{M;y䫴,PF!.$P44aA$RU0a}cGRU} v?+sfu8j˟ܟKۘ ]T c15֮wD9F*b})'x#JфZ,F<@"76ݧ (Y!a1pAXqw:~{2>u;/-k‚qoȂza'O[M\z(p f硉mck!oKƮn'[)tр5}~[njge~ }]*FI[Jy)KE3-^|@xBo13F,fWr`TaKÖ3ZUi 2|^CBMk؏r'=tz6 4 5ʼ̪[l4#^^-Ɇ1uӰ^YGJT7  og '#U-N \kN<c*{1b$Ա<~)?G~=LǁApЏyO bBӁ9b04l71-O1q=P]4sm_Vw\RVk6׉]X_h}$iiKsT"6F없9ݮ,e_0fN:pߛ퉾fj+nj e,i@<4W:~RjL'Eo~Q,N1\f)%:ZD$:;X}S542(k78 |)\䇴-JF(zaJyTcĮMgk FM#n"]4hM#̧yYsV{8[uo_rx|y<>,~=c'ܥy }d:=.xjij4P&6kSQ}5U9OuߣW#5wk \6}{;Ǖxu CYx%tc2e컜XLljǗ[._*vBe~mw*:<\UZM%4ZlZߚ{)C6x|#5)_2yoV#oD3V[e,7ǯ"">jiImYb[O>"+ė (>ᦑll+ߖM~m%GeogCQuv.V ^AB}N 1lƢqG+I҈j$I`Jd{4>EAStG5߁4ER7g=g3CY,oE?:{ "$#mO4lM=^sb ɳ6د R ֟963Xi}3ลn`<(SU^k?ز˅x˦zPLJm^Xp=#7]| SW*"2ڳy2dܘYد-:&b(ⷅ>uja)g ׿t|[7PxMyD|5kSYS`#ݻr6FҴ1O>S#̟'Ê 1gL$,,N9gӈf*JJݐa{C#"K2VCwG+Gޢ<fK'WAS6HUJT55! <:gMDb̐ɿpBBD&mdC>ht3C2j =͉z3ͷl+Mr)L zt1 gFuk/}y)D)^*\dzPYvc%ϊH7S7cgS%ѩ}?R8/\A׫##Rtt*jC8T u.iaM |emb5 Gs" KB/Xet ژr;2XMW?s͸SX.oJLR)ªxҮ ~/e]E{~Оr+m'2ײ=2]"۶wXݦ=>8iÅ9#.d8xTG_.bb.esYzFxmABў!Kl$ۙHjumLJhzʲd3u ANRDuPe(i?F,i{wW ug]7?0t @,g^|Tʹ`>Lv=1̖^5h \ũPa/2۶_j%02].'DTmkSFjB;v3 Y=VXYbX77-/NJ^z_qFO]F\"›I"u|`q,tjv0ŨKLQU.~]' _G9H6%l & gvc*lޮ[(irWs׋?J8H虙swXcg'7>.l?1Djc`' HQ-5Y  Ж4zw Lu^Wi̍U) ('gŽ!X:s8Ұ FTG1]Wdvk07Mc ]e$RJѥyYqHg+)V> tFe)R4xmMՆ>xod4F!*WŭoRtv|ZB}= 2)֡.o_ˠ{%f`$:N1~a1X/JW}6F{A+Ar sF.<: |Rm-vlbOy>᯸R=[tN4zQ'5TݓOtqvVՓ`6= 1Ԙ-oYO o_MyY_M `YHc̣?F|'4 $j.s^iM;g.=r05]jOG3b̞x*؞o=HOT2:Nu F>޷S$9y|)]T&uf[h&F@9~:/-2➉l4I~ ֑LOa R γgQ:XOL4$qCiՒΤ1*?U8PdO:d3~-mliȐtK "93SA{ VGDBO| )=ӉЇGNUPR9TgݓigcJ\=9IBsתoArVLcqU#/Pwɸo]3J<Ψ#(֤z7 C9VM8/|/ Vس^%?r;FO~W{R_4CNn# ۨ(@ S|+'M)ޫE5X9 ϛZcHR|y}"x|X|_9dnjG^]QG*nMGf E%)}@'=D/AF0Bgcg,iqSf+8>udlv ~4'H×NG3_GG J{[z!YT) #l.gݾәYjJ:u,Pf+L3| x͵ӉBw8[ʹwN~\0:`_wJ dԩyZE(ip63Y{^sϥ - |C=C?o~r/Pa2֬A kJ>ib.3 ksҜH^C``Z̏tvPs ͡9Їb0aO/r*b\hpVdԴm7 4Z/5˹1kIZ!\MMz ϒ驙NTGN[. &~76z?w+(֢?9izC!WYp;ih}E4Qeʣ8AavhzI9)_U b`9MMtUP)8EP&s:ŧ#~A{VYtQBK'uk6)7OԤEVM$\p *lD ZuT$Ƚ|j"1,&,ʳ% MX?1 Zij"!\c*Lt`9>ĐH^U._gYIB~0Up#wwz"b$_ޯa};7sM?wTD*L+lszDN})`{6)hFwΜlڼb^= qdRn䞑4\]8OyɲY'UNydY|j4ȥмNBJ#8nW{UښZ+S=5SxuQNO2icz+W3T͡h7PM-dP 1J>M%$s=S4T᠏lOa 9 ;>LR(4?^ve}ڰTp[DmuUc;1QgZ29 cElT3k<|!-*:0cO9ROy,Lwt`U3 A[};Zx?9Gv<3lɘ4YWi'A}d-~nkAe U tU=Ґw.ܶh>|H1riK9oG/Lj!c?-cnJ^rs\Ŗ/ؓMgq D@cM?CWLt_\NvjC>Tgk\<,-Ȼp2D.>TMnFY_ՔTk28ASu^ w6gZ[ 2nuP\W*F8),~송L*lvH WON վG+sML9D!Y1"Uw ɉJcR)I}2HGk~فnP/C֬;/tqD[fǧ6uJ],\k'bI,:=k;,̼ƻGi5š-![+g# mN^j +yW ->:Tp0_#aoƎtRt쪵ÝACMc#~[5et%CD;#;4ɯu,yT>G᧋^x7tg"#~/$ƿpK="A<5^_yxa_ {fㅧJ.Adp_{Mv;(9ÏUJ{SM|LY DȌn<#f;bk.)q%nh.㙸rந.hջal.S%K鬡9?i endstream endobj 284 0 obj << /Length1 2210 /Length2 9605 /Length3 0 /Length 10918 /Filter /FlateDecode >> stream xڍTk6Lw#H;HJwtw 1ҍ4H+ (H+]"!xy}k֚y3Lz2Pk"ixxxxppgп &C; As<`j@!Ug/?WHWX#"] 4P$uq;`y r! 䂈htAm `p]Ÿ.0.@ [@ja;akvp/;8m@b r TZ d ?ֿ!ml.@b;Z\po8E:à{' FP%S  qοgp~'v u@ ~B;0W!HEB[fyxxE 7ƁW}W_J_bD~PW?=AO#^^-ك!8# 1bހg< d2[(7S64QWTeOjee?DTN>~~(@HTnRb-M!`gEۗ&1 Q7A|?_&s}7%Eg0?  bx=EЀ"T#{VD, 1Ԝ\<0E7V qkF8g0 ]2#! q آZ7>A!Ã(>AA#0 P8(`uuBBn_0[7p+yJN7BiF"n[7pFnHm/EFֿ"3 BguF?_zmDT"ҲO=BbDw !8; bkknQ.\<d-Gr+qAK3ċnP8w);//D_Gt7D0g @\0pw#C, 7!|x D̿^z0͋8? ߐpGXއ 7gnj#6Vڋs}WN3w.M rj"9CRP5P#ℽDe۹0IHIPvIhbR%Rm|P>83a!ы^nniaPUVakmM?% RvØq LUldQ5m='j' P=6)rhW@(yͲyQ>gG#aXcc >Xд-}|*&`"Z]Ҳ[guUn۫|/V?apu)(5IےhǬV8vFx]QDaFV*dpzKڻu0yX.Cs7PE0vR(0ⰒoI|{ 2tzGSa xFоēč/MM#?L9by=F-4rؖuzlVBx*e*/EY d=m$OF3cR>R:CΏn=[Q1\#lGU aܝoc+GTC K׿wn>ÏoqSOL,dDJ[#瘟^ʍĽTʽȢoQWȖy=g\W^H]d`֜E0v zAŗ(Q}f$zv X݋3 CnA̔nohRL5zcafLJd o z>W0z)(vԧYM={͕f$g!;9yZC[`NT^͵v i8qzؼl«eі qz΃Jnf깠7dAexj(#Ѯ2t3r?Dc;Pė.A-] *J*zM<%HB+Z=(2SyeQH%M"Gr'y=/uc^GpA ElKqpgWOq &y?/~aotU&cjp^haO[st0 V}Ps˜".dr) Sih6бׂ2 q BmiB$']&* R'DaȖM- {d'^`KM*ZNDo܇yĴ&)]$n5-S@\;擕MI\iW[TBwnI(bwQ"z{MHށ^2U,K&L:yfk6!+iFN:koSrzaU c o_Y2=0o{V㸺`vP/OИcxFquCZqەqic'̽ׯh5flNj I/]CUoœ`C }1HY3֨R5[X$426!ضj~d̩5C{ub.#q~ɳ߭WBКƦ>*ZDGel=zT㼆e粛EKGn={A؎KΫHI #'w?U-y#$jrgO(ƽ- MrW YI?ex~1Vs k'3OK%H:h.##0Rd84[3B2wMY qqDV5{aR.BzeԳ34uCD/';X*y AE.rV[ɿMHFYPش:{z}٣ VA5W?gWQ9 V 5e i ?v׊geAF!bi&j\Ba|3)Sn8YnH;J'(]+B֕<_=괥vf\TF#| `V@b,0IstIƴ~7dXXpδ,;}+F﷞[[&w%b"fc+*# ge : {YG]\Wn\34qN/q+gwH} nf90 c&]Ä/UDo0c`۱/>uʙՓ_k!w nQ]*=/h*I :W'gm3(Z ?-(>JRVR7 zw=L4ФՐّ«dPFȤz0BH?4ZY8ѬL%dJ~cdq24g؆unG]6-K9ԩxMWedJ^!of-47UM{"%:ܤM <_|>v :}U3 Jp14{dA 1_ t;ҵ~& s0!W7O91vs:D =[HSMDL#%3 @:mN-rtOl#+ĔC3g:x^ɼ7ݖ'y]?ne&=$~;vyf.Бfb9tL˼L^c(e{:yvSia$ ֊ "UNa9l$2h;l(TZ`a䗸tSU+c?}e <#l rPG5n,Wni3xBl.R֮O_}wkp%[ j:-s .zxAG%Heo~keܼ맫\j(egښ&xrbI՛? Ў:f/ ѢVJB:wۍWsNP >f^& uU `+4Y^O᧭ rF1q+]ď,kX+zlHj(z_h\Rfw(P@iXڂ e4H;)FmgY_+%l6}3S3I-C;/@;c\C8Dga|:; !l'] 7^Pd-n]O)'5˙9.D]L+҆^RɁlǘjVm2c#V□CT6xa0̔0[˞cuFPAv|F'Ȣ7IUy+^"jOh>Uq Kƛ-dGWDĕEσ+2]s\FwRv >Oqڈf^'>9D癦 ֈUu>[oAZB9y GޜXҌ}NQ )@N1s(,+lJ1Bw./:6ï#7C%ER4ǪS=_뙆 =BJpZ +Лa2|$5sҔQ]^ +:v Y;ϩ?UcY Fq{<ȿp=5 d #0eD+z: \,'kdͣd)C2"[}'h)d=kZ"W9thjSiF0I~1:^O&tuxRE=y  -H 70#hJIWrYazG6ezpsѭYN5WIA襮F mGh2z}@=,d Zmal} \b)z1;hS0*n*?R! 6d ? x> {TUMXTP3לxd@Ϧ(mcH|$'谛`W [@G ?־c0jIHxԟ#0~2هw޺썁!{GqߝA;[75x0|Aw_s]3^hǮjgO5CHĂe&#N(R?hB?.Ȯ cl'~;[7:m!嬥Ek.K*z$W:[da0Y/Y}v#}uv3h3't򒂭OKIqLXn\* uм>Tddm@Aue#\4Z9A}),ݔГqDt ʲqe:u lI۱V=,eV$J[%,$R2#&D8+ H$ψzvڳLPzVGotڟvr>Jjj~3Mk0Ёd {H(oW]YO]`,JM Hv;mwVQ}h6\,i8?Kաjj8}{Bl#윾ڰv}iRe5Ǥ^ҶxO8'J>x1cam&4V~FcAnj减:Á JeWB%:9S=|Xvϯc?=dâۚԒ@6bƇj,IqmW4v0Z-eQzz%r 9t-_כ8t0λUTzs%[R{IOc֙~u\0rJZS?4\PiqcSd۪r8 3t>ܠ?P&fRvK$y VVϫ֜_u>g|̰BhSY$CL{|Sa]k\rg'8ǽHMVqj\ʠ1)t `f g6 {9%PU0a݊dp*{Yqx7u;~JY\^TE Ks7Gwi_nr1[|q8j5،Xn>g"H GY7-:H 24Hь>l[Gɋ'1A%1rK(8)ޚB|.}0$suuB۷:y>ۑ0C kb@K2:h=Ovu^C$"Tbi+S\#) ?\r`KN9S3:)"["X{&Vܷk}ϓ.jD 1M endstream endobj 286 0 obj << /Length1 1422 /Length2 7584 /Length3 0 /Length 8561 /Filter /FlateDecode >> stream xڍwT.P[\VxgEӳ$`[!l,.o Z'FULYqs߈{:#e࿴8;wT i~/=ܼ!J6~ k߯,^ݫkqͿB{!, Z¬~OP`/xejvn7Oq/ZX x 8oY#0 l;3  lU%Cʵ6$q%P)64j#V'#}VDa`j׮.+sqcO.:M:ɻq  (9bEMp)+fϧ5]q,Z:UIE_.k#-uOɟf-;D3KROePhu$\'v$S `Uſ*rI%P$#Qˡ&ˍo< 7fzifCNКQ` M Igya%820Jv>4jc5ӶN]r!~6x$Ւy"0I-'8-HL2:'񱪋js@k|͸qm6 ڜu_ޚ[Rfh$2-/+kKs̶/ آ:D3=Q֪TxX)IJwq:Q"Sb tSxPaS*l Z#MV%a^Zl6Ƒ^pP˃PYqe7NOitא~N >fv/EV;;ba)m%'.ĵKMUV᝴wG* sϭg"ڈ@j=(Q).aT =J4IÇz 3"?qv1`X!0 c}5<|x;On.-}U]!'eZVyW^C@Xf!*E$Ր ReHb|#y[M*c2 wM{CȢ᠓gsQgzgET ۭ;u^; đa^?;tD _6l4ؙ yvC}-,Kw9Vý[Tьn77FO1iI37!m'JA/*Vw:޹n$-÷"Yd k7=~ ׊V+fɘ9u˶Ij7=1ݝY.!&KA1 .[dN~mBR<7pSpuZA`wt zBaq}Nm_O+67Gf.竵qi6컺ܡ&Ѡׄ@W ͓9ʎYfɑGO^e2DMV)y 8-cIX~cQqc 'IW'@*@YSEݟ Iƍ-_[z{V1(fE}FjO)\'(攑Ro&rY۷ ˿0-kӞNGuV/@։Ы/p0<+f^30#[Ǫv9vwísK0"E䣫IiB ?xN=Vîkg*{"qcxZsr:gַ\ D18i`0s2t:ߧ; 4׳t)cz-i={]RD^za&k#CU¾򌆧J*X 4bBm9pbn]:hiJ.K[PU٧4}|pM~A;,NV-`o8w?5UϺ0E>^ >>aR_nD~)=F4e֯˓^q fAӫĝLflgq0B>hPErHˀ[u b =hԒn 6~e'dQ!''-YV<}jJ1u.48b"La9OkQ; _]So֨މN՝3yk|ELb޴M 3llD@;Cc]+m 6 KFF=nbXZڟ-PU >k1doV lȄ82T~aҫbrRj8\s+ή$XG>)CWqW`j;@iY)| yiqe(Vvm_1_ _r9] .HD7o~U&k==~<(\,UWiE&#,e0Qt24v0"9V6h\A@m.T[1kqyG}{ncyN7;t^<xp?mHP|sviT22Y7)]H7t~bO1 gJ<ERIS1tbW?x},g}<3[nrAb,|%bҹxDk m0N(&cU-#|)3ZGebqnћsmD AB¥~X\fDVp\}+\kB ~1qItݚlu[[nG%~hF>|֍v c ha̫Dt=H%`"I!s-M˹̝L0F6YiOg7i2́xeZO*AJ6ܬ/$预8q >' Vk@՛7ڗH`Wy83Bv'ȇUd5Kr<9"=/ ~Kvښgz\~JzKGm-b9k7LK2Ř1N(%UuzVeH(8}Za6ꛔ<2-?R-@-Z=Oz}Aex\aY.Ga+88R[Z1W^S#mgFk*dLe @W_J)5]xzmG.?_#Ef|Eˑ*43 È͆2xIKpΐC|8K*݂?$Y+uXz뷫q͵+ozT|v-MZY> <)M4[of/&m*1lbϮ;lC| T5$OsFN*k]jp&h\kv])TI8 33$}uNײ0Ͻ"Qr/ ^Tf)ltypVI-0?pa3Y{-͇|ggٷ hg^zY]HpmU>lg`Xtx6)4ԛGNKئʐ8V ^CT䓋~1N"b%'a:bBVK Q-(3[E\b\p]h RL'%gz$0&gi@-j?r`%JKQ:.rY&x[|z(lKD{2]40gɥx_VJZ PxTK,Xx]TTkâRҋ¾'(FFV;I$I;/քcA߯eesX@,#qD >ɤ흷/>?3A-fR;̒n|]eL!o`^4ς18>N@?z%t(' ]mdQ;^I~aYmgHRu|xBX0.oD9`k9ӆYl*&;n?4rR({|M:ijs5)Y2PǪA:_+$L))zq ΞX(Ƙ/hSjr REZv~5 d!W_4j~QޢA<ǕCDt +ӪTIȗ\WrL m`K&GFmk=bv<~/_ci_upH9 GL3_N|r۶sD0%%T}s69146ȷ^1qAAaqbNNTvRɦSWVM>X2:Fp,`1cGAe?ůM䰥b)$Sr"lWHZ InͶ(#Tg/f?淫~6.z^㸆.iq:N3 eU(1W{;]YĦ CWe8.aB4HV}́wbڠW Udf6Z}uvB N" F?tw  ppuWd *Fv=jy'mRhˎƋj}|y'r+S7i!^iʓ 3tVugLrZ0fb2I٢Ls<2-eM#I;pO^#;z 5=)+/&f; d"VhWe&BP+qaW:]9b?{do"=d Ax e}r7*DG}!/hӢ ᴈEx6#fٛO^uȑݮD Yzgڷ8 rSmm٨s٧iB5|9xoEoCo]'j^+t'b2+չ)\DUqC['rw ǧ 8EM30Jjy:g|I)i>cߌC/M>Ymh5|F+yo`⢣tT5"(̂2~Pg>Ъ endstream endobj 288 0 obj << /Length1 1415 /Length2 6649 /Length3 0 /Length 7619 /Filter /FlateDecode >> stream xڍTTEi*Mc t&-@@H.HH& EAPA:H]Pz.]E=w{k֟f7? -Dy@@1 YXah8/7!! C"@ Ac|4DT$$@迀H71<f "P! fW $**; u@ 9!m`P?JK8.b||g/^ C;t(k`&g2^B ǯC{Bܠ"P w- 9r" w!~'Cll.7 a-Eu^AB($&!!E3_l`.h/ 5"߯2[V@!4W07 ڽl Devuw3@\ݡ*A0.¿}P4 @]P/_]_n~.Hf#EA<;i@[ ` !qCؘ_ceD/*ןEz|y<` ˜цjw  R4B `7oUb60 a; $FAVj swϨ Q f /PRyAmah?758 F`>., ?b8a> ( 1 (пˆb>6H_  nnoB1 (>^I`f!YX>&|?qwst+e>!Dڈ8 i˹{YO'VӘ885oO:DY:ZMvTOVfʊ9%:\r|=No6(5mnfJDL?D(+g/7x kA ѥ:YԲ hkǷ,=Frkhd:[0Dbt:tng%t?t~xo WR[,Z:ۙ bhn6r f8QPe x2|( O^[Z0{e,w2Hyzeo9[иHSkwEMJԫvmwr1L2 r9!~]y$ȯ.yukȇ&~1V&uޓ!gPқ'}]ފ]fRᎡ'%X3h8G_p$%4)oGp]ך5|7X嬨4>\XLKz?/vps_Vlɚn7t }O /"L%Lt.j8#ԶH8?ovyU[5OҖC4=q_Ƹ`2O汕A4`Eh䖆.S [STCݔ63yoRrߏ _ݿ47~)uyGK]Dp[0Kzɸm;w?l';xLs]]C:TȜ]|0{ѷmL+U?nǛ:W\VgYU-gV;0S1+wI[*9^63=΢'|dVprݢ,t3Ng7LT_me16j)ʮk :1/^F#6C<UlEoof'4aw(hL.'"`I۞M7%{y0S&8f/z_ݺ CyJ_^F ;>߷ ;7k'. O4ѡo,Kan^M\ץߊg}eN}b9rl!EO,bpmJ鑐خu| ԷtzU= fyQ9;:N9;:7Ϻ 5[&٣rxYAqC@lGaiEA;Kf@{wÅR{RS&,Ǣ>Ҳ1|Ԅc^c9_4?'pM]峪1H<5pdwn:8lV٣b6h^a>[4vvcwe7VMjiy7{ 4=Lcj^(՜ycaKoI_dQ \ wS)P_^\K{4}fva A^&KA:L.b9$H? ʯ|[KƦҋ2Y Y vbi>J0HHȹJwưn.^!  %(.&,;QT~^?=uK&^NyB !6fI W'GgH_4'i4px)-m.l9Z^.mUajau[͞D0zdtS^~,l',=wrڨwǕfti-LB27g2_FZ$KE03ES)~(L/6صNqOb/1sxƕ!E(,%}.HSuH(7Ȍ\J|]*@kWhU*4p0]xҬ=tPJcI7S +k#":9NJ _M{{i5*ɱbsDnt?ͶX3}y=TV)3+OiaXժ.X J 'ya@JC_v|O),%}2lyz%?\Ho;zs<{PC&[}pwpt#-nNlpEx]qEjS?,w{) Q..Oɾ@%`耮/ %8K"b*}XFHśr\&KOyuQeǒ뙶q"ǔZ!E /br6H8d"&_0gezJ=py{YI`x[ .-ROu>2^=ٸy}**ٗ;Yd]IWܲ%»J!&?+VjBKMbib%kQ:Ro(̪/^nsWrCj(/B4ttzi 6ߌiȝ:-._T-|4x4&tfd &}LM Q/->bb ϋ J'2:?Ž;4Zbx@rd,KS3;[KzJXyMʭϘ.! YҦ[zЂ?em[-Nsz#z/\5Y8~e&՛o Ͻ8M2NJW:[cswv큒ݔfN@#fV2Nx)/Lqݖ3R.Sk`f'fk_q ޫ|HR}#lEʷ;%{wO_]%cvx[OswomE }c"Izf,KߥN߱@m{eReGA[63E7G %̤n<>~,|ewMQvM'QLkkyI +Yo#vD˜ƕZ&* 5ܳ"0w_@7KvY ^9!JPqJO;Kh뗸CZuT{J| sя5):Ģr,Ϟh#BtLD{u^ +WWgâWNzw Ew5N,ګ?$ӵE`8]s؏QpӚTX|U-{gwh[٣\+[qM6E"#Vȧٞ}P !,x$DCg" ,uأݞ(k& +HipʃaKA55:8\jw`ݬ~w̍ػ!"OߠdBڝ?:۵c!KxDy,xtMm[ՑЫ>/LCiSቅ 3pŽ&w{/ uʦpJߕx)sZbB'FMh[R :Q>fQejG*ƑP.1A7xkLa3Y!Z˶*Im4K ͋3dK2"ziJ-FN6rU5Nhe!TQ.Fd2e;c&1U($26EH,k? c2g t"箌#>1bI ^tZ\Tw= !$ d//a6<u+- m]ENRjZRS-e/^b sM1s7РUf?p6zzH RqxcVhk5F@gESdА.425 k*-S/BTk2#$^omꎚӑ Ƙ!q4hxKf-Npk޼ E븽x3SOՅoN54Zޭm6,oju~ \^}>°Ӯ­N#\6Kgs+Nς endstream endobj 290 0 obj << /Length1 2003 /Length2 15636 /Length3 0 /Length 16876 /Filter /FlateDecode >> stream xڌTm6&ۮ+۶m]i90ɶ&56n}?}mױo;XI^(aoBWaf01201YÒkxe 4v|d\m̬ffN&& ;Čݬ {;3,1 20sss:Y],'TM.Łݝ֙Bnb P:܀f?m1c%YZ9%W7wq7v>6V@;W;3p@h_tk`f`OdePcppۙahllofleclag ae9:Y9838[A0U3ڹ8잌ufvV@iM>D,.v&&&NnV0d#O%@_+s?Xogc7 o#Xff haeO1/|'+.1ϗxxcg%4tehb @`c8?>|;iWWe߱?gؙL?~GO #mwB66ƶV6| 6@3+Wv1Xa; Yhdbj״%WclJV\+/j~:?FOcsHq;S{?V`d fE3ǟC `dwp|;QNfQ?b08AܜFG7?}?f4Aǀ#:;[G⃤F+y~ B]e_#WAvտm?X?_y}#3 21-u}cUя'_ֿH3r( d?̝?>W;?/QnG֞$at5֦N\x>z?g.ۛ[=T ϐj~^rjw}BJ pNA^Z&z>n kIRn}y1LPm](<OO&S<ѕ Qڃ{]*Yih&s$.4Hw3D2 '1E:,s^kj,]8d8:෨S"2X %E|EDti ,UV*Qv5nK;y)`JFtC'EV(4@s;ns4eYmcn:ǵдƑn_4►+<ie.c`]@itde>K'\B&&.^Q3C0+Ur]GMmmz 7G>Tz*OLSB6)(TW+*3"RB QK1;u<7/5tX:(`N"~NS`7?7e/:q1k}b=/x:SpH3p4B7CyeA d #I֕9C]*Nʥvjp)!ކ㹿^K"nt˷U/{QL?esGܼ96z`N$8jڲ GtX#:"9+T,L3iJdd6χOMIX]dzᨇA\h rd*{Qt:W/,}^뮉ya44=?޹;K6 d1MMֻu,ݎyT;voOk#3Sm,_V*d :r$3Ƴ?ì=$/TstJ̔حoHv8y?OW95{7}YUPA$O=3"bt'}`:@$[Ύ]ʝx*G롄2qjMRn{G\tªbDȶOQs.vՄi+͓Q1T_~FmoM;8K>0_;Zhp} o.8*ҖE-; D( \*(}jXT;`&o~>ph3i(o ;?j ]rA$.X= IӷM&%u^tm[&% aPmT?-jikPZUZ¡?X~لY*3^֮p.iS1F8 r^[P#,p.m72)2>8ڈ|?o(d+d6兣>aX;>󃙐lg0*Nv?{l&B@Cw1m,|RA1gӊli UQ_3/ㅣrQY,MA#J^LZ"Ix﷝ 'h Lb,Xb0[h7݅:ʥq oL7 F+iWQX>/a\VPXO+<5fa׊lx ]EdZSKi_lv $^ZNBwT-IX5vʱՃ$ T\TT6hSgF\ ׮gK$Ot~^uSUyCL_QpP5%<&f6o 1(S$^_Ӿ;]u#d&-08pGa5iV2? ޗpL~V͛-:M x[V,]dV| XA7PݏVUW/ *nqr}C9i\^ok %ȏ,d^{D'ƙ; hiYG!dyv0H<>ˀ@s- nlE}-dD`D8mdLFUȳ,Z&|8YO9OlJ 2@pgfX oTJSE3 V%}Dש(eHTV_ut;..-bF6kI*^TGYhIziӅ`=5K%r.PrvŸARov#.\F.*Uv?dlEDʅL/FrU+ƅ/n[r-{ؒn9 9 \_lӤޏ8^gy'xAEycB3h41am8X8+}5#һͤsڄd%a/||˳-aWHXn l4$uBXq1\-lJ1,ː\G{$l7bǃH@R c@%6< #(͗:Iytdm4V뗽Rz$3?t!SOMX8eZE^",nJ=0]t+ƒ$xTȸ9)Ÿl=+q"݆{4KTD TDm:=~-hgԲw~ ZTWh]ٿOn|v,F?T3L)}iE ohaIRhS*͉j@4B˶]jT :aȊzb́8#Y `_C*QUB@MCB(o~3] ُEW1P8ͷ<;.dj=~ boXQv-LF|̛gb#'ʜ`MuLXs. ܉bU/PbBSec!*<bCu9SOMVeuP`|FP,GJVl4Tƨvޔ@j<ςJCjȒW5>g,JB2HgZP,GuC,˴w:y䕴%K&y ?* bO\ۘ…}J j豚 #<9_U?9r٦"o9 Om>#i8(m[DHU"- - ["_o[ڸBӳmr=ǔ"5dBm>7/FjmT}Jsy)Y"-MWߗ&8rAb(EwX"dž(ރ+-&|URX%2JQKt`UW_ݗBI0u~6uTWqN IW&WX}l*Z(m^6kw^ogzFNAW}=w0>0 X$㉋Y6#R)_$ ӗӡSoXnMp~w}VM FS-5"qNjP s *$Mf.L˴ǒ,Kl M}'޽U;\g|o?o ؅7j c~5.c`1s"x!Rj<hcT<Ę>kJ8 zzX r׷i\*}.9+ 7bNuTj@>)P3(Zs`ۯDW% &!+m9$01Kaڼ-,ƌwb]$zѴ<ך'h,X{J2ƾe.\etO:k^/JL8(Nk)$9 Ucdqyyt--uh^P5>LT%~=y7qd/ ̊*'bײ6a4Ĺ  <폽VheTT]2ZU\5mJwg w'~XfrtoMu@-u˽J]YB|F;m ߆߿njh!N}uP:3~Z I2&{7C:׈(qzo0N Ǜs DrxWJ:dAKzM:SfmrB"(0~EऱЃ_ &@u]on߁?)L@QBfUW-}%5;v3I%s*(qսTWNj9f,!h°S @b0.\GIcRN.FX3{w6۰r(KaGwLzOVb`X<24Qz ϰ3ذ\174/L"9D9RaȭFx|8RzQ֓;uZ/ YM3kҢUpK(A6g{fDXYpIθm 0]ΕOHoA\*Zdyٜma U8>~yݟfI/2Yz?1 +0MO_| tX3O d ;EbTvMUʸ\ՅLhg~}T]]S"hJ{GYsк*4,w/ŁaڎIf?4 U-  #2:i;]ةW۫()WLZψ1< i52K f,@gaMHuW)jRL2o 9⤔g7V0}0|‚~ƐHV.*% 9r֗=pK߄ۇ'~˃fS VOVGDNIcv^|9T O1"mjE *# _59~W /VbU6򒻬·2B6iv#6?K0}|-#J6-цucfh/ *(Ūl ;C7h)JП bv 9R]#0/S|j+}&=NP9wM\ߥ߷(+@t4·akT/7,܆mO2SuF*l$Ev#b2x ·@%y"E˙آnꙋcLTǀ/:#!DVXbE.hl1Ҩ|C y_TR(*aj*z6L `۪mq w+;5T)~erx9Ը0%*+M \~_f C麃أ1iPA^R*ˋjI]ZȒivS!) Rqy̟ud'zJݝl3Q 8!‡B[G(㟳]@ҮHM?VGz:sBҞyAދȩpUbw{6voqʄWED1hL}K[ߓ!Af//=&/^tJBV:5۶M=ÓBǙ6;9$)5'\op1>%nLIڙt?Ӥ 2%w1_h1E*fZad Ԥz'mPan4g:r%R#כnkH4鈑dum^<`S# Z~pF24f߉7g.?M 0*?1^Q RQ!$G/b`J) sKB|ޫi6Kq(sw=@#S)yP4$X9,sڔb6<'GU%]utI=cFN7^XUYNz𭺘Q oD % @S: w`, 7\АϪZ&1",#*g=vh lz&; Sr'۱O T Wj_wNXF~kL~{ "u+jP%2i;DD;S_ erS),Q),]rեѪ:|k݁f9J,X xbʍʖB&}"6C.EW[T/-?9l1%s}G [)gJ 珧~ke84**`)|ĦKtS4隶]JQ*ۣlOlnN$yHI$rO1%.ٙcH][N#֧v.aktu/ɠYQ|Pwt֓íRdjƷ+Dk~ Rf* QR2HsNܳ(Lq6ۍa-RLAcL͒]FqUe䌍) E6ݗ+ޝ; 5POnB(?oF5Ҵ1`KJ=_PS{0_E𶟕aY;=S [OS:l((ZoX@*KD񖽮@8iu bCX|І%.dAۙ4mAh/("=T5>he93b=&fYs6Ea mDCt<;#Ii|Zy;׊ Gc/l^U@ ( >̳?C9o*jjwmA\TGTfwșaZ uk܌~Ӽ wI V[6(q8Ŭ wXԻ+H 5i] } '6fo|pݖ.q_3iȥlpmWS->&3_'ͷF~D@ǥwE_>بv&R!ά9AC`S&_#z*fK s==]v[wYHz .d1RA'bI4rD˒+Ag# ̙h =ih*OU R+f}:9&*Cn̹ߺ:h{ xKhoYSxrNF8)\ḩq$Gt6Pw}vm.bk :=5eLo(sk]i`<',Ek~O#09;\t1 ޙ!~׼\J26"D똚*{GnM[\A`Y戮ইOg*!GJ[iF9ӈY f<_ 3B;@H]C_#*z<18@E%}ՑvVT-yOv Vby V5DvBHE"ВSs??+gFLĉ0xKz+樔 {ԭ~V>I[ =6E"qQ3QMƢuv|O6|ϯ0kid9AsӀ-HJ _q˨oOU5~ǸsWA_gp&31ڡpApbڒ@A/\{0#-B~>)wnՖuRWwu iCHw5"clȊpAYY<]6EβY  oԎkm_}KqlhʉO6tY,߲Z8Eh'b ػ??}Eg*dDBٶ@(KE>(3h8 LǕf`Ad?eiTWQMf g;|x*|\\3@8Iq EFS93; n(L)V;-`kDE+g 6r2&֜C|+=YP 0ȥ:.YCқ 1] J wS.Pj``x]͸?zgo}{n0va)g[zK:r|krr(!$︗ 7N3P5L}7~9fu Z9u%ֵ~&H74v9-4]QmZWF5?]fV@8U{{B o?0XWPYćЎKMY\# ?>ȼrO^ x~?1E fXo½./OV֤DBWq|oXݮ3DT j^Cz-cՎTTÉ@'c@{'~u PD+'ŠNum-$fH9emڑv1|^1cppy>|5n=W'!;ӹ>p< fP`K UG.DxO\" vڔ@7?H" i׼\Y=_B*[٨2 )|o&kʀ+N OZf-뗧n:GoBĺte*subd}%_=oivߐ yFw 4f9ݹp */z&x6Kg>Hhү+-5a\7Je biag*A-=O[ 1'Pf*M|͉jL$0WZx_KﺈA=x9U9 WW2W9C^].he"'վo7gžN 05*Qg~l8;B$jrN] ,Ue'\aj * :ϯ>p>*_BǥG9_ڽh ;NG2.EwHn,gKJOf]{yhWL(X<(mSG}uIT~wn}.)ϳ?c VܫCɫL{VؖSٷ']ZokdU|\ 9EaJ4ĚPv: l}[MkSQo9yop!G雯/wf96I^8oJJDm~k2AŸ)ZFs =7Ϯ"$}f?n-uHHcYV4xcLhL]  X5)jb\{ *ru,qmhրwzhU {Bˀ4M=?G&x[=ky_uouV;a}߄iƥvPBG ;~%8H1ݦɓu,_n:&50 Rg,8Wp)M$[-S3׫s@gw" ;azš<ғYn`L/I-Б&%&1"w/=y~}*Ax"9ܢ3::͙[EVE@8&Էfw}1淆J9H-K:F@gܬ΂]X\3-kaAt摈i9ezV[j! 0ncϞTx3WDVPC?+$Ec-4ܩ]n'ڋR3ET]_lاŇRvz.`Nuѿf1u40R/z}uL Px('e?'St"-yD 5m6F{pɻ/Zp:n~qrAZPb8'h936S6dkS(JesnB529$޻- w%]X >Z9Xq)VӍiR^Tv<Fn+ Q4d.H+6Ldb4F/'T6+V A߭oHI: Frj{1Snd=&)zɮ=d⮩;47H3A_`/I~cu6Z[~ZOn5 $u*9fKu ww|#@VCfd2_[vlF .Tek!6DX8 ]9>16~FyBL^j#2^Lv-k(zNҢ]-!'}ːћ~p|D,xO>HϱJ fٔ")# QzeKPZ!8%Eq#D<.#bڡ)nut*1Ǥ@_Pn#Y މ^-r5Kia"qҎv"DLF 7Xcm!KpfnDn_|e+vrS%ФA3^V'`b˦EE 4Faǣ,Wcg7um+HBf9F"φD87ɱQ8+Px$[@Ap0hQ^?ǂNNjT~8_ѱrFf X)S@HmD}G9?=Պyyztۢ+v6Û6E 4rF3{Qea|_)d"zo_DycHF9$}aq=j̉xngrulӘcAZYƒ\8#l9dpstj_xvCBYZh%]y_&R{6aܯ#CvN34xv|Vci6PZ&izUn- + K>,@иC_L獝Ã%ˡOهǰd*:ݯW" 1oj,YguM[Mue=SW؉e0**KS<L`|aip\WBC`qA8Ӳb=& B#U}(:Sľ5:<[֠QH֞b%gVkAnȨ?}逧IH̱AOw!|u, PȆVm\ +qv9δ9HL]@a]y9Y eiNQ 1R\둷k%ɍθow>-1xfv=E^ H^  `M8vZd{} 9/h(qAfNY\x깍7wut\ rSgr0lǰbNtE@$4:]W'֩nŁ.%}fŤR\;N@+TU QUouKW60=1"=;!:#2)\ endstream endobj 292 0 obj << /Length1 2580 /Length2 19016 /Length3 0 /Length 20486 /Filter /FlateDecode >> stream xڌP.wwkp@#;N;w$Ang朓{*ڟRiJX:enlB)e N.7 -1  "6s); Nn''C𿆎.Bi3%@ J9:ym 4``p p,e37hy+;'+(# f]< TgƆB в-tr4s { p[]r@ Xojd_xlfadV { @UVˍ`mhf70ٛC :@VB`I?Z\\ASdRe| w -~'aĮ 9cY<3ea;/%o1$_'G'$ ? j}7BX,@kOth74x=N}2#_ePVRb;$%<V.^N(jf㏫< a!U=#߱T!C 0qC^ /ϓmdR3Q9cYw7+;BMu2nf=[ WYR fa-d 0PVrrpd, 7+d"R!oJ yp@ YEK_3 `g;A\V.(; `-% ~$`2Cv?.qAv?Aؕ aWA? A a zCљA],쀐폜 H,x!,!Q`PD'2f6ZR@*eB +AHC?6wtwGt? $?  I@H HR?y!`Ca ΎRCqs?7t~0KrG Os [B? hin/z?94srB<NH] t{y=$^HWK s89!n6.+npptr2L%i >@cbpw }(+l:$HG x zL6xi~H2F:}8Ma VA S9 Z^#?|fcޗo:pȴEཿ9^jkߵ^ st$ЧnglIqb(١\Ѐ%| EZZWL{;TҾ,NyjGh 7\k?TJ XlVA9.W켏7 ,QTzRWH{JWɣK^,&k0FwRfm"sl5O'Sl{h_g}Tenph_%4C9¿Vw}fBV<ѐ BR7Д7!o+GL4+ 7-{q~T(o< ɤl"I ӼYz]f4I= ,En,i2O ͅsLtm1|4Xf} S}*@*d(^<o\1"ڤ2d1]m(Zd_ezw$]IWD=FwTTy.JS(s#"{'f[k[{r7/bB\n+?VHd3)=o)cuȲ)a~y%~0OW¬1[3GW@s+QT,8ڶMk/ &+R`>ߧ~0W1JgbʘgƫH?JAϞ2fKV- 8vSz[Xŵ>.6S ÙY=HZj,Ug*͑ixS?ZRviBfFe53(@<( Ьı['N6{1Qao L|2ZE~hg ˾""%%BE*V>kC Bsgo-rJZu%OAECM&H+5񚸬?vPNr!ՑdmJ[wzWē¢G} 0&{8KӨ" 5VpIRq\usi5<(ݜgIU7^[hޣ[Ą<Tv + VxU_:1ɪNcnR _9jx%鏓8PшkqRDb2N;fXE k~g7Y!쯨_Xl(jl7!c02CXT)yXe$ Ig mp8E/i کq+x UBᄚȎzSשe2iǭ9l҄97`[h˒2+~iļ)O CT[G,) B1 E!Putd]6^tEsV2'I.pl$E~%i*kyrRy[~Q!vXsmEk;}?S• }rʩt8ID hN&cU:cǫZp1[NGڝ9z9gӓNF&?X+B{\4L~Vje-od\bQXS-"ߦxb,W3)>:ra otT DՓbGz)'wt]Wf'l@vH OPF\uP'qg1k9dڤ9{˵$]bN@ -^fppR"Y+Ak^3]c xঁ<@U7W*׋ca+J)) Ga6შ?v$hp8h\9O)ɭ&w̡!YAX3yA̞kϙ̛$HŽ־Ӿ[zC[epH+&cYb j145 V9H:ChŋeWEC!qȢs16\1w>w[XJgotM Rp~ xݗB-zUe= NXqorF?uGxŝO~A$vBB>~e> @KChWS[;QuuZ9f+ֽhػb1=J3c5R ДkCA )ie܎5ODz8l5o5Q{\7Z{š;A=~ _J/OPeoOE~h{vRW|D0^Ѝmbc]ZĊ2Ḋ5 Q)-ecK%IV#t9&58pKA*A*cOpYm ڬ+-GВx)9o9 ) Xzʕt|mN'}8zYOm'$}oʀM7 S*-vyrTG)VľɿdɐSB5=߀9No̬JBs$dn4^Kb@og[Li^3i4̈DF'֐yM'ҊYw/]^ -_`,Cr3"D|#L0G29L27v͕ekM4+Z1m!NX֬ ghNzz0+c.'DJ )*CE(HZ垐i |P7ؠ2CXt|f]_5+~M}V]bi>'>;' -;sV28RJJĖ ~@̺p% _*>2-uTz2da}#`gQ.%16.d `:1!R)-D]ĩRQèFlCWq 灘Oev:Ww7Ѝ:xz|,+@{ܬ35A.=וQwurQ%R Pt[=Ѵ$p݈YpݽNRUJK$8nnn5~;FPʈ#WFH`eDжKho)a[tt1?|xTK +]\rFx_b1Ќ&rL2?Jx}7%5:6`> mkg \LqWd898DvЮTmBʢYwA+-_D ؎LgYW?0a;-j~* &F;dN9F[/}L ;NoVY㡅4dk~ˠ;}{zKΕ`kL{Sϔ0@kF={i=w ܢ<96rs.e`5~4pi~ %&q!N3+D(ΐ %z$ʵ;G϶;C+0 q]8$|Q=Dl]Q(L *l:&U.t_vE6LÓ>1ҪDb4ڧ)ψdF5_,HH\:UlHd0 HRQ`_(ư2(<͉2#F2tx/mOK.էhLA"c5}ĊMNM1DY㍆ % m]g 8:͔ݭ'ŔH`TmSs YjnQ:n'pwZzS7|&,{G_xEŋ3lg8N pV+_bO$#3QQ[іg'OPG??$t/x4Ԭ8yg O|N.rr]뉥paeGĄ׉j{0VTCf*6961*GGys~kED|yڲgss9wOlDhϭNP?^,^(ߖז%u6[':4 S zFWas 5^Q60Cz{&uc_b[qQ=NK֭B]޵ә6,1Xlgd[M]q#)E;:1˰/nXN102NPBRž%4i9[\ņklῘW8Xg,QpXg wFAA.\PFLQc(i W{"ڮsdS:љGj*rþ{QKq5(ٕ݊|tT|1HTF5{z=%kYe0"p!WMvWOBH DaЇhn)ZFj/&n@- |o3 /W æ j)$oKԎ2Ғ,DqFS}$``ceނ&фc w lL vY`j(_ʳm'ACnu\:"$Cuͬl:pzoC0;&{@zawaGXs{H1,.O&ڟdiʏ5.)F B9H-$b&fd7w+FrDR%~+!6tn\(V嬎]]Q(#7pY{@><^:;\h/BCS+F2:ز[MX˴f1ג?g0TE[Y})#jNVՁ9T]ߨNKo1v+kzf vx?5i˸4aD4 )%1d;Ik-2 i?y-7ME&6C/ tu#U+ٞB*pCP&ªteXc,ڷ([w38o:}++ U-kAGX b/쳼T|+1ti2i`mc@(s`κ+HU}[~u-aQ{0f=S75]OjMzink_ʹ b d~EOX1RIHw?Vx‡yۭjM>DRGe;a,ȷ m4e=D^JcaCwc,O|Zw:x;ژXj=ǥ$Oq߼L.gj#3PئK3@ZX>wM)k6i,{?pD- *>S.iPBz PRuڲRݒ*@7xe:{j&_?"<^CY ^ eD\hq΢VLL-y5}y7u޼OF5O΀݆W_,+VfVkB]+핕 W{ZjBfZp铆kߜ} ZռFR~ ]S \tRΌt K>c?[<(ݖ`DA$5hL[;hNab\ġ]AhʗS_7(]6E?o0Ag4PAk YV̲h2S9l. `DfA@GW n%Jb+Z5yEoCL xOtm f)~;$,;7p0hc}b|Fouk)y`Rƍ*`WgɒZ ҦZw )]`S`?)Zb#F,F.[d|%;RW8)7"p̥VsEFyek"8Ux?#Jm"e(x¬90K*BmCVE2{it"q.U֍n&E(vP$_r&-7`9׮&AjLg-gHr"RZ씁>,ioa$uTP/F< С%QHoSRLRu>-TD^a0MшcI; eTwy4e!3i{'BO+}Jc_f&YceYr(jޣ4n;QԌXk)Ru0_XE*,r8kW٠aGYl",JL[v#bChN+F$q6;u}}?rd7.1I(_S5txj'6?M}>G!w(-[*QNEH>zqV58F-$@o"c g7ȥ+2QL0{ P Y}o)L\JjƐdۡ_ȷ7 v$θy4]bM];VNEii:9lshÆ%_2''ZC>7#j|O~Ȕ7UkӠPVf| Y.7K'O"=Wh0l4>"p#8:Fo3 t?Ar;QlZO+0# [W@hF,OYɉ"U?b4AW+/?4{A[-3!g2 tR5g#6:+~zf;T`ltmNEiYm؉A P{?m8+n_}|l&,Q!vHX4 3rBۂ{1B2|CW?#7է+{7Hc᫦W8wPd6>MxS8~_cf3RvT?wpuţL*6 T2L'kA [^BKUXS%etr݂i% u^@\A@Di83xMUUԗ̖o(4»lgDB/3mi\C9+#sBP1B[>u9w}=QSbnܾJ/k;9 5aHSW$(D.j?J!;Fmd>6ZY 8;,wM`Y扎L-S;W>}x㞄` ߜخ3Ar&+"F{a:XڐY~ x4KU\N&GUiLZ3f:ސt NHj˻ &$Yrܖ]̞W[6h$<2Ly1N>o#L&G#Йa̷ԘނdlY (QH1@r5*6ҵ ̋樏罊㳟ȧZ'EZvnf㊹^jz%*7,vɚLͤwŶ 'zޑ>ׁj;MY󯈑,q,{(!8sʩL'ߟ''qʀ]1͇\~g[mFSvb}E(+믂~1|d s}GjP7Ẹgg>/x'/2N<Q=s^)CBI0n[d<:hzJI)I">FsXjB02AF d'IM1sG C=Ƣ]P{S;%~Dlo4RE̅,AyZ}Ա|{AH9 RuɓٟZ=ts`\~d^5 s:38{{#3,!O[/OǬ1y_4Ĭʝf*N71_:#nQwrV24J0M` ġI9olSɈȩ6%,L{4Ak_jQ@qχouhS!I!ǚ'Q;#Uє WƟa\SQ=QpZRo}sAfwjo[$ћ&[=NNTnf&ޖu2k 84DSC6ͧckPى,a˵D.և}pT΢^"u ?-*=ۥ)@L~x@5cO@ SEjbPnK!npQZ c*{iSL~o6H68"y[[5ޗd8yl<*šgCBtj:sBM581҆.KU4gu"۰n Nɯ)ɤ ^KwY>MRy>leQ3mnOCp$2]Tۭe׼rMY|vF1&58r>v{v^\n 3Ҙ>=R?WW <Új3-Zp0B<(Ta-i|/LwDogUXQ[4oi};-?ԊvH}yw|:ޏ>Y`>e!m뷝[FxDl;L V-256Ma"'y fߥ,XcƾʍZ_ ZE+q(]O|O 'm, [yYHtTtŦ Yɀ%hlZrηS yCσ% -'AP7Jm.疔nGEl@$ Du}%Ŀ;K&V1)2eǧF]Ѩ8DЇmRܗe] t7/x0lk %jAXWyz>'rAp7Ƒ93)&ei]}yl\zv{VF\@xO^U4#݂}C@rU|DbX4Ͼd@Un Gf%X9tCC-:x4x[캭g NelA93R&b>D '>T|9p!Ud`p\oN3ZT{*xd%|F.x۲28{m @ڋy \Rg9( ЮfLZq.E4wsP b^Ti)^Q4{%8"e6T 6-J-,4Q0THtE>"X80(8\jNRmÖԠyb7%> ؀ f sk|g קr8NΣڇZ/\(6i8վ%k^sr{C+\Q?rEt2!{6Um[(+q vY4:hoCUO4џKڏ EAI$H 1h+Y-[?ې8UG~ֈ)VH>=(h^nVhx^<=i ĎK'=zB Ci_p$cϯu b:,Wvg|k?L#sc¼d7OvTg^ T?#2ա'ީ%'u"D@KV.☮z`=_6.}jnI~` V{ x*X#Ț8XppgfjEvmSNzݞuܓmMR jSy0-f)ףβw#Lﶾ81@RǽG›t `^ DL4[>G\̓|E[ fbsdXa䥟}+ӏM_AVܝ#\3AWS~Ч{ L.|gϠAEF|1bU0*(8 ߤ 0Ǯþ2fa_aeN|0_<)!DG'RqщS;3}gK@#RM>*:+aE-Vh^&Jl Gȕ7=kK=f]jvmyoLhg dɔR #7FOK"yQ2z>eQ;i5y8f4 "{WGBC^hp/[JAذK#p_m*_拳Zoj0 aֶ7¿.nGC(Cޫtd 34x{kEGY[_[{Ćw!G\NOgP%{Vl'":(^LC!&&ݡ@!wCqm"ʔR #Q[`HHg7ˆI0;MKPbε8%7+|Im?Th}CJD'chse!] ȗ2Ha{᭖ꂗ_sv# Wr`34_C"F}BhJ2G2uBdw="I4kPyFЌ&ѝ_蜡wyIRhDxo+rT^EاJ]\|4TEsx̪6$q{6M[stIޡV̟D! 0\v'2nhSf}4H1XC*2V$vP$ <)c}翠8ke\A[|+Tcy ,@I_,)\% YK5I#v'g#lD%P$Ud\Aex]C7 ws?jiFI/IW$Dx<ƶ`*Ȩ[`HBIv/(ɬ`X|! |mi$F3 ϶k8Cb2u\0"_!a1i!2ðk}=طtSJMM Xc$Nm\9EkwM1t33  /OᵐoR=FS&h:gS6(A{W6o]rDI:C58F*8cwqιOY7)9j_},gYscNlڞW؋A|2|sE}]QAo|  ʻ-jDU"Iy#L}sCJ?V|Q}UvPv7mQ;vSFne#!F[zzIv;U(Mvshzg_o-fceJGk6 _?ƽT4d ;2EWn Cݝ,ҬèsDi崲.Ka#s EڷO3-39.o0cmpKɯ'/RW (>S^c_N! Y0gziӸ=>6i_\ R`=Bc줒(;HvUùl=,΋Th=L#'"6P HW á~;b, l\/5뤐ZaX!F Wr)ݷN:$[`ǃ`♃`Z=Շ4{m/]aa.j.Ta+"\÷}\GP[X;f'.GF9 n Ci) Z~U9APl{\B1E'³8 ΞP8(oك"1 6T ,n!Ǧ-rmIzܞ:2j:4ܶ=>6-A)_q6^~7iN/qfv_nX2xnOy^6;5O]df HQj۝_Ta/xkڢT6 %BXaB6GK[ݽ"MSz/61RaٟpNa] 4D$+?;#{=/6r&#äXkTӄt6ETr&Yk>%l 7e?nA=y ]r6\~ڶ;?y'a>g[x>s3;z.}8e=;Uu)h](!1g,Q%57֕hЋqeRsvM]G1TCu!X&23uZ,lTzM,+\ m9n}6TNL8?sڒ܆[ԅۙlصZ(>:LJ).luV)@^{eS%Zã]2a_AY,G Qm`}A.6SIruҟ!tljll$$.tjYūrY΢_/=oUfM83sH#OF֐47gN-m2%H qLK+qa aTo:csVU5%U@rQPK] 矧WM=ahDBò gb9lS@\k 6(s~ 9^= W@$ַuYtC+ jx4N6; D oVxfJDm?{.~+h;O~]}{Ӧ01KM%zW7NM;<`^ ?N_f8<@/`\|̂/:!#7*G *\97m'+!Z.A{=oB-*`jphˣȉHH)ߑCRG5Ž\[>.eȧ !k:aD3ܛusdj.lnͳy1vy"6~iU8}1;9sсdl%шjEј군{'qM5|`a}D *Ҍ}ߑE׾};;uH^i,"ibQ5R,*y&]CXc*U,-r םQegA}Y6iRq&86Uu-8M M]N∖^c QwNa);aUUqރ=8=-GVL9!єWof 觱WjK<^_@}S' 8#uHz1j=vL6?P4)N4LN4ďg]+4EJA,RL,YY꟥[ f^VwL+2ME+h%4lN"bw&@tx^x9FB}*UrEG >ޛc4E+&7IxAy'е}[G4r`X ͩuJIUU5VC1b9i$QD53 Zf'8Ԓ!C ?jlJ24I;4ppB=~@s3A%A7MaF;A9k&}U&V!0AIZR 5Ma-|G`[RpE8rϧY&Ua6-낆I,ew!bfYw֊N@Ӣ,S(DsVG- *nU}!Xmm{V_q".k㓽^3ҍ ysK4vn!QZP.Y1PuoP=u7[;sOuk ,SDՒHAc|ůx̞ԕóB[]S_2#À!/] }q},A㲱wl½L~"dbt{oB -_}MUCsT:њfDwgCl ( -i+9e#1 endstream endobj 294 0 obj << /Length1 1525 /Length2 8140 /Length3 0 /Length 9152 /Filter /FlateDecode >> stream xڍP\[. !8A5݂w w;@.ޙ3_^>[|kWQi[20 +@RYΉNGq-F;9C`PH:A.O2)˓2 Pppp8^ p 8esH e6 vF9x:A]' nve5Ds@fxW!k ݝ dsadC\`g` Welt-k_rM; xAP'W t8@S^ e 88o߁ ? ss Z,!v` ms=@;ٓAqus6w889C~P I=;?)&k C",\P+X^o'dV`;;;/?{[tQr?UsX>X^ 70O#t`@I ? x=_FOAu_ =w߱T`OqCvvgq#(7oB2vv 5bg]] {Ye.=ZgB bnYk^2;sU{,sۧT#0rry?`?٠0'SyKڿE?/8rV?S\?S`@v :>:]ip-.0l83 鸪'ugMcd^ttByXt!s/4ùnkJx[zϝIv;I]~r42V->>:] d=˾o~qW6V;0d.,{م {b/wR!w-WgJs7 -1998-l# F{_>yی+H]E{:G_"ytH6F ՗nW'%e3cpju_a$b95*S*:ANY/既#HO|X;j3d pvad|O16A=h'{+e$t3/q;S\,lfka@38JFo=֑?[h0ܻ(q%rMK;sczc;\=s8Q(GoHK[Tս<]lr%BHYS`nWL1V 7a-Kd7 7dpbBew,!dݚ1I ̹V%$#{ـlFz&p(rC6i醃Ñj?j;<5ғϺ} h,[U_27@,Y@8qZ ڡ<* AV?(:a-~'Q^(+VN 0d.YE>c D)\%lFF;9Z,9$T K[XGG8gI}CǙ`4~FiOM/%\g6Kn2^&y_,nW VtL^ƝwvH#SeW~V&s^տ.hY5򵘊X yr%&Ǹh P@l\ߎ͗ 0Q1YG!J fm -ծ>+ʇ zyR|$ ݋7%#|/v'̕y,"rlňG[,'גPYkBͣmvY-Ʃ+iF~y& = sX şߕk~ 8p:bFabgSoEWkO׆Ec&|9M5eK;Wzpq~Dz>L/'Hwݒoes{ H1KDخ!#I,15ս2/5y*~Q.w? l3 f+Hic/N.kn^!.|eЍШj)X=%i)V)mcPvAhOF|˨P | d\&.}E33/Lyw8QX k(K,袘R,W8Ed֮s02 X?lx+ByElX!k a|"A 7>JV2bB!0vqv!5_Nhu;]JۢٗzY"Ukw ;(-d:2ˑ…!77W1%lRKWOWFQm{d ݽ:Y[B(dJX!uwn+[-GEb, tZפHg-;k5 P#^xeq9zY߲t^Exs_'Afl~rNmv-͌r1VR. :]J eff,cbTK]|Sh%l4wz=ޑHh;tfdqBn92Ln ;a~6+~mYkD2sjg*5{X܂ʩ-tb#c ZEr|kZ(tѴ(ۢ|I-!m%(jBQv1џ r,J6}jc֦1>aZN=kDOQԭj3h>Ĺap^9pL7~P+9iDW;.RMW卵bϤRn>T\ j"^\T:-p :iWZCVTkm`MmBoVx3g [%}Nݹֵ9egGP:[\6 ŸĚʰ{[ڄSYX^џgrkbف\F-̅Beļ M6.޳ Odk!Bkbz!#dh(^Ji%杇۩>p$kU[L W4De~ix9Ow/TZ(Z|p^ij2J'םᾉ2GpdsG$n_R0<ٶ{Z){ܲQTHNn1G+A+ԯKTۣNjV`v)V:إ\vG@ƺd\-.f%EAlNmy֮\ u m~)r Ct1z4OS7nˉ떳 Qp~vD ԙ7@ɄK b=o6'II ַX&QKn@sLsa&c&K}q/Z*u!۔q51Af^Etg+sQr7snZc^6czM+&YBʃ?m4J1@JAKZN&hN^ ژM$[ey|"cm3`}M` H6ߴ^{>lI!}ʷ{lcoDNՄ>~iw׃h  fPfw9yOsu)z6?wf$j^e̯S)΍*#mZ½IT<\%t&fJVhc뚊@iF !>ZÖfQ%Q%_0Zؒg/Wd% y[Yؕu#F(pqnV7ՕNG,Z9jHˉ_h#5rbឧe2}tĩvOYh梔4րKmVCn1>eynxkw/ݤgñnl%x67\ApYp>< WE؏xnܟ_)wf sTw4ɜGkp`ֽ?UmY:,UAz߆[uuik @l-"q6̝B@ȀgKRl^dni^*%?6Pfgdp-CflkƎQLlƭHT]QI41.ҾBɲ\ɬո 5i 6h~G>U=*e[lY URp7*Bt q5qH4-K h9 qL_t> to$ Hfwm5w2) '"Syf}݄&d—(Dk8uQ dhLXw}׊ZtDw^MpEm5^BX>N.Z2dq9e蛳WfDfx~UIH4 Jܸ2IsF~g$~/֪O!c=L-KR:eʾ\0({;+| AAezw̸_e ٿw<Ŷl L/T6=3 ̬܎ФjF )Lg;) dձchI|0l2_/u&9o] HcCjU/$hvhЕ5C.eخVb9Eh2%k6d7 Rwʅe{ f8UZ3?XLӞt8P4g%ձ^DdO ȿ%zɂTKXİF]7P}t9+`e:vhrç}M0Vv܍?} k3oeY -I\1a%! MȚSx#hnȖp;"Ro%">0yZ -1T;R8tq Rkv=8WX۩NM}8>͈GE b2gC<Րzni0\ h ڳ8vʳi[j61r$Q_"lvP,"JbG6hkɔN}vcmvwThԞ>g?$~F$FZN6 i+2*^@@FpzLH|b[%F3% K@ 1,w{+tuBء~=`ciF@}n,b.LHèĒJNlYadG.DIҳS_^P*='cAx0ϔF]NU '3szjs 29|GXCh]]里5g_Mԗ(Z 8Ggah"4sP(Ko,*FۻtB2Fl4gɬ?W1}>ЯY@'q=| 0bF:ֽ깃y+Dx/G*J`sb76u}j`o)^COHEJq }a3[ˏgP$^ӱ@z!9y S1bg$1&h2 }bq&V w_&DЊqטcu Rյ-Oa?pKyԼwҨ;l"nj8 lm<؋`٨)+-G830N)̱[+IZ2\ŘޗbsEoXBoɉ'/rwv:Gu }X |qPFIP9-ȃ1h481U$^-0zG|津sti eqVL= ^E{]W|0MU܀@oz"-=-j-vhi !QMװ'oHNȔ^(91ygzұhJ =jaA-*oC2MUq/ ,kD殬݈;tF3hb60[)앦bn BٔޟTx&MjU,W11'+).ѸD .rDtk(ѯؚɛќ3Og?/[=ȼ0EGQh)#3X 6N绡bwZܯm=}!|ٗ]FМpܤ1n RafaA;^.K+gRGYCf1DӛLzzgz6Y_FF!i6:|كRry+bJ(wsVI<弇2JגsMSùBs$S7wRXL(ITH-0V:d睮VRq__0W[g}/3yU4z"DcM634p=$HYPla]AH)MxD݉+/KX(L7bďEL'ݟq>E* Q'FZSS5xf!"hi=jvMe{v,1Qn0nO\AThJqڿyk-pq`ahd@6%n&Aٻi{hj"MN4ʃ4J%^L2kp6|+)V=aV4w;0=:D`pPUqťew8px`BڻDdLPT /ls3!! ?ĚՉHvDԽGmgOYBųGHBWoo-z~W?{Xd&e|hrDG%6^s+|)A'Ys]~ƫa]xX~ Bp3;Ƿx@CC&K6ûՃGaH+*|GTAZL*$ |IUmj2D\@]ae"\vKvΰ5;H|q6>C91 -h (E|uWX:ck$_H{,R#4u"w !l mΙ_Q_]vlym>,5`tB ʟW.K's*H}ʋz3nO] uYIXE) Yx *˸zLw~MAj)X4h:66%I.jM*W>*Y7Cb8f%F*5Ɲ1WK:$!N_^kT6?!j;?P^axqKQ@>Gw|%=9@y:)D}N;-{:)K^MI*|R6ю WVx, ЄtJڵBD8YlQdyeD6}\pLPYg~s N3Z-+Ht+?s{֚XsB\ըݠ𜳱mʝrITR+4[uqU)CGx;Rarؖg8sTv)fz5UmCj endstream endobj 296 0 obj << /Length1 1477 /Length2 7144 /Length3 0 /Length 8146 /Filter /FlateDecode >> stream xڍwPZ-MJM)! {]ZBI@ "M*]tiRHJG@Qwfޛ̐.gs>n *:"`jHZ,*IA Q!HeG.37 DH/7 b*46Lj@4XR@R DzKU pGP \Ho @(/,%%);C!.P8 <2.h%v.@# s ԃx~pM\?1 ; C >G77XS C  h`!U BD tÀj:BhpqG!_ 8h`{QB(/¿`Yᨌ!(T0(_Y;N(8x "^>0M"@ I)C]7 v1HO,~0(/ c :¡hS Ü؛A>lrD" }fF* RRB1"@Aq Jbb\M(Y)a߿nX[APCݏo//xcj_.^M4;gR hM ;3@ /vnGo ;4RE:.q ^1b1tV0PXDcSXz@'7}bPQ+, cā(/"Xmhoa?o_>q-8,ؿLM w#\F;Td\Z5OLy7Q >VLs0vl&jL6l:K2YmL_?bS ZХW7V-\/;'~]Ue3#'V ^Kh^}7~P<ƕ|-xfϟrx&gM+/ZZy|:8DdxmFgLI\OQ6%Ixҝ7֜l#ԣ+S_] ߯_[ާL(}zBR:~I"&TS=GӾlrlL" (tC=nd߂CiȺ .Hlk0 ل?Gm4#s-f'g_4&{2Bhp<:7;9um "xԵxWuO;iISSjCVw;#[v.Ǚ?TuhqNZ* )g}bjyI\Ll%ۻطeִ"w՘*9=ji73$l,JGő.rR!qSFvBQީϜ8$˧:g8|RfGy"*sE$3YՋi׊M<1+"G!|~^ÑZI U|PMxAdtؼ/+C<$:el~T`W-CNuֽUբL.8>ږ9M6'ۂ@:" ep^Vyp8%}2&ƚg N~2ݫY6ɢ_f#HߣP/z6Y 9$:= a u&`MRgY$nY2z/ǼL9[)dM-]h$YBѫ (bLҙ=]} L[URaVI45U)~>sIhYMwm$/МLw]mYuU֦D'ds{y715ײaRZ}m]z& 7xXQMgSotp}#/+X_YD*6`+32јL- #=1F5:?wQ)U~kڢ*N,S7jz8r{0K<ܦ;bB^d G4UGwpIcl;u}O.s4Aھ b8G֊YYfRhzG4h7s[W1SgR>ח.qdZ="Ҹ<݁J,N_W}7Ӛ)sV<{*NsZwήO=i6~uP÷jzTQQVLih㡠~2.Z B\уs\O {Saz{fٻ)BEjZk,Cr kǹ{Oَ.>6UrY9ԡ6|冀|Lm3|.3ud^z 8XCo-.-ea:t3ʜw3ڵ/IZn }ޏUC֕g:c<~@zYp3I*' M+ǗYd˚BE͑ q|J[2_RQiP:Ull{ UݰPLS >~$n`Gg.76*FP\|߫2 =f_H6b9Q5PBicoYUF$ǀkaoճ[l"mJ;ٍ+v}!?Tǡ4pyXb᳇춟xcWu:C:V"36Ŋ 4r ߨޯ2k%_!\E`js@B{;K܍}:y]02ovlJN,7,GtƄrt&9ݽ?t:(PD6UI[5Q_t/U ۷aD釯ɆDYt%|k;n8f=al2.FuߤcO6JX×}j#z.UXi孁ܝw$˳)* }U.N+s>iCFATtMUOސL`Gd80ܨ+|Tz%q7l1g;ܭ3{f`S-)5j%_nXzxeA~iy+/**skʟWrb q>e]pk:z|_K>Tu ^ke>4ھ5c3PTQAIȕhStzhY㫉2X7~*C`^m[sI+\Onw3l) ]!#~Xud-YsD$عXaZO*I#Ҥ\388Bc`B"#o1OXL>aw0 G2/ORl$kӖ/SZ)d$b7!79_ u5V+2A4:xqK)_,Xr=Ez~1w|Ϡb%B/&.jzT&Xa.YO?m{6hIH8`ugdh|V '`ҫm }Τ)(P Ṋ"UKo?}'.ȇR* |/xQ9j&9nk ׵o;F=#!ѳ,圐Bx/aʺw}("/0esRX%2|S<Ҩ 9cfI|Gs6mMQ!!W&<GxwE) Q}q]BlKꜛZS]Gci5rYf riοl>MI1uYՐ*|1]Q pv ܮFqfvl]ܗ-7v8o]Bi;()^gG]4Ezυw`2m9RP n^XZiVठU+&Ӟ7oq̓~jTLl4!Y~ b8 qDAb5ma'jBFPgǝDA@*^FgoTBB٘HdfCJG'hD׸59wĮxJY:;Id=pˤcߝFϓ,o*dU3 6 iM]L%EhgK ą\vxpZP{4GZLzH4V +a (E7ڵ P4'kښJZ(et^|qW$׶UIEv8tk8l|0Y^I>&"~ș(1xQK` cR&O(/[[BanU*[RXW 1׏7##U{8 Kj(p%,+:m339STY2a9yOUڻM#{C;oجOvSB@.Go2WwOHE[3?Rl}֙~ȉCUܢ]70^)oOs5Nz'_r,հ5SyʱV/MEƧܹy(X5nb $yT lAuXw//+on<-PWGk6A J=_0BDw/l111,e2 oj>%6f;Yf QU]1}`INqO E9t\%ZJWhH Y1ꄆM+0ۉ L~G^'d,@szt<2!GJ;\OR|t{:{B4)H&˔QϠ30UN{*y9A l#X#A\Թ(6s~ḱ:?OI5O)w/P4~bU+T1Z۱;!!x,y]/ ,C”_LTY(2"+OjxnG.O< pjwBU kHRę̆IȎ1.'$]h\&nr`pSMت{^{x.^ĝ@pptOy\PKsJ]O+f]ݤR2)\' ZoǶ?p8;t.Ǣ, !1׽ABؓL|JuDM>iZLs[@lϪS3~I_v%4 3#ЉqHٔO$4V=w@`f#TyԂKRe+dS/Jj. 7I_QKY^h\ -_x%ycYwK3Oi^,2B{igY4stby<^fRI[GWN{?Yɿz:A[Eʫ 5ۧsa2_q8.tw |<q}l p& o` W|TKUx sjIP5Af:K2|% K<Iӳ2 ="q2nБ: }QBVu:j刄;wn݊ռUth\ZDx?_%[J;fu1(xP|.}W׺fzuLJ􃒷"tRO+v^a endstream endobj 298 0 obj << /Length1 1477 /Length2 7141 /Length3 0 /Length 8138 /Filter /FlateDecode >> stream xڍtTk/]҂Ԁt"" L0ttw !-"tuߎggfe㕱[0$/O@  O@@U 3_z9-(3 8 "@Qq q+P@rpG?G'(&& !V0Ehe ǿBp lae:Z<0[ h)!ݑ< /C 3ojXX ~'nPX:g+ W#+P(t&<B:n0?g0W .Oa`'_(  'ʎWp}Goo#`*-\A$@5 قaDG ?2;XE< @)[pPR]*YY;WPX>m+ |U`6p؟dQ]>_ w,M8 ?7B}4c(w>.o-/!Q|uAGMMo 520[ߏvVH+?T?5`0 wZ(^PSeZ(>VPC+`Vp_%(,@ ,<P-FI/ j A (< A('?%?C EAwA Qa3"FP@?_>( [WAV.jU߻rYNí$^Wj>u]`]5hq M|8IHeUXz[C-nHcNӥyzQo25tN mN.ܺkKu6*E\F=4 (d}m5E͌";p;y|2A3r˨MhI0|s\_йƈlpKv+YތWI"b0eehlqxk!sPcRDD.6*Cq.mtъjADͻmjR[E$6>jgW kyU\Uv=GTfR˞oicf "/>ͯxFw(JL+mS_ -vce wU=̜vqE#frw19tW=xڝ9#$VhIm>QladՌjxny8CeԙԞ`kDde?h<߷ $:~ a|fHMDzYªy:}gh)s.*6IGs`@$KѵI3C}7=Do:))k[TrcŒj8R=^y'Nq 󂒕e+uV ~`%MH˝5xG!M95*ڃ"Wa@J$7VhYM)6ځ6CBU>aE[mv:yD)h|~Fz?6&j=/}24%IfNfJϲ{(]}ɷw( \^ާAcP)õo^ӄ7p)];f{ߣSqur'R;5o}l]'E_iˡmh ߓ\,Wj5<݁a536vvck<$ԌF*S`x}ӓ{OѶ:[-/ jY\qtK)f OI^.VI@gSk\:A#VOmVxUYtO]٣$q܁޳cxJ [S{(檧)AyIKĸyx-5==ɚٙ}Zo/xsǵE;r>:UPɦȸsI|&ΤV x&kɒ̡#%M8#$479 Rhm?qf4w"k2g`c`'A:Tav9υu߼:]N#dZEdV_\MK*+'+:wG]BWKk cA' qj<4)c[*BIÎ-z*'}-tSuV r.c xczckDظK?i`&:(_tڈ^~zO qޗ׺DHjr} bP~}:L*]5KGgMfE}|GBL͍kcDȠh<KJcaYRιrDˬP5J>,% 12}ȝ% h=>hG PO B6 (`zK?d$Wu #N vO󭰋|"=ȉl ^D¬ -(3:Ks't2ez.4G܄ w:2{SSϓ~|qh~꧗7p"༏>T0|؁NXG*u)Fx-k}b Ry{=SJO]EA~tp)$䧞f"y~%;t5 B_uͬ%~۝?-sXHa.*HjDM+U#(9!z ZF۽WXkL|wZ|j˗TN/0u/$[>Z4$q?V%r^} 'QGN gydXv#;{wQY38O_8cnH؇/xTe5] g ZFlTή9R5ҩ7W(uKGҪ7@]uoԣai?{;aSLrA1պ  ۟k4tn6f07 J *ɑ DUćqpnB!x&q/%wj6H-hmW}2MgÂʙ:.ӟ UOӨh#v )}U蒞$U_'eCX~ry{[##xBFi )j'7v;*ӴoiaAj X!=Lr8pT‘h^:ZNX»_Ek,MR p fܝ}0|F|т-^n:eXEiՀh1&I˅2*0uvzUPbv.:(>;(;Bxrʘ#'vp;皃.+INb%KEi7,8t6k{ӻh~BMoJG`UM}=ki`gsab^{cqZȧ}Bl-J_*GW\Y4n<*N>bfcB*';bct_#BY)AL݈{ޙmrMO?p̦b~N2)v 8MQ76ARdnɾ #< GoEomg:),L{AT:nD.<%ן^BGaDcX Cͻ [gZf6 ;y V@7=׀caTt'{8:̓k_X ߄GD7fPo4x1JPۿ{!i\ʕ5>SqDLL21zWNh,'8eeugE 0IdSCFeSN^trp@TZյޢ۝~[;Id]2?{ :?C|vfcO*f^\RD4[k2V =hbI8[|;8qsuW5ÁerAG*I tKH9:(Y2EC "-NȒݯW'IJ׾j{2[golZb1„#쐹ye!N zT27vy=bI>1\nJ/1SN ?xr2 }]‹< n$S I>kYʳ#M/N;5ʮWwN/vt0*M7v(l;ѤJEƿ߶ Fb_e HV6a}MLB:cC N6^ZZ I4\s9Yg7h;*Jg)t*z=B,m-sWAgQ& ԸUob*i gZ5)`i\׀͆Mfu~jmZfM{vHSW;/"rD bNR@170Zo6s&m.tAMU;7%BǍ?&'v ǞA JbSd߸ ݱ4Y]ӤoE2 ^-e譬W|nrHa3ddİy@frąqGxg/Bq"kmߘE|;G& g.P(U =Paͤ (?ap&D!CCQͨ4A  vI㲺=OZ= -ǯ{~QGhwsDaY.$`Yj.c*}Yҕ{qa BshgB¹p(np aMig,nktJ֨[DT$V:9l)4A`)> stream xڍxT6ҤJ5{ !A:H{E@M:Ͻ_VJ3g<v}#~'#DG %J:FB@ ٍ(o⅄"P8И MA0 $*)$& @/I #D!Hv%G $!!+#0B?BpIPH,7rB/W]ȟƮP_#3 @0(G]N/zw6@pBB W (tC.g( S@p_Dwqфߩ;T  ԇ{A=QH$F_aǬwRBxx@($^04r)h>h(!c.@(!**"<@6065z"<2 Pg !i@H(05^P?-?!_O6h9!0XDMMьO2**"~ Q @HHH &&g}&]*v&~ߴJ~m@6D^."@p$m2@CQB*3#c^lLAwRM9S|㯂D4miq| lN9 XQ6{ Bs5a$͕}b^M L|SEQ}爳ҵt&\_Km{stNޘ#tXyW"LyTDQZ;âBljR ɦ bcycm]| (Rbr*gKOdd2C'On)$6n1z6DPVվBSu?ںrh1v"6Z$Ďus/-E MM]I 9&83[]`;IFjZE:PƙX 8hwy:%iӢ{2. BhN'C"04D]"ɖp9s+LNkdߊQ[ KpU>zo=Gm+Z_8n#+H-1W9Ѿ޴nS^P $ z fhqZ~۞gӑYfVW }tOH1yje}!dI|n (3csG4ߠ<&];(6i8 ?R[aS3I[2eוs魒 ̑o ΜJg vfۀtb8#h?Ҋ'C}F~V{PyJ].,%o/vf/m^p$|<;!e&o@sDFLذc1&ԁ shqi*ˈX*y'(@A4WTQlkW85uYEn#3;50$_4!J| 0 Zl0XM[qʎZ{Rh_- Xk5>Pgs#7Gyx*縒ԋ鵫1mH1bx^Nl|-Ud3$NODբ)E<4 Hw=*<1}$y_˲Ә5&8RfEϮwC]I*vʜ8k>N YOXX&{SM,>|1vPE?&JO^a{0MJ 8E4y F}Bֵ:$/zQ_yGhUM4ЦEX:UBovVxZX3EWݡw5@#:rcŀi>Ƞ8[LIU=[,) 6]]hSh݅u7[a!uJx@ UK14LWBbxPɾp6 >S9hݻqgA=eN?ZL=?e>uQM=K=^xՑwK"ˏƱJ!oP;RsoW y%&f5#M.܊;pƮx&ȇ)kXb.[.NI5qBXXȽlϥ@jYX.,(p**qw{RSGPW=c\g~"yߡ`= rFyy)]I/|T{^\UR{*<=Uyj*4{8wJɦ%"x%tovIj3:c%RIq8I2RsO.)5h셙kb.r,Z*0.qÏ.&4wf,kk*C+oO$ҏWڄ{Y>S| ȺU4ry$~~Xtτ<^ >d ୏<. | |ejEȔgշd/0 VKP BW:b'9#D|CFVO=Pņn"C 7G6rǁHD@eG.3=M+l(T]z|dyxҔ#g4kD7;o3<*J}agKDK5Ee{wF3^TRLtfReh^;Pbyg(%(/%ߧˡ]9[LaqOiS%ܩgSoK_z9dUv ct„+hpS\|io OMMbLcl\s ^/;f;lso7L\*]П)k&b׍Yaйe\7_xv,1f+73fqܢaUԭ.G8cbjG .YEqGCc+F ,N}(Ywʦ4>rIJpY#߰E\26JmW:TE;;zJs[zҗJfY#gA/=aO >a;WR zj9{`[uJǬYZ|@)ٞB0M+U~Ϝ+T%/Sͳ樄Ж OX l/ϼ>e}~P4ĠQ!B`^xHi_:Jdzͺ$m(3קk&͈i& @ms-H -L͔~gaBf}eZT@PHIHUo-YًںW׌bM&gsUߤ%(\yl!eKNjHn{^zkA0{G5"bs @y0fܦsԌ&Jo!.փX iR U7dBW'o:_I3DY<qI.@.7-s3iź`WVm'ff9.X ?y#.!p3WrۑG+G8$R87oחknK2RpLnJVejd;TuasF,pr[FHcd s}ΚBqg:QJ5n:%ۜ&x?3|e# h̾O\\7T4oI$6NX_+?N_?J:R5H֫J}Qjb{1@Ns۫5`~ J7eu r Zn1naė;ܫ&=@1V7XM4jғ/eI_%B,DDZӵ :nNꘪRE~k|6u z zzBŴ(v. bht~2GP48dE9Zdq{!"N#{z5_x9wX^(Ep{еN$MꔫdbqM 2[+79%خD"}&T,ZVۏS_]6: pIyv 仫!gF6~޷;WᙖضV!Ju%؃fe3LSJ%gmT~9TVos=dRIj&'~żToluñ9`Ƴeй%Dǟ|f% R-(;W?u~m){)j\GYIus>siH^!կL$ Z |zV[>>~.pw*?"m "T>+dp`%"sL$vK[j&~Ч.5:Wtp%wܖywT彵g;5'h8Ev؜HvY l. o¤$~owc }]&S7:on^WO.s4gUoًt!|!# xC]~zvȽ#i8xFś G&rfYayIG^%öcs#" @ -]-jCvrk]K*=\M45?LF{ߌ%&o{6VZ8&5RTs$b!H@ GMIWLzjvčfƸE>Z0ze])ʜ]s"B|C sv{CqhBDJc굙iyTFM!kaIEOO Pq0p2aWZ&G5w<xĒbm )hT 2!uYy̏ ;%uE8z>DziR%C;7>9μxm `FҪ ʺQ*Zro@DʄӘ;RxR'hF0%ѴvO5*mi{aq~"X z'ot##C4Z/?YT>lcܣd+TEQ"BW\tziT:=x76H07Ϟ԰$ء^1Li~AyV@%ƒ`BnGiVicCU,z/O_{|` endstream endobj 302 0 obj << /Length1 1376 /Length2 6196 /Length3 0 /Length 7143 /Filter /FlateDecode >> stream xڍuT6-Ni=FZBzm 4 Ht4(!!Jw<9{vu羮8Y *P($VP$, Tջo!  89Xg_3 PHPE!XM PH3PD (")+"%+, A<0B1NU7aŕ# TvP:]pg}z#*+$ `@(u1p50g2hGa=!h8gpF@H . Z@}W8X@n" +;\\!Hohpꂰ^X  8cPx ~wU6BpE#\kD_ip\\H,?5]П:!QH߿;fk R ñ@ aIIq :Jo eMrᆀ#/bp "!X-'; s-q G/  [hi>WPL((#!JIۆbv(̟nq=8xuc-[ KCq_"Tc,/wCwݝy~7#;'=NVCW Ai#8HX X3 7@a-(au½?08^vqg]u$3Q I xqtW'H7B $ fڡЀ_k 9 q HuGq2\kCS(\㛰 e&Oo$_,$\/|tdhVnAcʱ`au# Ns72 A42x/?ξJ(,ffKa}]R8T}%k1o]BG^$1Fp\J.OԒ~r{^5Ŕ)n.pC"ob?Y%E_;91%v6q6]`u'c1e\ioZʝn16jdT:ύ6ɂ};&=Er $'8{K1rd3YT`mqGw}2UAs$ I=c7O G>_r58%X6wK8$v)Ko ~xbOK5o&DȂpL,e"m<`m鰃6xEA:k"WϺˣe)bp ͝XG;l>r*\d>PD+mbZ(C|2<M7LXC4.1k*σxj h(' IRC Ґ-&ZÏQ SDgn n<;^p |,-lQ=XN=Ĺ3EX|?S̭iA m/Ffm#`*eFRWMEЪCN1s:;l=YV2CgFO,_,A蛎Z(g)i}!O;]>f,җDIwzc6GzGH ݸ!`v/tTc*$W3eJR&Q/C~f5v Gc~Lۿ)i@U30" w>Dɡ_<93,0D_y~çԸcU>ϛZe2OМЍòy%O:]PXfP_|K^m}fcĘXv5mםψ ~#CꪣF!KnIئIeﱇ6LP4 /%3^WYmO\fSDD+a&Y۾/j/^Opq` };9̸XI `9#W^|I ecWJih/ȭ|F T[AXJ6E ^F;7^C*u{ة߲(PYco1q$xi}i>"0l6W7?"_!BCn@^Jp5^d9dPmLHs*]5:) HȻloҝmiYXo= 9%ϵ0L.]ƭ:a$<,=v49;M뛮G%Bͬ F.<*ϵpz(ܬS8}]GYNRUMP>=i]k>C7Nj*~ ;KxAc'oItq7}hx㦣U糔χᷖ![*)*E62^Ee/Re/F+[e5F{TL+Q@NuXSJ"3sGtZtu.rT=5B#֡igE{xV7 Hlie)#.<[oRϬYˬ&kQm*Y(8BQEtHZ}VQ u~N vZ%FT*){Ҁz(!b>l9"0Ʃ d(u>'n}X*ܳ+ۚ篌OF6"ob^&&kӚϛn|+T?m_]K,@8>8 Yri}, nxfڬTAuwZNR#VQka.4&* rȩh/g|j4[3 fU=DӥyUhRicu'{_" Tj\ӑV_ }*lPCu#&_GJGɍlrMpx}RrQlka$s-x^,+#bTs ["WGOI p%0(m|-9iqTϒ(IuG}ļPJMGѶ22pPqGj&#p'M5ـO:ķ%uS) sT|z9t^kc30\LvlBJrM̪Tjj3U]o _xp4,v &hZ 3Z!ByB}Li̐4ZEXece*qrTY; W"!ӆtq#[*9f{t9Ɇ/N\IH4bgP\CyļvTVO `ξ0|BIChoCWRs4JX_g(X \'RTF ;VY9eS1^2 .Ѡq2a7R'6(? @?'|V+ݿ^]DZQg%:)ny[PF.%vȑIRok!j3TOVN(38ׁR@9[E4 !iƐԽ#&t6̵$& J#RNh  ^PH~Sv8'#-ncWqt~KYQ󾇈i9R10_p㫵ܗ.0f6&d' Ҥ+BEtTXVf !JG|>ySJ[ -͸Ijo~V2B[Ȋ m7U?ZHXu=@!%o'&*skt50Ep>t}T!YDv>/~ αTL`w y'GKH=։}g瞄fp*4^Dʝj&'cO Nަ}fOE"cB[pB˯Q?NEww;ϙ'ewH)7T\f%n JK1*ϸt Y.}N~ ~mYQ| Lh>*}uP/clb7lo?YnŨ4X >N\kh^!CKUTW Vσ=֫OUJSF_0,V4{`Q Ǐ*72KUUXǠi А) #P+:jRZ{HTsSg?6t@[š$F1_$ʕ,6b?v)'.e1d&ү@>$GiVA욍gq!{>qTcyϒM/VN]?X'FA(!cԵHßgp0ӇiNƪm^:r׏ Р h$jrVgㅎ& yJFle-N [ڇzfKv~e_iuRݜ$0 Xve&^;RNƁ|@Zc9j=tEYQasЉ̚v~Ĩv!> CFo *?})GF=5By`˒*C[w1XARߛdeOqi0ݝRvFkMu3yQT\6윢BiK'C+iG]dWlT{mٱɄ^+rsñb]-i1Rb0'nq;ՄIN2lLRCe81ad3;% ?0NPۯdMs?ZhaKyo AITKkYzHW?J H( O+G )vA:e}5 6%AkiQ7.I#*xImMUtXmO+/>g&l6n߹$}Dd0vVq/RyʷMY}Sy[iUeVӎG)MCT-~z[,yMb= zcr[;u+Ă6Y1{^[:Yk29oQ<2mb5>Uo,_ ڳ!",Ex_ ۰گ;rd)K=`"ŹZo "Hڟ`]޴wִ̘ztI_~̑Tyzӎ`7r]gvU56UU͓`,u0l (9 \ZRqct sv1t]3&ݠ@d'awG4u)܋H·3}xaQ>T^^Ě;hcʘMgK.]}~_ #}m P^b=sWvvH|wK .u<ߒ]BL .?)!s+`3ϺBcqgy h@Yexو"#N qgF< XkͻD–q eZytֲ1 gTZgH %?fz7PLߜ{SPp eP&`X&iP^6+s ]e-%sPFk+cRV{Z־ﵺY*ɱOB2 FȻ'f:泆yjzMfFUuK;‡'[vsr ،a7oǾD[tAI=~_b[VC+݃UӢ JH F}9OW֔qUˈ!L rv~szzI> stream xڍtT6ҝC7C7Ht(  1̐R " -twI"t}߷Y7g}>y+> "x% Zj~~A>~~+ǏjC`PP[#|ֈ PtD$D% ~~0 PApV?K-'@@\\O:@`ׇm]0[%87 ۛpxC=0m {4>V#W@f.[0 {vi5>'f @0&Za^k֭ nqC3y8f% Eq~>/u¼!P;cy wOߘο}`@_ $>~fxs?؃pí'8# "6``?O`P\1PT]C^|x+(  @BчEѱUpN78'?ki pfoIw'#eO?qG7⁹ho1/j UCX?Ah^!>~2lA:Śu`p!b"u~xE?h?U~ $,yK\Av`?d0C a=Ŋ *]Yb,1/ww@lѐǃ:0?OřJ9U5_Tx~ ==>2T Q<̏osJ *w6!wXO mF'څkzk=97{MrR~Vbk(?踏^e46RLL%E>qT HnW2ezHxwNM$sG͏Nj q$fuGIs ,dfCj*jOj*| $WbTB?Aaβ2iel9l剟a|DUIԬB :C1q-tAzHb1f],CU]ч!F"c{*\k`3;8 TGew86Ty=KU5oqeV]diM9[jɸiUNbhb?y۸A;i~V#'>Kցq}8aհ}b4nYX{-O܇/kD:Wd۪KkNYRjN 5L,O^e6MEZB:* (RY\<4j@\_݌Nq#i۷*-bL{> ꠦG*x%?7_N1i0/\eʹkZ,B%V $%2=mEe v7-v43.f5"<S+oK` }l0E5[ iMǏt R-y ӃھR`q Ѻ>LRV?F[-D*~wœL|)BԮ"iCxߟ/E|/jNY8OҝKz56/)_]PYuW.9' ;`0J;!RfQ+1cPRaO5/_հ +ei; SwF(&z-vq"TE;4$u)HTܑ9'&`l,:L5YOV0o1@6ubt 3|ƧF9 B8rٍ̮dhͼ$ߴX怫Uhʐۂ-kL~qFBdECLu__\@%L Y'ÉjoDD%d =AHDqZ{?DTZٝ363ħ \%,]E2 oI5J:mq_5²cJ8},H&v,]$wTq8 H&X.I)d\ylu (R[01p\/&ȖV,^ː"ar!zaZL ڋw¼"7_u>^6#C Mm\U dQ@W۰䟌$WxϚh`yxHG֛tAi-@ys_M&.GWʣs.Seno*A%t''~dh6igQ_Mj g&Ypո~" ݁Stt[¬2đ 4C1 ^5l_i j Ks1ܼz[uJ':'ƎHs#-~ʄdjsm~5?k3)(UzH R ]0.7iԭMMլ#c-wr=ZB!4b U;YU;u˒?-"qj,tWv 9:$($2t.6ͨ}E8v4b)4p(ܝE;ݷ Zs2;w(ǜȎ"6YgZM@/ҿ@ e_-6rL&R>DXGUXv}άk MMYn̉c\tIPT" *8N wKkOUL=BXfr57{.H[Ē%v ؙli#Ŝi.\~Ut%.Jst̥נUS8.f 7sk2y'!d3!Ţ0ѵRHB~<ԋnR[# _zuo9XA^h2_hL8#sN,B4 7/ת[Hm%o6РM!InoBB^Be5vGH#,u#tF txG\xMKWBgyl}YKΩAz~R3U.ԻKKFX򒝉$Vc`wLQZŶ]9A=C n 2{{"ݐ0+t4&RMY0:;v 1I^uuqa(Xu~j  LrE=W~p>lڡQ0}{wSb6S#5 }%Gs ߛXt+XS r*k">B]vN;8޼Tbx}&MtE3J:b dG`N lz sbRhֲMU,3gqa-̈^EHoE>)2~8hIOŖ8S5Ȗ8> f ~Zjh3{$B˒65?=;JS_pWΨ^FY1dHCU*v.JunqVzf򢙡M[%`rΚQVQa?O޻sU׎]CEgk'reT>PഖU;H%V1,d Kb<5nq/ n qOELO^<:p))u ZhCm.XĊ Y?{/[kƠ>2vjض[`_Ř&0y7ۜ>~1UZVyc1cNG"ps)O2tඈk>^H4z:Hug═-QRZgz dhoGelō!T۹pR>%ldŘ!>V=kEU꿵ٻNvjܥFnB8):fԲ]oeI郧jof=% };8lŽ { @R c[HzdpU2b_̒&ap?zdRZi(9*Y4f1\t\+^a/Ay|:ifGEȃ=>(C5LAW8Xqo9@hdIupu|ޅ c_s{U[ Im|_U$t23'=cd(q,.y2x+G\tɪTQ䔂ͬ{]}7C)cvX&WęfvYINA>K[vR+YƓ W=Re5a7ٓ@aq9ETnNd4g19J|K(QBڏK=7|~nR ^v'MTL\,enRG{?f?I:q {z,UH2a8 ]}MEٱNx t1̵}*iOCx&Ȳm[S@(g@;2GwRBVU:b J희/ͮMϸ!fB1yAH9xq{3yMo*؞ʻwU^{9Xsdt9iuQAH,R&}͎ O+Ʃ3k0&"$EΩ$Pw4e8Ek,h}^MȆT}Hz.zDn+#mjR5J"O޹$Iist [z;6z+GSl'SlmӨ>Ϥij#yձA^(zQ@kJW9~j㹤;E@ē9)7ζv BSٸW!lҍq4$Ӫ0K&dP}j/jȟOIVEo>j_99-H g%d ̝8({^'6`pbrvrۄ=uS}b. ʓ/fN_CxhK;T}@CJW.Z̬ٛBh\wGǜJ K_G`Ԉ'6Qإfo1 'Ӟv:D{eCK|ֿ^s(0έ +tbg`(P;j0ӹa|$pV.!ך'$|t-ե @G K !UxZww}bfT8 ચNUIPCMk=5*HdU'w[mΠ@au/Q*ɋh&ۓ{E*ƹ*T+~Q #@$R', xQjGdFX 5|0YH M?z*iT4)Y̺.K5ͻu~?[CIt_&FM=դY{Z$Qf~mxe-W8G5ƷjB[{$ 8?ɌkkvYL.pER7hcM㤇JSlKNuJnpLt2s/[~>Mvs$6jebȊv))gp.B.:j'g·t'qew**6ET1,u~y/*g˩Q+SKRC {,*K#sb4 & E_ 6Ћ_sLf gތ/%E,sV8c^ff r_/8~un\NSׇ&Y{b0w!lV3 ^nªb|ނ9zd T Qs Etfax6!祾IR{gqr∉>=ŘGYY|efh]vIb ^uC#I5T#I=%Jo ފ zlK\3P)wP0DQiN\#-"u[;fm*EJ _hw Vawja:穜v$kX$q\;[MPrzEp^("fKOZ)so{6Ha }|oj,C>2NIeDa:="g=Z=Yؠ"Haf׊-so?9Aj:P [UlQE[܋g3ջDx-Cl|D9iv?-$j55L^PA8h0J]*sw?ogUE]2!rH:gQxDo=#]K&}44ܟwPr nU6zq!m`A,q d`2ـ-@p0g(Z!zC`UFS&Ҳpt*p͠\iE0E*D'T{`P)ST?&߉FIvuUU"=ю~8IQ ('͊dgIVT2h4ʝ뭈O2g>8.^vU wk&w<;tI"@b"Ɗl=MX7l<:PBuNVNGCS,$pʻ]Ğly)G8ɴSwڮt]'UT#i;A>Ts{Ae^WԘz7]\`9̑8b'֔X:->^g+WKAE(/ġ˱E\Ca2AϬ Ɯzg p%&eCMzkorч3,}ѯ_vHu;ؘX;܏ȃkw.) ȡ])}7OZN6%G|҉N(!%5.=/IJJ. ˼߼i7>3ƚ+CUn0|Sp"F4 #Lb~xw~/B0XlP)II԰.ok񸴺s-y{? G]t _9wkK^K?–ODloPF|^M\r38p#T~2ߨ*mJ|~S6JJ9ۅ6\w:5덁u*yXw endstream endobj 306 0 obj << /Length1 2012 /Length2 15253 /Length3 0 /Length 16478 /Filter /FlateDecode >> stream xڍt[ 6:ĶmM8m۶gbNlޜ`ߵ]VwjoW^MN,D'hlkh"f tcg(K01Yaɕ-Mc%W5qpr#B&b(c H:[XLL܌fFF:pD \,2I[#,G|PQ8h^1q02d Ml>2Xl,Lg *^s'';nWWWzGz[/ԴW 's1௒6&.ln/ `madtX 46q|d(IHL W-D,/6023[&91iz'7'Z@kGۏ.K7 * >*w}FvN6Yh,lkcctrKswgZm]!S _e;1-M$Dac33q122r2sL&nF %Pv3oO;[;G&&.&'go:`FNC3 ?&wph1~|t-OG *#!,(%)$dca11YlG:%9G˿{B ߽dm?:@ѵ>ޘ?Ku_6U$lm_?~ kG|t~kteL-mWcfMJ/_vh"oh 󱊑>qhͿ]&3yEF 3;񣣘LSilw3聶NK5zLm`:Xv6_;Aq'Aq}  >I|#'>8?!fȧX ##g>>|F_g`!E~H4~h4~gJN?"?n"R{gۏD}q~q~??9?Թ?R1|q~>~cbbfbhkdYP-J7̊0xq+r x͒,= JJWZ$7J};4*Ӊ3TW> YqF`ro!Hl2:,C0RFpnSJ`w"c3j)-V1M^v\)in(sUM{?ob,tq۱ϮD\WQ) Zkۓ9Ulv7L2f eRPiM'ێG4_WdK;d[66c{.ߝ7Λl>sv5bމVb  2a5C-'w];r3z Rٚ6gI(>_nU0tD,y#DZYrң*ϧzrЪs6ąydǂ(/`"<;*03 $P$āFx 2T2rM^d0>%nQCPnTCmJA^80 }|Y0\$zg-.י:6h{a tKhdzpV>y&N 퍥ffntcx/Rӕ}n]UWIR+jd[n2*b|Pq:yi]G}.:fwE4΍[VIt|aNkQP|&&&)(ݜnl5փgzR~\xF;Aш`{1K2Z&/M*P\b&dQO  ḓw.DN8+seDEXAu_XsX 'v%t*y5"{R7%lk=QV&;|G/xi<.VaH9#sgaZ clFa M~2$@5`ż_FyFc z55QEz\k k_7"Q EksL}|ߏGj@ @ 'sF7a g=jy(/֐)Zyo;rQ ayv'%]Cb!HXWGoG:Lj.Br] +'_L :zjP {ͧ^tu<g>M; J)_匿VqqtHeaTO4܁i}~OƅrQ6T|EYpňNJh B>x[K&QꩡAg/|1!k}!a\fo̪JIPs2`%֭&F9%dLbļ.~6c`SN2g,+z:+G>*wzZmqc \^{`׻6 |Gs&N?# eAMQ~d"j NZᚪZWLj5j!Z6u)]bx"A˂ w6ɇg<6?K/s]i|Ī{,ny;P8hhujW>&$:QGbsC`t 'D@pbq4[.)w$k;Vb5eF yk9%' 롖uA{g4<38ZIWL-%3[ WxP vuV=;v~?x= ! ZU5>ɢ\-VNQK7 @p[d PUkhѕ_6+WkԀ?/!"B M $M|;'T9}#zDtk^,N ʂSa;eq^hڳB-X)1';o_0 4==٬ wf7J}C^70R?qmyd_oIkUJžT8 =ixq07rI*Q EA4S m.ˎ}:GAzr 0yN~j1as\6>7:WQ{% jHwrXη]AUŲ-5VH`BuCQܤɱb bS 7Z@$P7Ғ(B(DeeoY^\yz\j/ɂq;x-Pg7CȀG\/K9Qb*!6uU”8:/#sijpC pWcXV6"vr&'(զqX4t#.`_vɢz`b:`UҺwWHn7#ڲ>RHB&k1K2@g` Ħץ#VKDqg'}ct]7D`COD[!4>e:皱 \u ˫s rkE|W:G'NoJP*)<5=flTP=~ۉn9ƴ∼y7^Y-\tsӧ;"^(7&$j>J? Vqu4~YU=Q@hqM?'"~PO%+R.L OwixLMyPf՟2X(Lk\:";N+X3dZ J5yNw{p,αuY^"4^eIF5LW(]CRuTulM|T3k~Pb=a0W/-pI}EEIgԹ:sCVNuƛTzR ҄axU=L!]y҄!vZ)qQ }&Ǔί_g_) /2(H_*:DygjџvaSJ^܄ 5-^í:~Xh"i|݅9<m}CYLݑ̤QM=4)?bI(2%}rP OLA>F-4#_ݎVxsSCh|NGMdW$[ᓇ)&Ģb]vY+dmaqnvzO衲ۚ p% Sz-_rqJqDբwAHN8ǘCJqE_aϼ)D Sʨ7 bʲVO@}RJQץ O@QpyHJYx(f=5EUrb1-<$(|!(5H.yW ~yiU ѣUNޙ "xڎY!)m4^~694 ]Wpab"O[<ѨXK/+3|5xSvzi)(<0/2*됎(8;o`S(!@܃}A M7J茳X8v@OC:wzS@"W8FjeMhRas/9-B¡VQ$zX|ibgL-kRbIYI U 0+ 1 8,pO2fN,lgy!(IsmeZҘb+Ћa>| ! 7jF5iYZ Rƚe DWx]p a[ TEU[nߌ%]@}cpǛi[Q5W~enfXxZ-~u=gj+a-\c!^X%-* z^=NOlۙkD@;i'Yw U|Olo6Mu(5^7m\VO!^tsɊ\zB,Sp䦎o<ۃ.҇m{`E=>]}#/]t+қڎ\CJ1M=\"A-kS=o+UxyZVf`nVD^o<ˋbx;'͂;f+V5r:!>EnQPE1fk}0Tg ?!k +[+?>WnpL/?|:~1@wtrFdgP_Wa0 Lᅥj%f~fH0Ђ*黒3M~iŔ:kvKɐ J"ڛqUsVƼu۳a]V. ܷo',oz h@[AxB8İofQ%ڇ`|Ez~#M#o$)NOD" 5Co:&,ˊ:.δLmaU~S$r[xZ' vB jPbTl>(*XV+50m+T.{i3NJby.3C[iJ2w"E9hX'x.zɼ_ѫYq"!׬/+?'n4~3Q:3XBoƠR#)'RX^5!DD:ypdFr9LN՚YbGme?i<}Q"g4x'nodW-Ȏ2thjcAT@-UMDDRNJ-v%F̜;1T6~ m[j>RS"3_S:.M.ҦafȟM<kņef g*h 'Y?obe@ ;7dI%~%ɝDݛi1 TqaKphHaO}34 WNT,jcCtT![mԷuD 2 NicNe".y}QHR$MSqdKd#],G7TJdݪcv˔AuX{hܵgwDj1VB.s?p+S!w7ËT}&Y0zd9-XFˤrkZ0y7nMC0KԊPxsnq"Zf޼kYjc8eg:/jrGCÈ<0[09e$1/<|$-t[4^R"sU_NPNuwj%{|#C1tFcE'xOv ɡAWmU9b$Y E_nQj|&KȨs^eTi6 u5d)EݳWEHۜ*]:kF.Eu~} d <ܡ}J⺚TE6+a[Uȣa &7Ae P קg=j?z82?&]3 ;Xޖj+Hqh8Cxߴ Ó檦;'۾x8:;n!Iׄ`mpff?߆ܸ <\%XP8#oYt`u)^ad]Ȁmha'}0[4-Ie\Yh$ 3^Hc87S#u }32@υ728x9-}9ؔp6yoH&]JKM.`ݥfqIkb?c ߫)0 RP;yXNQke*LJq+@A):hćVkf[mT]aUOjc }$si.r1Scth 8|paR'#R~V)1;嶽ј"dϏ >zLʻ`cȮ11HxL@){F6JvThgP8stXߖ޼$1~C@؝Tm$Gũf||;. 1úB1r7j4ajWr3o_~6o2\ʾ/ KfB0<+HsҢξxY{399&wpGJA)poVhv|\vcr,mX!(#"rq1Jv%Ͽ S'P1u?FLzZo0ϓtoD,<eM Dx̾}>cWIWp4@gzp $=d[ﮒ|̂yH]j3~PK%nzHwW}TI/3xCA$';J*̳^)D%( qo5AABf ~PCCHQ] )!L+zf.}Q+j˗a)x͒C\&nϽdGkF?:vo) )1n`oh{L-BOJKԫ֡"ӊ 8?Ƒ쌤a7ԭQ:o@Z$~22lH>_~}wumj-_#a)JdJҏ"L=}&3Jä́k xfkkvl$Y|ֈ;]WSΜa/kڄ5_@,a^F lc7x2K@t%]K7f{F>4؍cμ̭ܴWIJ@'3^,=P&vS^Ҵ^t֩+bКFځ̄,+D59;nVs^&a 8AۂZΟ.QN}qI˩ڀEcEmٺҩ m,5;s z[Yxؒ|ݓ)l%"Fn0MjHoC+<.>%@$yz2,6.ýx (3nԌ Kr9'`"Z%Yebu8hs8;F4w=f=qɮ+OjH*N3Uk+.y=*7&~9*Y},ឡn#<[0T>h/<_ uhP_5Zl;/, f{q@6Af6\Ň=4uf>>m2D[5sp=FE͞ 1a_ $g9rIoP&C^{˞W@OhI1ZQ6n>Zn"BYC' |5)g d8&;Wf$ӫ אm쯙y:ݥFCQd8j ,TKJ#\s>|Hvu Na#Av RḂҾ6򖻨(j_ w0Gː5QՃv= kbfz\EQ?W}!yJ6q}#Kw[ɬKPKFKWjE8݄qE5fXNeRZChRK*:KQIVn<~oaݨtm!^A[dV^:ĢpQE&,|嫰xإ[3}Q]n]z,|kߺF  kUZh\S 4J.=bT2K˘_ ? tywd6jjx@D%7QzaxV4D4G *5hu<~G9ؖ,5ɓj\YV`< F/rENmٹ2;K8] ī`>;:e6lB2WgbL^zH5\="<SvGپSΡ%N#%DznXB`^RZIckp$&D2ȭAeZzq\&$ަh߳5߷ޥ0uV$,NJٶkފX-6, cor.*aɹ,~f_#R?_c~ (ͪ9yMg!ɯ=8f/.U <81YR9UFa2s lMLo^RK@o4{ ύ'%f#YBB LMxnYgp|MUMȳ*34 7},nwHdLʼہ_:\/Fes[*0P =.~јG(HXPĕls99(@::@w8@noS0+ s/ 0)y u)![&֎p\qP+f'ں$3Bgkzz,vjZطt 2 I lE=_Zi\V})>v %<77<)Ug;V9ڥ%[_!oN@.iMn}/B SؑqX~4<hDb5#U{͐AQd d)8;?>d_fY E\aH@\r^S۴A,풯 .}-v}ޑؒ6>MZ:#)F1xH^*2дν99RdwA72A:) xd'댙mQoPbIeX_c0jQwF[>Fo5q2Y=G)f~FwfWD+' P]LYe;kYebN3(_g*eWgZyc]bJŒnwxAL>Ґd#;P-4z\A.oާ9+M^ű՝Ie)Nx38P}F RꤼsM%sq@bo{ЗoLi֭d8]]-%[z[뎓X2ר u[Wܿ)q9ؓ@KJ$͇1iϵpE3KdP<ɏϺ{v*YUFl^Á-OS#{΢Etԏ 4Nl0k xJ0g4 mqr,7#(![.\MH+e<ݙ߇Ql; ǨG|*7ud,d,=NELOJ@a2EGV]̩;k1h(M(E&?Sԯ}#yz8ؚۤRkzl\yg̢u`am:B#G, V#_soQԤ٠M$)_$OniM#6E1Yfͣߞ!E9,q;Itg]![EJ\rHvy1 jJWq:ӊ'wiC-b9QHغ|_{3J|DTFQ:M|ԒbT83,8B%6|at\tT+0vg3keb+{.]atHڻDeTMa`2DO}ӘT.EZp(VPߛ|%e(8&8NxGǶ4[F7U^6{پҾAB蚑ފvffaCg.J8xU&,ݬʥ:SWFK.Xܢ 4NmT(;Dތ!9?-ofNdݲ*)* Lꓣ=]Ue bUB`n K(} "|k%ϸ yQ`9`KKv~?F*̀O5#I+:mEL"nw"quw7C! B!4gL1apmsqW>-Q(Ìzm}dr lDsdKQ~B6${(S.PXiD4h?vhf^qڻ.J4LiTfє7AHix՟`uGeݝ<Ϗ6 ȩ~r3S)X$|))3h ?|iliF&/.l(bmH*;G;ro3 a\L'^߄qj^\b+@Э :LA]`fⓒ4e[ \Y( -lp(DAk;~wx,0=73NnV6/Th$A 1+*^'HjKPFuRH;Bk9n%q щJ s&c&)A/tWx^#ĉ^Dfk= @dMlf`Euڏk ^UgRo^@1׮͢Œ#p^Z^[:Eq^ ݫ(;}-8Gx*<ȸ8@ZhMWu<{` S?#X4oxCĐ,$VYA'Q0v ~+wtJ5rꢅf\D>%XmkiZɹ 'H=q]@T;0~~ֳPPXW/R_G@H/4j6l[d4݉Z<&j._p3[/{5i +PSaθC*6yԖXN|MsD.[Gf&o 6%Q0 Ts1)=;*A"8%X`܋Zd B_U~u&1~RPE k" F_mh x[^e5mrjo%@5BasW u_XHv+7Ł[/ ݺ~! 0cӿZX\M~!g (>ll<X6o\$? endstream endobj 308 0 obj << /Length1 2674 /Length2 19320 /Length3 0 /Length 20860 /Filter /FlateDecode >> stream xڌP #; { ݃w`Ad}vޢj^}DYA(` SPWga03123"PP\m#Ph]@se&`C{- `ef3/@dP`:](AV< 6p:L &V@;pF3[?!\]y<<<M\-i W+*4.hb4F ? 5 Wg ,].n@g8;@MF 7Olbf`hbXl%IyFWOWzoC[ lu \9]]]@kdf {s1;; o~ g^LwnL '76`% :fVL{9RkqtpX,>.&@ߊE,,s+h G,Z c ? f`o#fRQQv+ (&YS.@z7xn?cl`<Q_2tKOo ܺw@ T UV "4" +\ͬ5~-}X\f6<ߌf濗`l $V x́ 1WppF}&ߢ N`LI `XLR+Ib0Ar8ή+AJ npv?]gW vhA`.Z8?3y975_); x@.6 (p#gG]Gfa3s?9Kp=_LHK4w5qOPr; 6?.` vpw Sr, YY o/nvm=mv dwzNmfgƳ%48ÿXXE;Qs8s,KX3x|r%9kMtuWaw&?a]urspMo 诋OHp r@;um:>p]lM\\:3_3nÿ1l8_-.fA0a?_I7?)5ss_Dm_+ 4CXw0 i!`؝JaYrp{@K ^wIE[ٖ^&~9n oMTi{}2WmCX,<A]xW3K"ɍE3Gca*{՜rOeb4RfϽ#uexuv#wX6$Gwӯ9 uVnݾ|GL՝VS_TEެ BePjF5}x_=/]E<>')/V%%𨤌i5.Zz]Wpgݯ}#VN^1#NTx3?R Tl)>;~w-%#g7]uHrAt}uzdi {(@"Hrr(RhG 4RO1 ]fS{ Tdfql#h,<8 laANG3AL9EkLIŵ$JLoZ,<9[AP CvrUoNac4->kw>;Sq^n3[p~ iCM?$AZw\CvICy`3!$]ݡIޏ]IyƧGCG,w~Vj-gYpϩs)ps+%V-7˂xB[mHBTC14݁HTF⭥=o9e;Aw( C&w4 pik+j oVDJ`/Z(+Yz\A*MSty]FeBB}Y_G. B$BqP5J0}!- ܤ(BkCؤ虲K`*/H: : z~5PR R Ƴl)A/2̈UbODwBew\wZ|D'^"(|W17Ἰ<DԐz 괧$Y1^:T ЈoOG0˕E,I-IR_P&vI #ApAf5ApHp֤ NIſb &dBһO䫻8^ \o-VgfLIJFh 8_[.Fpe *dKAr 4}Pfvbhɜs}Vi$MX3ֈjOb X<! M]4W2o8B-wɖ!j]@=\8D`d݊&A ښ!mH߉R[a~xg J}Bd#XX#AA,jGj0vuBi {+o-Du% ޫbLITx^dZ[H'eE:/"dJ(tt* 4<_1L73ptj khZpNT_{\$+M9mp"jnpwFn H|EPj'S R۬-c0xbosp~ּ2rśkwwDK,]3T1ۺ,Cet6$wf}3ԤS-Cj~O'I}؟z~M^Q%Ɉ3bA@Ⰵ'")W>8LQ%j*9OҦ+UuWj~]=nʥTaN35>-g\쥎h&wI7K39< B&i1 \jD~K[xjI Kcs@#89iǛ#֨1MU=˝34?*v䩤]_]FJ2-\xL>qo̝y"[w?`pɮO_އ,tիenSx6͘(XD`ίv_⟁n /]4=NaT& rCl'._-]_!GS{\J FBLW*qsx]p)܉hI`Ҽyea8bVQ#bW[*Sqc? UVK̆Q eoeP4Tr"͟0 T.BB8a8grd ް gCT7b(d<ζ\ԇYcs[8 ؔ2Lm5ӧ3BWbgX*AXd~:2prz#;gDULjm@wZꐻqJ c>DC˻yyHuB0Ix/IkēLawM0~w$Ws`}:jb[5 (^]hsɘ 'QY-g%[[S rq?~Mwj>Mm]U' ίȽ `YO d[QuK7˰~8&iPؙm/FtI,4"(>%fjKiUFKSTwimOqcM1K<:܌@#d^VdeM`=@gK*x7۶1.iF>g4` NrVI)=$3o ˹/ +YF5rK)޾՞ʇr[kx=::⣉IXBM\D~- ڣ;lOYT[t@_\{΄yؑD@2yZh+:P67ٞloc ؐ4(rx;cF>HܢH旜CW,/z]JB.sU=[%*9 Hsv}d8b#~]Fxio|EbAg:qě?$>vsᙊk*q?2QAO^v@wǗBb_r[F@H'){Igј%LxڔC$d 9̸pC40 &4ά_J0Ajs< _;(l?HOҹo'e#T8Q!ӈ>,*=7~%wR"B<]& 3|F"o)ݲ۠cljzTBz[r7_`w`63tMrhB{]H#@c毇s&ݭj!w ~\,1d6ˈU D݁sVzoMͱn.xWGLD!ߋ KQgM|Ka=!%\4\Uv)`>lRnzJwDž jԼo`=?@уi}[*/'="`WVii+"Cm$5 xl Yvl *rgL׌=.u;(y1YmyDʯMAa ?I#_-hϴ[5 #\7viHe$ ToZ<g;o>ǭ*|Hoš&^v_";--h`RQyУd[S pE a_\E)%D66hWXS% B*g|YOKCJ.R"9IKӒG0_yn~/S6ǢBmU|%.]* 5FbObhY4nxw9 +|KK.5XnBq9?\g(f^0tS":W[Lz2Sg|%tA8kℷ`B瞦=~xLatփ`DJp(>5 4vSu#OOd([x-B\ZzʜoƮ)؎36c0zuK՗AD>64VSg͈wc^2\&1i5qmO#]7>)3*6z;P-2~û.Γ@\ʍPJ2VNwn6C* "UtzŌTs w3S򳡜A9em#Abz=˂(FyGPF*^7m~3! w^nU]өD}NI栚S)"(!oS4;aiJ^D*{ӷU&4m &g*CsO#KME V_w#($XIV ^ј݉/dN I lQ8JEEb-oXږ(m-Ϊ&:%I*'յWf6MX# 58jsi񠑽^qqp2i.6O;GGRމa=9i<[^vyJ Dd*¬+EcvD/KǰB71P œjO!QoV r도ڄ4kZT4ߪhݒ6HoܢలɱW|c ڔ|Gʅ;{vC O1 K2)n$ONvyRnÚ/ҖƋhWT*>T׋=I{يyW 07N,[0DZS}W̃UtlUBӨDTTMVuhHlͤ!6ͯ$^tQfCb*`xxc5\2"zˆ5`/}NWT(;:'6Q{$rGsV*2Y'ϠAO]ԠfT* )3eYlҙfm_9GBD!(+(ka^/`>Øuԉ9l0HS]`yH_HAuѐVui>lo"*>i˄@*k gl6 |״ 1t}Z8_՟Hy}.7/+hwZTSSt5"{sqJ46-ເt "PWK6Nz2q,6V^_G@圄K:'HplhhY_C.C Dt3¢GšU)ס m)td1N'>dFeڞt BNLep ѼBCTF9`5G4f^m|APQ;uЈYh,qˆ:ףԫg=TOd-S \?/R̡?(|f$וc'U^Uy{)W ԕ~Z<JݫF 8}bc;t8>dy,^U^Eُ-,qXDF.5~;Zb.lPd΃Rse8Wν ?&YPFȥHGv4񱧟KǤBq]3$&Ջ͒kA9RtӼ>PT/amCmqm©? h!D`˞PEac=gRsmlP3z;Q8ݢ9I:8c Ytg"vw3I[tcYECo.zb%qKef(7ʠ?N>!wti0\C?x.#֎2d5M -x|~ǒL'@Lq֢\p\dhp!~MU3גt͡x~g1I@LEZ3r@rR"~RNނޯO.{ȹ)LML8F g Գ,ب05Y$?\80Zy2YM]ŧX7ގ1i8,c0@rJmh'B.4CR<RFkkRdTuc}kzcOga*AHdD/з|(%87/)5kmN"mM,ᄮԗT~֧YyiS NZx{Q`1Pɧ>)fMJmc}Rv/J;r\}UV7[si>m4ogJ|>AeUD6TUzny,':Z7A)XS}q9ԶҞu PXԩq_һ GI ] !L,jCH7d+m|L kB+׫2;PXZ*uMEs! cuǫ75qYhXr.HV2Lȃ}Aȴs[|7bµY6=mL< Uw w# Y0qӯ*˛|\oڝ&d?㌉g' |oeyT]NV&m`sem_d@/ ˅O{%ЁcpTYDb2KF4ϟ)D7k4A֏&)߳}<0:7}Z<6MWѡ?ifh: Lȁ]C,eݗM!u1M2 sc?'#om7h}#+\A>u9DUt3gsNH 7݊y!xEMd&݂-9F?>ʗEL $ %2,ڕg qðdIpSFhɶ1b1.l+hb '^J6wQ'L_ަ,qp[.Akdə\̓_jH>֮.|Jƫ,!†U\vjq(,! Bqeam&H*t(QSWxأ|OmULXPibF'匯eF2V} ͤui/NqѻSAGB2ia7PP雘0욊hVgdxg[|ցF Δ=3lJ:6hoFUw>s8Q H( ^u.e8-V0 @Tdj2n̬db hё@FBad#(V f T͏_m⮺ryG{4 ܖ3!b詥ҝDQ4  =1 Cdv)ߣf'tI9i##K>ϑ{!;bJb)-#4^zRMNxѯ&~/^AvOKbJ|zഩ%~5UϼR-S#E =XK4z0#kRgwKq/i[1)_Rnr6.FiHQ%VS&ZV0a`ǽ OuJ),YT8QH簎u)vӛ{aOS ' Inh D_o̾}) sV_7949]=Գ\U~.[ގ;!&]΁.ت>Fg.5p*t'Sj7ʠ/LG9&t  !aC'lQ2ؖحcZGE0? wb}kTBR7 RY8b:qkf % d,0kMrE2&m˽xdm4!zK-+Vس=VR |Ş"t+b& . ,?#r;S XBE0EBo}}3 °9:kB&t|- N`C.j qjo8qL81="gYZYC)GW+\87DG̠uRn鯽;iEHZN3* {V02'0F7E>@xu Yút89p+eCp-#*­k81Ym+AHy- }֭9G$'yjV`6!0 .;|܁aajfn~e|ŻGpHԟM58n;.'XgĜF8Cِk+D#w66+~F?,U~m%v(]^6|Y\='n|]=8킲\FћK`.>';DU.Zn?\uOzo[rlsᜡ[m)otCzy[L_'s 7\,QtI"U%XvegK*?]eɽ*BA2 YVsƄw/PH'y&gtξcI[7$tXei}D K4FŽ [oi=iK&@ߘ}*N#z)3hW&hgWqHئ޻(/MSWv‚۽!=SHID~PwLK}hX%4'Ff<O0|\xtIqE.eŭ5ޓbo(* Z/)Ma_J9/7?+hPJ]sDhpBwq``mLpK#DQGMIjqH8yԖ)-B *Gb`@$ӽ«huke?pcR+8gq֖63t [JN!j пLE˯ S[ն7a@y7Tp1bzHB5 Eix$x|bдA\F=Ѥ5S0tdMp'dgfaɨKw]/¡q gK~L ꧜'S'Lz9hLiVEr{@z]- p~:OJsb{զfi}=N =@mGcAm"}:rWȰoPм's:0sL]WCX_JTB;chId*yg꣱]g6꙳2ՙqK a)=aTe. >>NJu1i"`jƓMaC+5To6|iSWr(}pP|shϾ ull:], JfpyUL3(T*voު=ak};A{.O;[?^]-3lEt: x>veڢbhg}:yn0L`k&aa{Sg7P"wUW9wn*p  "? ,_B.Ktw] `]EukY af'yrs JZwU#\Кɾ'`x ߟBd:֗LIE|]r `%HI"`k<sIO/_IQoͻf e||</#$_J띾ɈCinjn$fJqam`d)Tv5^l(p[遒䝈ly> v}c!#_w.0Ի 슕}!#ȠO|rƂ<$؏bHt~J7{#ww"h04SiMu 6-|6zm3 T Lׄ}>B q·E\,z$ɃN$IAeq/RgVj W{Fwy%  im]Y9t)$u=`W)dqr4,X[puY|hTzo"wcX;ղ8\F4Οg5}ң+r)7MI %cFs욿 ]%lǏ>58SRݓa1Vv{ "NLVc' _WcC9~qVֹ`UXxC$_ -g6JmD_)d  OԔG:I#3QEŀlhPYXhDI mu3X181KY8u7*c K)Xp(7V< .F7=x/wNE}}8ׁw W&ne>!mK܄Q@2~7d د42GŰ]'р4:$ {ѫ&mg3y=cE{D~<"QP _w5%)sBG}Nr٠f9%_^$&hP3GE4}DHoVy ;_i7Bn&0Ѽ`x_Tw7mv.IX~j!ڡy4;чIDlLf#,KAɔSz" }%L/mSR#lS& B-u=2iOKZabuk1h!2po6Ww102A*'3Q1vNBA<zqZЄ^9J&ZMDkˎ.aC_CDiW N N둗$ NoWv܍Uw&|6!Y彤y׶~gdr' <6 H>9OLg&i}I#Wvi(9CIJTe@Q9e;<.mB|gUS6ЕkcOIq+ /FY֤P zmp _&[TK&  ǵ+$Xf-$PBxpc E]R҄)4P=Z`,MRFl2hԵ쐂x+K헏Iٛ~EFb# S7TNˏx9z V:H> 29:ѭ\Olٟ 0sj6+%d_ 6 F)ԿSV /)@*=#BF\Oxu** # ~{,{=3F[a?-AY#cs=#ƶ?Hi)^ibÇ%ckv;'Up)ֽ98[v/rvLZ~.| '\LlS-XN u$LYz_ݵ@bY:З聼 Mb~ P3+˜H[4ƠNp% ]Ocx` ju,Gܝr|>ֻ8@ezHTi@Y 'U4 }ɑTa[8Gp*wQCYSrN Y虦fhT_jaV-<F"ҷLeU鈰 rf'LXzpnbWsϚ$n bZڊEBX]JLvi 8|X;nhMcv-!<%$iyQ%ý]s\9K9d TgĔ^ t"S6`EG+)R2 @Rt*,`5P7anjuFErr@Sh Oŏ^ɰyfT#~~\c?@TM $[dfmr/+%F:W0Ix Uܓl^#_]Ysk\ Kd$N:oȢhԃ;_AAn͡ٺ-v̱J!&Y],)mֆP<毿ߙk*Ɔ|VwЯ9c\ug?=etiKKTq2q޲msWlO;S|*&Mh%X.]h i&dFi6]yG6*PCSˆ%?w7^p gJdL2_0snD|j)P>1ubOefsA =mO!cXRM. #ӁߝHaOx TsE{PPt߾vyXq.7NڥŮ6ɮT<$c/of7mGZ|}_], -& endstream endobj 310 0 obj << /Length1 2280 /Length2 9436 /Length3 0 /Length 10770 /Filter /FlateDecode >> stream xڍT6LR€t -)! 3PP" R%)J4-H |95k̳Lz\`%D<s11CBPO)!0'+$+,! .C n* "qj C@N ∌hrQJ"b@7ѕbx@]+~ 9B) o u[FxR8"]ܜ8r8m'xy/guXC -Eun'r29 wd4+u@QVBVO}Vp3•F/dO`'+ޯp^ d u[* 4pATA~l  ??xZr%F sX#ˀ@! YAd ,KI `g=ͥ C-{Myy_ߌbfou9nhW9VBÿ*B=!`m([lkNm+r쑷+r$RA`_K'( /<$ yx5  t XxTH% O~#QP_$ *Fo$FHHڿS7u#d(!9-#dV"A l$ Q!푹}K.Ԭ SK s4 Bdmm!戔AȬߐYofAr#N /5j$3prXn?RuG> %8 tl [._GEg^d\ A y 2CW@Q@-";p"L7Cz{^@d_N;l_71a@SuJ-'W'"gd?NnF^-+7zco2%RlM ?\6M,67yL=t9$PvQj=ב""ЂZe[*e'&T]eF)\t ̽DP8J%owQY&;(cYN[Y-]cݴ UʟK>K~byfhN]iW"ḁ6+gS6|/U&[ۨvdj`߇ןT~_]]AnfX񏙗?re2TNo+_Y=W1-y].M+՘N\KDxR(|c'fLؓ<$KDv+zt,%&06,_N&ޓLx3ŧ>|!SC)A㋅Sjǁm\s_NɏOO۹QU6}GW'ZfTNb%4]>ii24ܵ[eTܚݴR&eTcYF5^mTD$8:5մUXWm"1s 1%2ܭ6ڟOg n8STY|?2G_ָ\kq;y3oj)oCwYɹ.=o~#ykNRZg / z q^7ݐN*n-ٚyIL]0{sU.YInG[]W$/E"Q=K{F3 uTb[e KXϼ T\/(d:o$f菘Ӟ$۪&IDTB'C5 őrO:MҵO?;<,XlaWrVkXX @! LTĎcyl}+4( E -D, 1tA6rglEC#,%"7vbEk"l4>& HƆI4Պʈ&Ϗ{Niq0[XzPo0]MҮ|A^1R2 >pw$]5گd*Qv>}춵yط8hkaq=%lh~Tіx Q>HbjY 4W~R:-Æo"|UGO3eJ2pt H/ kgxb\=zpGָU29SXY (/#mhб+3컒{GkMk(pm 3/O3|d=B5P] d-iGL;`x~ ^=bԧ A?PvmڈonOOygKie}Nϛ*_ *hiM~TJ`RoM3ЉV:LheJ{t)L&[|ƻIDrk-'X~/!]l2Q)=clW:]zP$z>4H .CA<*qCT_n^ilx?{ԥY滁ˍ::]mxD:!eiJ0o j ײ>mĜ#㮷ՒCEԜ(.X%Rw ꕒش=d8?4⦴{ȯ&zj tY+L}udbq}bR(mFMokgJ!2o;BJ ⱟ_3cL6V7cp_l{S(]:T,PEݻEg vI[ƻҎ8~ u̇*'Lӻ+K8z3szܢ]R27^~_+ݪu^jʂ{5K"|GUk}?~a">./TО(41Ξ{rNm-r`׉6x.tCFM`"N&EQ'[Q`ed æ[u+ 7>\'qX1UL2,5;(Tcެ>@xlIޕZG-qSRl}rjBSB^W䦏M[q;ءT^w(['yQF szɾ.k C Y/RCJ#Atar9PSCuǧr8<[M.:% ?W;ƮB~ j)3_~8-u*K/շ* 9rUҮ 4&xG/?Ƹpɷ(8́\!l|1ACNTjyys|}[oz44P./<{@@ZB({eu{JuGdǫ?eOdCDK`ZRԚwq_^?~eb}I(l9#ՂE;mٍ*Sr}7g[W=6gJJX__ʟ͋B=pKM~ؒ}^$=&:3øX$yA ; ff+acT٭cbpLv|<>QӒmU#@Qի1!jdnr$'lFpZXW4(3#QSTuexb =9N@J,7#._PY&5b}R3J L8%ICY9O HX\ uaM>Lzfk s^KJr=Z l:gR/L!6,hC/XЅbtQN>FM({_!<3)R T^Vw)\`1HU&abIԚLϹk[(6^ٲ)$цI@{͕a\ !ej `TPb:{.Ő3zx2ă,u Nv4~2B?wYOD#.̢wV:l1ը7G-dO-0ߞIvgYA|ʝT4ԗMELB;X--: F3kS!!eI8Fc[y%l췡}VXzE(@z>=|X]$4nG(Zy'RJڗƥuVbJ(gV wq8G3N(C,=4k; $q͐IQtkCy9~t*auF wJov=;jz&iO/y4:W%͹e1I A. +Se$+8q?ktXmy}׫+^j9."_#W)^8Ym@HrU_T]y ܊))_ ҒO6X\I- ty*SwjQ/HW[ Ϊ;VbԤ6`~K }W7 C$%ycV똴пt5{P"-_A#xkFS͇pֱ}6 =jNV#PÜ*t?d1! :` LhjVŀt^L҉p xi3]O9~+%CnmiCl@J,F&52)%c]fT@cϾQ7-#V7_۹b'}\F-c"X \ױkn'0 0'ǎq[\<ܪ,Siwyޜ TIs&teD-&aA Pݫ6:'`:|=GgI˵`ir+mVRAy32Y]V輭r +l *ef{OF7=׫Ѓ1GF=+LيQt XWTs@6+]OiC&C}W,Ƴ%9£(IStW7hh-(ǖ $-fҶrRE"#MMlh H2r>+9+1M(8 )UY"oi','6x'F:a}z;U|3 ЩcoRsUL6^brsjH8b [IU+sr銣4^NFITWG_ۏ}PlS773Ph;J6 fޢZVyR! 胄IUbFGm ZBk pYҊC}Cf4+r *]11NA]iLza,dM #]M8N5 _V<'b Y~DO'RGp)("O?Oq `:70 gJR~ %}y] $U'Kޔ 𠙷caG>( OMrq4G>R[W&},RvM4%Difx"g$kP4>ҵ'a 0bh }L~Qӄ+-*u!_9Y_][v^E]}ꝗGdpZ4EʢaCR'd8_$Pgzԣ#-V{.o\8m|Sǽ0)TKOCisawThSe~fmKݘIk&ym&̆-5Aic7W'KJi&#%`hX^(!ـkAp#UYLL G U}#p&wVÁMRpo2MOżh{! KIy?Y3L^L7L: 5]6Iy% /QZ8ӼtQ˩jd[^dE xMk_0QRN&ƳGtfntrs߉k`P<1[bS:6a0(nz9)BMw5]--ybE u#I0=i;s 6ޫƅroȤsVlv(Y ,() bRf֭vDy)b0@,؁f!\w ;%NOv2BN'$ѪmYQ ^f}}[- ʻJI“}50۲%vvbF%ԥmXBj%fuwn${qne?OMmtQnV o5BUKFG*K|ɧ[kE zSZB#CƧ?*;'9Ft}Ӕ{%lra(5j_?i,9%vHղ%nmʍDciu޴ҮT1bJ )*o>vE{[_Bդb9>M/lhX1gs32*B{1zsL%.G45u5⃜1hVFއ{@s*CF.(b a%5GtkL 6UuBۣkJ,/9ۅ>[2C;d-u۸ޗ-qaIvSO[T58tϓ{l!<#h%_z-HJw9 ΔD~%4(,J[R+j~E؉?Tl&ddyaqЇosp=WXⱙSw~,fV0jiDj!'4M&G[DrJ XBzWYj0`H+UqϜ"Dfl_i1=Lcs)S96ȕ@65~x `Ss6^x9!Pqf҄Lsa1@F DOH3a !ST]հJ >;Oϧoe e73(莨NG(I&fupo!|R-GQ,hp!{i]8ҒE=ŭfCtLP'woUR^E/8MnfD|-qL[B`龦ڃʣMU9'[ !sC:;Vj;"(1 endstream endobj 312 0 obj << /Length1 1901 /Length2 5923 /Length3 0 /Length 7079 /Filter /FlateDecode >> stream xڍu 8kؾ5{![ƚ-KvaYcBHd GRHP%F(FuN_]s]fy罟%gzY8u1hX h[X( $$M-(h!hjA+8A+᠅CqN#cЀ`ye2H@J;bʀ6% M- "qk `%% E@р1Gnt""8$-~;`c}0$a$ `_ƸX8@ 8ڛp ce}#lAU, 'ܯ'..'@$05(vEzc硾PLp B(!_y`8oIo$E0*aZ ySF`.H\hI0O)K4˅@Q8@)ʀwq: o aЄ B<1+! xN߈ ` wCG'pן|,f N>B/ RVF6Vb?3Ǧ$ ii@NVWRB%>(J(z}_ju CY8 {@r yIm+HaaPˁ0>8cK5a8(a h7?eDx"0S`-OV @M1ވ7@Ńnx NXߨvNKZNbja 0aap HI18]R4TQ>~ %Fo$M@Po$,-CP8El?A) ߌ BqR) H'ˇ05:]}n@d"=}F&G`e>(pCEPyN“UY*%f}I* ^Iְ5ƋzXDI35r{ٳ[|(`_茥;s.c\>rKS~1SqDP3H7ET˔b^Tv9>Cs,].g_ Rr?;i\u E? !%גJ}}}<K9hR]fs5bsTF|e7@aԢ\ٮCw[n?+h{D*y#9FWs%>qm_ cSPrp?ό9A|&2EL0`Wd){OϷ b0M&ɵp7EPf uA(|$Ku#{T>g6wwf@|$W-T u偹̡Mo#®?[~s|<!D{K4ǚ+gznd雛i*a[ܮ8Wԃ&ܨ^bg.;sز]'fq,87f5tEgpd$Ko:ovKSjVr{FG#T78p#!{CȆ*+QEM#2rTmlUjKBJ" E:*M)"{eR9&-<{ \$T{&1eqST)*G<3r_1+w=.4hhVnt~xSd5*Xڊe^wMQ-ړMiS:}.Wh1 lbXKy시tb݇i/!:͐DbΖ4]F!aBz)4y&Qbyy'#J\#L;k<\nn?}*u6%`\lUeR[d^dgG:"Fco27W`;0Hyu򉭌}i{XzoX}Dq[*^G/qY8"whKJx]n?mVcPQ ǞbxH!30Ku9q's;l3P!~|Xr*|ԋ̱ѵA$d&IfhK7p7zDjx^7T羾Nވ-~ O\%u(娹c^k 6cDfm 8fm_H$k7<0K"0H5ΈOgk^N{oeT*!_.ӿ0>DuAۆNw17?޵x&K2EVzIC~^PW \Y&L{i`波UWDibN7;-Z#Gg/| R_=[S4̫&oC$Q $lT[wKЏw2{ F=ٸSTIpW uSRd>(8x:m.JяV2o4 2P gV8M ܞJ 1LMIH|F;LRMhi5q`_5z~7Wz]|Օń"H%Fkv?|]_tI9LZSMl2[L$!Y4YF5dCC:*6IsBUaBmDO=7f095LR?|L6#\\L̂N@wKr@q6X>!t-oMpvn9ɇ{O_7A}zp&AjGmyT5,x?A.qBy0\`}9ސzMjlƯ*b.yҨ'L>Tq^ 4<΂FJ$kT3M/Tpc=, mI1»;kٟO!/3N k [pЋ]f+WPE0WMT Q%=Մʂl &DqE!Qi~LQd.`CΠ'Q4>(?L> stream xmUMo0WxvHB!qmU^!1H__myݷDULG^͹t߷.k4c*S'ҵ>]g,yݔKeF$mS3&qGRp`I_3[dE4ݹn'&9綐7UaL)l:M z!YU0rўo>ν9},lj'}4>2]ݼ[ivjs92V+Vh ~y8&X-MmM|ŖE LS7Њ~& U 2X(pm XX(W8X&LR4=zukTGEm7h8Kc`Iu(!a <#G >n-tJ!]O2`̏S#',<ؓL%qO8\π: 3ht ,+9ugCwËpD|ORɉ#ɇW m藒1NwH=8! 4DCp&q"pBCT/9!ɨ~B }Rq҉TFIܨύ|nTs|neEA;~<6OIystg>O:yұϓN|I/|yI>O:yҹϓ.|R T<띹_mKz}K=W7"V{/znb endstream endobj 315 0 obj << /Length 753 /Filter /FlateDecode >> stream xuUMo0Wx$ !8l[jWHL7$Q_ ooffg'Ꭓ7 nnҦ8m=*XaAK 2)pñƀ28B\(PC/.8]9Y;W HPXJ\6g3!GdRHIMD`֩_'>GN~*,  0 `2A%r9"7BʑO=%Ɉ#Sxڠ_Jz0iPSH8pҐ,΀l 8!pŒ9 5#N|~LĤXs OZy?tc'4yH}ή}k~3#ȯ>3̼L}f"3{Yx}fIL}f23{%,g>Kf7ÉfL@,N]f%b0^u۴? .IF endstream endobj 316 0 obj << /Length 711 /Filter /FlateDecode >> stream xmTMo0WxvHUdCmU^!1H$3C z̼ïg)>]Ͳ߻swչ"ںQ;4m_%= uS'N%Fwڴ.HS1ʧ`׮o/x͟m/с!ZKNN~gEʪvyW~~ r-Ҳ\b&jr ACu ad)Xy x &I)RV˘.LI@o_pi k-cp [ ncXz3N4ִWYf% 9%b*ԩB+|A_F:C9QY5KE9rW8faD>ϐSG&xV_kU8!80L5#D ԏJ X'9Ł?: iYIWF܍湛n endstream endobj 317 0 obj << /Length 683 /Filter /FlateDecode >> stream xmOo0C@@8l[jWHL7$Q!LUzSnffonh/}f}emy9f|vrvx}[(mmMyTnrlnwwVqTrvԧnfx Wŷ?yQJ ySN2k1ꯑJ.g%мFw66XͿS>r}|oݥNrl6rGىǼ?;'4>+JV}}Ⴕ.Mۻ:ɚx\_h`:Pp/ *,}!$B -fu[ǘ6LQe }ĭAk2$mAGs AI:םJ "ʔ43:KaCg" s rJ_i:6dPtk69u̩3ȣ" P݀^R/z0cP_Y̰*z~ʟ''Mq_ uWG5do9JOpH+8QhfgBfg"fg$fg,e@yɟ1S3SS0S+UjfjCfj#fj&.]1SkԦf44U44 Kx׆_|0n:8pw{]Ap^N3^?'y endstream endobj 318 0 obj << /Length 696 /Filter /FlateDecode >> stream xmTMo0Wx$ ! 8l[jWHL7IPV=M̼ su;Uٛ=w]yil;<[[j<=?׾+v`&ߴț<^*;~&Q>MS 9_P{=s@dkx;`VY`s4JaQܡn.Uu9\Y6><ٴ.Z.4>Dӗ}~r:-d0VWk,8yLһʮӮђ[*mLr?q 5F8@=@)& 8Rx uD\j2HV0CzL] bctI g$`htы0\F0s jd< I6zg W qȐ+#k .bsrbmXK7ǵH7Gnb>&jؐu1VljOu$՟qWS/%1{\xB!K(hHTЖ枃Jρϯv=k2UKς_:~$/ ~E+7ˢ/ l(/} -+ZXukoԝE?ZK endstream endobj 319 0 obj << /Length 695 /Filter /FlateDecode >> stream xmTMo0Wx$ ! 8l[jWHL7IPV=M̼ su;Uٛ=w]yil;<[[j<=?׾+v`&ߴț<^*;~&Q>MS'K}v}tƾ`R\ws*pWl:*;m_Ű=EB.=]6E%‡hWvE;^N ƣՊU ٟweӟQ?OIz^UU|ڕߵ6ZrbˢXEIS:.trA&TH>4"PX H BM@5*08WfH AX v.2I## .zӘˈ0Qa8tcpN0A2 @݆s>^l>^wo_j4Rrtsľ x[%QLuQ.ݢT ܂PKߗp#}߂pMAM37CB2>*R{@8񩎤3 }c$f O#z  ) spW)9N{=g-_Z ~YK/t:/~e}Y%៍-t:UEk nmGkp\x{)ނ endstream endobj 320 0 obj << /Length 739 /Filter /FlateDecode >> stream xmUMo0WxvHUdCmU^!1H#x?gx]OTm$|͜s_Iss :L;<Sz==׾f`*_`ɫڟk3'iѴ}=M;7rfnj-eSӵOLg~8 )ok A8 $`I\3`Af<Z]! xNky"7 _㓧q H`nḱRONH=CpB:# =%888QA~!*zƜАT?!~> tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo0WxvHUdCmU^!1H#x?gx]OTm$|͜s_Iss :L;<Sz==׾f`*_`ɫڟk3'iѴ}=M;7rfnj-eSӵOLg~8 )ok A8 $`I\3`Af<Z]! xNky"7 _㓧q H`nḱRONH=CpB:# =%888QA~!*zƜАT?!~> tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo0WxvH UdCmU^!1HDI8߯-@=ۙڽ١=?w]pwdV^ڑݧl#oxdGa0NiqF?Sր'YNR}{f{x2A! u xk={Exo"}Rɑ#x۠_J B C쩁b8!=%p&r"D9 Qg̑Tu+gGNN8O-(7ZRntH ʍ(7:hEњr1+w(O:͓.ndm'#Ʉ'> stream xmUMo0WxvH UdC۪TBb B8߯{ .@=/ۙڽs{K;K.k6/k+[M'ҷ>dyӔKe'$cS`vfSfK}fƁVGGf\bu<19w|擬CTAW $rG]IyMsh$aW7y̟u? sK-`θtJ!'c83?NaO<Dg!;IX 0z)rЃ@kpBQ]^Z7! / U <ɉ#W m/%]cX! gȀhID8QN~ACT/sQQRs 穅ύ>7: F+}n4eE=zG~<6OɈy2kLd>O&y2ϓQ>OfdV>OF<dR'<>O)yJS*}𗏿tx>z{O->tՍ]*3>cC~ endstream endobj 324 0 obj << /Length 739 /Filter /FlateDecode >> stream xmUMo0WxvHUdC۪TBb A!Gp?gxYOTm$|՜s_Iss :L;268{zb/}WUjWm?fd}Oi=7gRd{nCN8oͰof-%6'&9Pu`L/"tkں(a[ duS $xqa MN{}m}gىx` tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo:W5?$R. d9M eCkmCp;;w~>|3E_?O]5߶w]Occ]=~?}Oyh9%?۹׬B|Ɯ>);vw%g43>\ 6 EJ78 1{~`W(-;]%=xe_,b+-O;q\L}UI--=BKE1p[! Mߊyu>.N5K)Wb٬8i[_uʕMzQ)V(Txޢjy!Z2P="Zd0\ÃGR\).2*Шa!U,H`+j.5Nα@VK-x%3%AYӀzΚ>kP#5m0Woþj.ZT$X/)n)#Wo(oRZ $Kp4Z-b\1ܰJ P"GXQi/8k^Zq:Zs9dB )sL-7xJ`aɽ)f$1 dъcCZC<73JgznHȰYɚTa,_-O87}KԴܗLloK+gJ.GZyVc48Wt]:P~`rZq.n1] S/Pu7Ue:?&?!d&1yHn5)yғBx#1ޞ]Go׏M?X endstream endobj 326 0 obj << /Length 900 /Filter /FlateDecode >> stream xmUMo:W5?$R. d9M eCkmCp;;w~>|3E_?O]5߶w]Occ]=~?}Oyh9%?۹׬B|Ɯ>);vz|N8}No)e0&h?q:P_ X}ac1+a  jҢ~]ߏ{_r)4i_px`!dZ>i]<U_cr%ͪcךv[\٤ժX*be-@E-X@-꩖xkM PY@ ,#bEA 5rEqIb>,彐A$ G#e"&c D`%rE*s(Ǩ5ثCI*=ǔ^pk+ ܛbVLbX+@8:13Jp3<|6 ^ΜANVjRy9cpסAM}Ė)|֪,+pp70h8J+NK}Eլk)up >o U^g{_e{]*?`CBhgiیtV;۳ѝ)(ZK7bA;E^]|sQ endstream endobj 327 0 obj << /Length 750 /Filter /FlateDecode >> stream xmUMo0Wx$*B!qض*jn$H$3Ch<~3~~~ngjv9{C{K;K.k6㳵ችm#O7٦4\ =؏8ݿ߳4ւ8͌>sIvdXC6OLx9im$l6Dl_7ڞhz*{pɲ2kAʶC+mk>lpfIQTT?LA>J e .1PbpqH I$\kL8Hb،Shąr =z51XQg_s2Ē+ sC:CQ}.'c-BbOEu+Xg~:?aj B.U $,ĨAA 2A%%" 19hM_)ELN 1sR3fg =傸aCYjV^w&L= 3nqFyDŽϠOL5'pZx?i^x?IGO:~I4ϼt~3][gF~Qgf}fB3y,h3cL}f23{,g>KYN0`^ay{7)q W7:*ሟS`R̯ endstream endobj 328 0 obj << /Length 750 /Filter /FlateDecode >> stream xmUMo0Wx$*B!qض*jn$H$3Ch<~3~~~ngjv9{C{K;K.k6㳵ችm#O7٦4\ =؏8ݿ߳4B8͌>sIvdXC6OLx9im$l6Dl_7ڞhz*{pɲ2kAʶC+mk>lpfIQTT?LA>J e .1PbpqH I$\kL8Hb،Shąr =z51XQg_s2Ē+ sC:CQ}.'c-BbOEu+Xg~:?aj B.U $,ĨAA 2A%%" 19hM_)ELN 1sR3fg =傸aCYjV^w&L= 3nqFyDŽϠOL5'pZx?i^x?IGO:~I4ϼt~3][gF~Qgf}fB3y,h3cL}f23{,g>KYN0`^ay{7)q W7:*ሟS`R$m endstream endobj 329 0 obj << /Length 672 /Filter /FlateDecode >> stream xmTn0C6*drضj^pHA@Cfy'n`g#govh/}eg羋򶺜m=Ooٽ[׌uRۉ=Iۏw{VQҜ8ߛIߞ3d_ ~~hZ# W c *'qU;HHV7xwuɻa;zopO_`_ݥNd0m6G_?[6vLClw6ZsaD%!p%blcä  PP[ u_g_x4$O<X^\NB8 \;cBbMx y%P 3jok:E q:/d48Q4A2="\šY+ːs(5$Y r~+A\HȕWr{Nxo $TL~K//p1sQ*GG-G-GzA>|)3Q/G""&!uN>|%h8hh$hb,n~ᰏnˣ+p]h \2 M endstream endobj 330 0 obj << /Length 672 /Filter /FlateDecode >> stream xmTn0C6*drضj^pHA@Cfy'n`g#govh/}eg羋򶺜m=Ooٽ[׌uRۉ=Iۏw{VQҜ8ߛIߞ3d_ ~~hZ# W c *'qU;HHV7xwuɻa;zopO_`_ݥNd0m6G_?[6vLClw6ZsaD%!p%blcä  PP[ u_g_x4$O<X^\NB8 \;cBbMx y%P 3jok:E q:/d48Q4A2="\šY+ːs(5$Y r~+A\HȕWr{Nxo $TL~K//p1sQ*GG-G-GzA>|)3Q/G""&!uN>|%h8hh$hb,n~ᰏnˣ+p]h \2 ᫄ endstream endobj 331 0 obj << /Length 672 /Filter /FlateDecode >> stream xmTn0C6*drضj^pHA@Cfy'n`g#govh/}eg羋򶺜m=Ooٽ[׌uRۉ=Iۏw{Vq9;\ظ{32bƱ)`Pk IckgUPSH@"7#?d 9aFm-P!.@'1 c09SGTX3 qxryB4 AAN8pЏ}% Jxxm_p?0䗒䗊/ TB~RtА3~N>|T%9%cQ/G:%uF>%WV6G]$ ' $ML/?mwTkW XֵdpZRF ׃ endstream endobj 349 0 obj << /Producer (pdfTeX-1.40.24) /Author()/Title()/Subject()/Creator(LaTeX with hyperref)/Keywords() /CreationDate (D:20230113161209+01'00') /ModDate (D:20230113161209+01'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/Debian) kpathsea version 6.3.4) >> endobj 261 0 obj << /Type /ObjStm /N 92 /First 851 /Length 4344 /Filter /FlateDecode >> stream x\YsI~ׯa7p}lLl`lsBjl-#73KX}؈@ե/j!p:M8 :.Һ0WDXpO#@q 2Li:&QM4cDsGph-a8hop&FR'FRyb, /ubU&04>q̛dUC?W- m[\I◣,v ]E㸢(V@Vjq9'^QGGc2Zuۏ>GG<-MXU@+g2SSfq< WSIiiuHEM5e-G*]%t,QGZNBi=)*Ks`qL'c<}ZIôB0:E^[/G ź,MŎ:k*EUDqzަ-8FH X-r/YfѯzBju $@%W \ Na)7baF'B"W KU'h'zR틣 {H h<\6>5͑0@^r]yT:@kURWtA*ْ9{Z'޶\ս"J iK4)p4mhJi** ]dpITv _\)]{Z%K~ae=JH%l/ڻ^ sԽ،8ZT R=\iS]7C9%v^ ze(c{+n 떲mSb*Ie~hut/.yMtPUFW?6QC4`4 ;l$'e 0Fk o Aə=݋T@ kDz4ڰyM@!=^w'~ l C[]0nF/]Xb @O W몾Kp JZO0[kpTsI^Etx8昿ڿڨГgR%S?TA&2 ke*!Ġ?GQz\#0ͯ&t wj=X'l>mBoEGNbOƟ7&}\N jbl v櫌Ϸ{j{{x( I^z0?HgA<̛@n pknb@ngh*lu:>v>X҅gn@)G°y j\A_OtFլ?Rb*n+(& goB_(FjA$IBlsyr Gw/[ocI՘L&;K6$Qc LƤuS^nc*$x&ۅdw~FdۈMQ oj. ;{ qmcd3. vz$ wz1j&3W;UGxt.yn\pXօspݾ 7់F`dEt`ݺɭD|{}lxm(NnTwbA"Et8ݾu9\uBk_KvߨۀEU'l uo~Uf?xOu>xSxj-C.[𳚄tOP8x Wm{=DUhz*#s X}lpZJDJJ-8jIB2VV?Ye_ӌamK,Vi%)],SQ#+>l ѦGUU-՚DUUbXGRtlRܦJk,rApYoME}Jb+%IG/u Gh_TM Wjxazż*g}\hau@"|R%02|lyNĽY|qA|zl3Ym%>M~{)7|{XG=xU8ڡ8*BMWM JCs=il0j;¸{watɮHߊ,be 4ؾ$쀤q_HsX8\%GҮjLr9ׂ1Mc(|V{631*F ̓{LlD-^ |ۺ]gpS|WdzJבMM>eC5,BEЗ+:҆qvNys"8bе 8Bv SZQ G ѱC*ȱ-Qm, Q/0{Q?$ <56748D39F3D7B15BA61FEF4EAFB25AF0>] /Length 858 /Filter /FlateDecode >> stream x%MHi~ʏ,5S3:ٗ]VeijZ}lvj9-UUuD"*" j`ZDmf"\=yy}2siL\ }L9LILEPR>%0lRAVGe$k e9 f5bYdd;YVBsl)saYv5PKl:Oe=, [Oe4m$² $̲A ,VX@,,X+a5 ]zahW7+jo9t2( Ю3u`pKt2:2^'=a AC0C0 aPzJ{6/D >rFLU]s})&E?I8;.x (P8|`1vWPNjZ(c(sh =TtzZ.ACŬq:j.jb[?zAeu62Eʨc,6*eܔvOAbۙVq1 .:~֫3Nq&]G'*xނ1txoPvn{gj>= pkgmaker::latex_preamble() @ % REFERENCES \usepackage[minnames=1,maxnames=2,backend=bibtex]{biblatex} \AtEveryCitekey{\clearfield{url}} <>= pkgmaker::latex_bibliography('doRNG') @ \newcommand{\citet}[1]{\citeauthor{#1}~\cite{#1}} %% \newcommand{\graphwidth}{0.9\columnwidth} % clever references \usepackage[noabbrev, capitalise, nameinlink]{cleveref} \newcommand{\dorng}{\code{\%dorng\%}\xspace} \title{Using the \code{doRNG} package\\ {\small \Rpkg{doRNG} -- Version \Sexpr{packageVersion('doRNG')}}} \author{Renaud Gaujoux} \begin{document} \maketitle \tableofcontents \section*{Introduction} \addcontentsline{toc}{section}{Introduction} Research reproducibility is an issue of concern, e.g. in bioinformatics \cite{Hothorn2011,Stodden2011,Ioannidis2008}. Some analyses require multiple independent runs to be performed, or are amenable to a split-and-reduce scheme. For example, some optimisation algorithms are run multiple times from different random starting points, and the result that achieves the least approximation error is selected. The \citeCRANpkg{foreach} provides a very convenient way to perform parallel computations, with different parallel environments such as MPI or Redis, using a transparent loop-like syntax: <>= options(width=90) library(pkgmaker) library(knitr) opts_chunk$set(size = "footnotesize") knit_hooks$set(try = pkgmaker::hook_try) @ <>= # load and register parallel backend for multicore computations library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) # perform 5 tasks in parallel x <- foreach(i=1:5) %dopar% { i + runif(1) } unlist(x) @ For each parallel environment a \emph{backend} is implemented as a specialised \code{\%dopar\%} operator, which performs the setup and pre/post-processing specifically required by the environment (e.g. export of variable to each worker). The \code{foreach} function and the \code{\%dopar\%} operator handle the generic parameter dispatch when the task are split between worker processes, as well as the reduce step -- when the results are returned to the master worker. When stochastic computations are involved, special random number generators must be used to ensure that the separate computations are indeed statistically independent -- unless otherwise wanted -- and that the loop is reproducible. In particular, standard \code{\%dopar\%} loops are not reproducible: <>= # with standard %dopar%: foreach loops are not reproducible set.seed(123) res <- foreach(i=1:5) %dopar% { runif(3) } set.seed(123) res2 <- foreach(i=1:5) %dopar% { runif(3) } identical(res, res2) @ A random number generator commonly used to achieve reproducibility is the combined multiple-recursive generator from \citet{Lecuyer1999}. This generator can generate independent random streams, from a 6-length numeric seed. The idea is then to generate a sequence of random stream of the same length as the number of iteration (i.e. tasks) and use a different stream when computing each one of them. The \citeCRANpkg{doRNG} provides convenient ways to implement reproducible parallel \code{foreach} loops, independently of the parallel backend used to perform the computation. We illustrate its use, showing how non-reproducible loops can be made reproducible, even when tasks are not scheduled in the same way in two separate set of runs, e.g. when the workers do not get to compute the same number of tasks or the number of workers is different. The package has been tested with the \CRANpkg*{doParallel} and \CRANpkg*{doMPI} packages \citepkg{Rpackage:doMPI,Rpackage:doParallel}, but should work with other backends such as provided by the \citeCRANpkg{doRedis}. \section{The \texttt{\%dorng\%} operator} The \Rpkg{doRNG} defines a new generic operator, \code{\%dorng\%}, to be used with foreach loops, instead of the standard {\%dopar\%}. Loops that use this operator are \emph{de facto} reproducible. <>= # load the doRNG package library(doRNG) # using %dorng%: loops _are_ reproducible set.seed(123) res <- foreach(i=1:5) %dorng% { runif(3) } set.seed(123) res2 <- foreach(i=1:5) %dorng% { runif(3) } identical(res, res2) @ \subsection{How it works} For a loop with $N$ iterations, the \code{\%dorng\%} operator internally performs the following tasks: \begin{enumerate} \item generate a sequence of random seeds $(S_i)_{1\leq i\leq N}$ for the \proglang{R} random number generator \code{"L'Ecuyer-CMRG"} \cite{Lecuyer1999}, using the function \code{nextRNGStream} from the \citeCRANpkg{parallel}, which ensure the different RNG streams are statistically independent; \item modify the loop's \proglang{R} expression so that the random number generator is set to \code{"L'Ecuyer-CMRG"} at the beginning of each iteration, and is seeded with consecutive seeds in $(S_n)$: iteration $i$ is seeded with $S_i$, $1\leq i\leq N$; \item call the standard \code{\%dopar\%} operator, which in turn calls the relevant (i.e. registered) foreach parallel backend; \item store the whole sequence of random seeds as an attribute in the result object: <>= attr(res, 'rng') @ \end{enumerate} \subsection{Seeding computations} Sequences of random streams for \code{"L'Ecuyer-CMRG"} are generated using a 6-length integer seed, e.g.,: <>= nextRNGStream(c(407L, 1:6)) @ However, the \code{\%dorng\%} operator provides alternative -- convenient -- ways of seeding reproducible loops. \begin{description} \item[\code{set.seed}:] as shown above, calling \code{set.seed} before the loop ensure reproducibility of the results, using a single integer as a seed. The actual 6-length seed is then generated with an internal call to \code{RNGkind("L'Ecuyer-CMRG")}. \item[\code{.options.RNG} with single integer:] the \dorng operator support options that can be passed in the \code{foreach} statement, containing arguments for the internal call to \code{set.seed}: <>= # use a single numeric as a seed s <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } s2 <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } identical(s, s2) @ \noindent \textbf{Note}: calling \code{set.seed} before the loop is equivalent to passing the seed in \code{.options.RNG}. See \cref{sec:set_seed} for more details. \medskip The kind of Normal generator may also be passed in \code{.options.RNG}: <>= ## Pass the Normal RNG kind to use within the loop # results are identical if not using the Normal kind in the loop optsN <- list(123, normal.kind="Ahrens") resN.U <- foreach(i=1:5, .options.RNG=optsN) %dorng% { runif(3) } identical(resN.U[1:5], res[1:5]) # Results are different if the Normal kind is used and is not the same resN <- foreach(i=1:5, .options.RNG=123) %dorng% { rnorm(3) } resN1 <- foreach(i=1:5, .options.RNG=optsN) %dorng% { rnorm(3) } resN2 <- foreach(i=1:5, .options.RNG=optsN) %dorng% { rnorm(3) } identical(resN[1:5], resN1[1:5]) identical(resN1[1:5], resN2[1:5]) @ \item[\code{.options.RNG} with 6-length:] the actual 6-length integer seed used for the first RNG stream may be passed via \code{options.RNG}: <>= # use a 6-length numeric s <- foreach(i=1:5, .options.RNG=1:6) %dorng% { runif(3) } attr(s, 'rng')[1:3] @ \item[\code{.options.RNG} with 7-length:] a 7-length integer seed may also be passed via \code{options.RNG}, which is useful to seed a loop with the value of \code{.Random.seed} as used in some iteration of another loop\footnote{Note that the RNG kind is then always required to be the \code{"L'Ecuyer-CMRG"}, i.e. the first element of the seed must have unit 7 (e.g. 407 or 107).}: <>= # use a 7-length numeric, used as first value for .Random.seed seed <- attr(res, 'rng')[[2]] s <- foreach(i=1:5, .options.RNG=seed) %dorng% { runif(3) } identical(s[1:4], res[2:5]) @ \item[\code{.options.RNG} with complete sequence of seeds:] the complete description of the sequence of seeds to be used may be passed via \code{options.RNG}, as a list or a matrix with the seeds in columns. This is useful to seed a loop exactly as desired, e.g. using an RNG other than \code{"L'Ecuyer-CMRG"}, or using different RNG kinds in each iteration, which probably have different seed length, in order to compare their stochastic properties. It also allows to reproduce \code{\%dorng\%} loops without knowing their seeding details: <>= # reproduce previous %dorng% loop s <- foreach(i=1:5, .options.RNG=res) %dorng% { runif(3) } identical(s, res) ## use completely custom sequence of seeds (e.g. using RNG "Marsaglia-Multicarry") # as a matrix seedM <- rbind(rep(401, 5), mapply(rep, 1:5, 2)) seedM sM <- foreach(i=1:5, .options.RNG=seedM) %dorng% { runif(3) } # same seeds passed as a list seedL <- lapply(seq(ncol(seedM)), function(i) seedM[,i]) sL <- foreach(i=1:5, .options.RNG=seedL) %dorng% { runif(3) } identical(sL, sM) @ \end{description} \subsection{Difference between \texttt{set.seed} and \texttt{.options.RNG}} \label{sec:set_seed} While it is equivalent to seed \dorng loops with \code{set.seed} and \code{.options.RNG}, it is important to note that the result depends on the current RNG kind \footnote{See \cref{sec:issues} about a bug in versions < 1.4 on this feature.}: <>= # default RNG kind RNGkind('default') def <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } # Marsaglia-Multicarry RNGkind('Marsaglia') mars <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } identical(def, mars) # revert to default RNG kind RNGkind('default') @ This is a ``normal'' behaviour, which is a side-effect of the expected equivalence between \code{set.seed} and \code{.options.RNG}. This should not be a problem for reproducibility though, as R RNGs are stable across versions, and loops are most of the time used with the default RNG settings. In order to ensure seeding is independent from the current RNG, one has to pass a 7-length numeric seed to \code{.options.RNG}, which is then used directly as a value for \code{.Random.seed} (see below). \section{Parallel environment independence} An important feature of \code{\%dorng\%} loops is that their result is independent of the underlying parallel physical settings. Two separate runs seeded with the same value will always produce the same results. Whether they use the same number of worker processes, parallel backend or task scheduling does not influence the final result. This also applies to computations performed sequentially with the \code{doSEQ} backend. The following code illustrates this feature using 2 or 3 workers. <>= # define a stochastic task to perform task <- function() c(pid=Sys.getpid(), val=runif(1)) # using the previously registered cluster with 2 workers set.seed(123) res_2workers <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # stop cluster stopCluster(cl) # Sequential computation registerDoSEQ() set.seed(123) res_seq <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # # Using 3 workers # NB: if re-running this vignette you should edit to force using 3 here cl <- makeCluster( if(isManualVignette()) 3 else 2) length(cl) # register new cluster registerDoParallel(cl) set.seed(123) res_3workers <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # task schedule is different pid <- rbind(res1=res_seq[,1], res_2workers[,1], res2=res_3workers[,1]) storage.mode(pid) <- 'integer' pid # results are identical identical(res_seq[,2], res_2workers[,2]) && identical(res_2workers[,2], res_3workers[,2]) @ \section{Reproducible \texttt{\%dopar\%} loops} The \Rpkg{doRNG} also provides a non-invasive way to convert \code{\%dopar\%} loops into reproducible loops, i.e. without changing their actual definition. It is useful to quickly ensure the reproducibility of existing code or functions whose definition is not accessible (e.g. from other packages). This is achieved by registering the \code{doRNG} backend: <>= set.seed(123) res <- foreach(i=1:5) %dorng% { runif(3) } registerDoRNG(123) res_dopar <- foreach(i=1:5) %dopar% { runif(3) } identical(res_dopar, res) attr(res_dopar, 'rng') @ \section{Reproducibile sets of loops} Sequences of multiple loops are reproducible, whether using the \code{\%dorng\%} operator or the registered \code{doRNG} backend: <>= set.seed(456) s1 <- foreach(i=1:5) %dorng% { runif(3) } s2 <- foreach(i=1:5) %dorng% { runif(3) } # the two loops do not use the same streams: different results identical(s1, s2) # but the sequence of loops is reproducible as a whole set.seed(456) r1 <- foreach(i=1:5) %dorng% { runif(3) } r2 <- foreach(i=1:5) %dorng% { runif(3) } identical(r1, s1) && identical(r2, s2) # one can equivalently register the doRNG backend and use %dopar% registerDoRNG(456) r1 <- foreach(i=1:5) %dopar% { runif(3) } r2 <- foreach(i=1:5) %dopar% { runif(3) } identical(r1, s1) && identical(r2, s2) @ \section{Nested and conditional loops} \label{sec:nested} Nested and conditional foreach loops are currently not supported and generate an error: <>= # nested loop try( foreach(i=1:10) %:% foreach(j=1:i) %dorng% { rnorm(1) } ) # conditional loop try( foreach(i=1:10) %:% when(i %% 2 == 0) %dorng% { rnorm(1) } ) @ In this section, we propose a general work around for this kind of loops, that will eventually be incorporated in the \code{\%dorng\%} operator -- when I find out how to mimic its behaviour from the operator itself. \subsection{Nested loops} The idea is to create a sequence of RNG seeds before the outer loop, and use each of them successively to set the RNG in the inner loop -- which is exactly what \code{\%dorng\%} does for simple loops: <>= # doRNG must not be registered registerDoParallel(cl) # generate sequence of seeds of length the number of computations n <- 10; p <- 5 rng <- RNGseq( n * p, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% foreach(j=1:p, r=rng[(i-1)*p + 1:p]) %dopar% { # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 res2 <- foreach(i=1:n) %:% foreach(j=1:p) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(res, res2) ) @ The following is a more complex example with unequal -- but \textbf{known \emph{a priori}} -- numbers of iterations performed in the inner loops: <>= # generate sequence of seeds of length the number of computations n <- 10 rng <- RNGseq( n * (n+1) / 2, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% foreach(j=1:i, r=rng[(i-1)*i/2 + 1:i]) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 res2 <- foreach(i=1:n) %:% foreach(j=1:i) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(res, res2) ) @ \subsection{Conditional loops} The work around used for nested loops applies to conditional loops that use the \code{when()} clause. It ensures that the RNG seed use for a given inner iteration does not depend on the filter, but only on its index in the unconditional-unfolded loop: <>= # un-conditional single loop resAll <- foreach(i=1:n, .options.RNG=1234) %dorng%{ # do your own computation ... c(i, rnorm(1)) } # generate sequence of RNG rng <- RNGseq(n, 1234) # conditional loop: even iterations resEven <- foreach(i=1:n, r=rng) %:% when(i %% 2 == 0) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # conditional loop: odd iterations resOdd <- foreach(i=1:n, r=rng) %:% when(i %% 2 == 1) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # conditional loop: only first 2 and last 2 resFL <- foreach(i=1:n, r=rng) %:% when(i %in% c(1,2,n-1,n)) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # compare results stopifnot( identical(resAll[seq(2,n,by=2)], resEven) ) stopifnot( identical(resAll[seq(1,n,by=2)], resOdd) ) stopifnot( identical(resAll[c(1,2,n-1,n)], resFL) ) @ \subsection{Nested conditional loops} Conditional nested loops may use the same work around, as shown in this intricate example: <>= # generate sequence of seeds of length the number of computations n <- 10 rng <- RNGseq( n * (n+1) / 2, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% when(i %% 2 == 0) %:% foreach(j=1:i, r=rng[(i-1)*i/2 + 1:i]) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 resAll <- foreach(i=1:n) %:% foreach(j=1:i) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(resAll[seq(2,n,by=2)], res) ) @ \section{Performance overhead} The extra setup performed by the \code{\%dorng\%} operator leads to a slight performance over-head, which might be significant for very quick computations, but should not be a problem for realistic computations. The benchmarks below show that a \code{\%dorng\%} loop may take up to two seconds more than the equivalent \code{\%dopar\%} loop, which is not significant in practice, where parallelised computations typically take several minutes. <>= # load rbenchmark library(rbenchmark) # comparison is done on sequential computations registerDoSEQ() rPar <- function(n, s=0){ foreach(i=1:n) %dopar% { Sys.sleep(s) } } rRNG <- function(n, s=0){ foreach(i=1:n) %dorng% { Sys.sleep(s) } } # run benchmark cmp <- benchmark(rPar(10), rRNG(10) , rPar(25), rRNG(25) , rPar(50), rRNG(50) , rPar(50, .01), rRNG(50, .01) , rPar(10, .05), rRNG(10, .05) , replications=5) # order by increasing elapsed time cmp[order(cmp$elapsed), ] @ \section{Known issues} \label{sec:issues} \begin{itemize} \item Nested and/or conditional foreach loops using the operator \code{\%:\%} are not currently not supported (see \cref{sec:nested} for a work around). \item An error is thrown in \code{doRNG} 1.2.6, when the package \code{iterators} was not loaded, when used with \code{foreach} >= 1.4. \item There was a bug in versions prior to \code{1.4}, which caused \code{set.seed} and \code{.options.RNG} not to be equivalent when the current RNG was \code{"L'Ecuyer-CMRG"}. This behaviour can still be reproduced by setting: <>= doRNGversion('1.3') @ To revert to the latest default behaviour: <>= doRNGversion(NULL) @ \end{itemize} \section{News and changes} {\scriptsize \begin{verbatim} <>= cat(paste(readLines(system.file('NEWS', package='doRNG')), collapse="\n")) @ \end{verbatim} } \section*{Cleanup} <>= stopCluster(cl) @ \section*{Session information} \addcontentsline{toc}{section}{Session information} <>= sessionInfo() @ \printbibliography[heading=bibintoc] \end{document} doRNG/inst/doc/doRNG.R0000644000176200001440000002436314360272307014055 0ustar liggesusers## ----pkgmaker_preamble, echo=FALSE, results='asis'---------------------------- pkgmaker::latex_preamble() ## ----bibliofile, echo=FALSE, results='asis'----------------------------------- pkgmaker::latex_bibliography('doRNG') ## ----init, include = FALSE-------------------------------------------------------------- options(width=90) library(pkgmaker) library(knitr) opts_chunk$set(size = "footnotesize") knit_hooks$set(try = pkgmaker::hook_try) ## ----foreach---------------------------------------------------------------------------- # load and register parallel backend for multicore computations library(doParallel) cl <- makeCluster(2) registerDoParallel(cl) # perform 5 tasks in parallel x <- foreach(i=1:5) %dopar% { i + runif(1) } unlist(x) ## ----dopar, tidy=FALSE------------------------------------------------------------------ # with standard %dopar%: foreach loops are not reproducible set.seed(123) res <- foreach(i=1:5) %dopar% { runif(3) } set.seed(123) res2 <- foreach(i=1:5) %dopar% { runif(3) } identical(res, res2) ## ----dorng, tidy=FALSE------------------------------------------------------------------ # load the doRNG package library(doRNG) # using %dorng%: loops _are_ reproducible set.seed(123) res <- foreach(i=1:5) %dorng% { runif(3) } set.seed(123) res2 <- foreach(i=1:5) %dorng% { runif(3) } identical(res, res2) ## ----attr------------------------------------------------------------------------------- attr(res, 'rng') ## ----nextRNGstream---------------------------------------------------------------------- nextRNGStream(c(407L, 1:6)) ## ----options_single--------------------------------------------------------------------- # use a single numeric as a seed s <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } s2 <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } identical(s, s2) ## ----options_single_normalkind---------------------------------------------------------- ## Pass the Normal RNG kind to use within the loop # results are identical if not using the Normal kind in the loop optsN <- list(123, normal.kind="Ahrens") resN.U <- foreach(i=1:5, .options.RNG=optsN) %dorng% { runif(3) } identical(resN.U[1:5], res[1:5]) # Results are different if the Normal kind is used and is not the same resN <- foreach(i=1:5, .options.RNG=123) %dorng% { rnorm(3) } resN1 <- foreach(i=1:5, .options.RNG=optsN) %dorng% { rnorm(3) } resN2 <- foreach(i=1:5, .options.RNG=optsN) %dorng% { rnorm(3) } identical(resN[1:5], resN1[1:5]) identical(resN1[1:5], resN2[1:5]) ## ----options_6length-------------------------------------------------------------------- # use a 6-length numeric s <- foreach(i=1:5, .options.RNG=1:6) %dorng% { runif(3) } attr(s, 'rng')[1:3] ## ----options_7length-------------------------------------------------------------------- # use a 7-length numeric, used as first value for .Random.seed seed <- attr(res, 'rng')[[2]] s <- foreach(i=1:5, .options.RNG=seed) %dorng% { runif(3) } identical(s[1:4], res[2:5]) ## ----options_list----------------------------------------------------------------------- # reproduce previous %dorng% loop s <- foreach(i=1:5, .options.RNG=res) %dorng% { runif(3) } identical(s, res) ## use completely custom sequence of seeds (e.g. using RNG "Marsaglia-Multicarry") # as a matrix seedM <- rbind(rep(401, 5), mapply(rep, 1:5, 2)) seedM sM <- foreach(i=1:5, .options.RNG=seedM) %dorng% { runif(3) } # same seeds passed as a list seedL <- lapply(seq(ncol(seedM)), function(i) seedM[,i]) sL <- foreach(i=1:5, .options.RNG=seedL) %dorng% { runif(3) } identical(sL, sM) ## ----set_seed_diff---------------------------------------------------------------------- # default RNG kind RNGkind('default') def <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } # Marsaglia-Multicarry RNGkind('Marsaglia') mars <- foreach(i=1:5, .options.RNG=123) %dorng% { runif(3) } identical(def, mars) # revert to default RNG kind RNGkind('default') ## ----schedule, tidy=FALSE--------------------------------------------------------------- # define a stochastic task to perform task <- function() c(pid=Sys.getpid(), val=runif(1)) # using the previously registered cluster with 2 workers set.seed(123) res_2workers <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # stop cluster stopCluster(cl) # Sequential computation registerDoSEQ() set.seed(123) res_seq <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # # Using 3 workers # NB: if re-running this vignette you should edit to force using 3 here cl <- makeCluster( if(isManualVignette()) 3 else 2) length(cl) # register new cluster registerDoParallel(cl) set.seed(123) res_3workers <- foreach(i=1:5, .combine=rbind) %dorng% { task() } # task schedule is different pid <- rbind(res1=res_seq[,1], res_2workers[,1], res2=res_3workers[,1]) storage.mode(pid) <- 'integer' pid # results are identical identical(res_seq[,2], res_2workers[,2]) && identical(res_2workers[,2], res_3workers[,2]) ## ----registerDoRNG---------------------------------------------------------------------- set.seed(123) res <- foreach(i=1:5) %dorng% { runif(3) } registerDoRNG(123) res_dopar <- foreach(i=1:5) %dopar% { runif(3) } identical(res_dopar, res) attr(res_dopar, 'rng') ## ----multiple, tidy=FALSE--------------------------------------------------------------- set.seed(456) s1 <- foreach(i=1:5) %dorng% { runif(3) } s2 <- foreach(i=1:5) %dorng% { runif(3) } # the two loops do not use the same streams: different results identical(s1, s2) # but the sequence of loops is reproducible as a whole set.seed(456) r1 <- foreach(i=1:5) %dorng% { runif(3) } r2 <- foreach(i=1:5) %dorng% { runif(3) } identical(r1, s1) && identical(r2, s2) # one can equivalently register the doRNG backend and use %dopar% registerDoRNG(456) r1 <- foreach(i=1:5) %dopar% { runif(3) } r2 <- foreach(i=1:5) %dopar% { runif(3) } identical(r1, s1) && identical(r2, s2) ## ----nested_error, error = TRUE, try = TRUE--------------------------------------------- # nested loop try( foreach(i=1:10) %:% foreach(j=1:i) %dorng% { rnorm(1) } ) # conditional loop try( foreach(i=1:10) %:% when(i %% 2 == 0) %dorng% { rnorm(1) } ) ## ----nested----------------------------------------------------------------------------- # doRNG must not be registered registerDoParallel(cl) # generate sequence of seeds of length the number of computations n <- 10; p <- 5 rng <- RNGseq( n * p, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% foreach(j=1:p, r=rng[(i-1)*p + 1:p]) %dopar% { # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 res2 <- foreach(i=1:n) %:% foreach(j=1:p) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(res, res2) ) ## ----nested_unequal--------------------------------------------------------------------- # generate sequence of seeds of length the number of computations n <- 10 rng <- RNGseq( n * (n+1) / 2, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% foreach(j=1:i, r=rng[(i-1)*i/2 + 1:i]) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 res2 <- foreach(i=1:n) %:% foreach(j=1:i) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(res, res2) ) ## ----conditional------------------------------------------------------------------------ # un-conditional single loop resAll <- foreach(i=1:n, .options.RNG=1234) %dorng%{ # do your own computation ... c(i, rnorm(1)) } # generate sequence of RNG rng <- RNGseq(n, 1234) # conditional loop: even iterations resEven <- foreach(i=1:n, r=rng) %:% when(i %% 2 == 0) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # conditional loop: odd iterations resOdd <- foreach(i=1:n, r=rng) %:% when(i %% 2 == 1) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # conditional loop: only first 2 and last 2 resFL <- foreach(i=1:n, r=rng) %:% when(i %in% c(1,2,n-1,n)) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, rnorm(1)) } # compare results stopifnot( identical(resAll[seq(2,n,by=2)], resEven) ) stopifnot( identical(resAll[seq(1,n,by=2)], resOdd) ) stopifnot( identical(resAll[c(1,2,n-1,n)], resFL) ) ## ----nested_conditional----------------------------------------------------------------- # generate sequence of seeds of length the number of computations n <- 10 rng <- RNGseq( n * (n+1) / 2, 1234) # run standard nested foreach loop res <- foreach(i=1:n) %:% when(i %% 2 == 0) %:% foreach(j=1:i, r=rng[(i-1)*i/2 + 1:i]) %dopar%{ # set RNG seed rngtools::setRNG(r) # do your own computation ... c(i, j, rnorm(1)) } # Compare against the equivalent sequential computations k <- 1 resAll <- foreach(i=1:n) %:% foreach(j=1:i) %do%{ # set seed rngtools::setRNG(rng[[k]]) k <- k + 1 # do your own computation ... c(i, j, rnorm(1)) } stopifnot( identical(resAll[seq(2,n,by=2)], res) ) ## ----perf, cache=TRUE------------------------------------------------------------------- # load rbenchmark library(rbenchmark) # comparison is done on sequential computations registerDoSEQ() rPar <- function(n, s=0){ foreach(i=1:n) %dopar% { Sys.sleep(s) } } rRNG <- function(n, s=0){ foreach(i=1:n) %dorng% { Sys.sleep(s) } } # run benchmark cmp <- benchmark(rPar(10), rRNG(10) , rPar(25), rRNG(25) , rPar(50), rRNG(50) , rPar(50, .01), rRNG(50, .01) , rPar(10, .05), rRNG(10, .05) , replications=5) # order by increasing elapsed time cmp[order(cmp$elapsed), ] ## ----doRNGversion----------------------------------------------------------------------- doRNGversion('1.3') ## ----doRNGversion_revert---------------------------------------------------------------- doRNGversion(NULL) ## ----news, echo=FALSE, results='asis'--------------------------------------------------- cat(paste(readLines(system.file('NEWS', package='doRNG')), collapse="\n")) ## ----stopCluster------------------------------------------------------------------------ stopCluster(cl) ## ----session_info, echo=FALSE, comment=NA----------------------------------------------- sessionInfo()