clue/0000755000175100001440000000000015145307115011222 5ustar hornikusersclue/MD50000644000175100001440000001144715145307115011541 0ustar hornikusers5c829598bf99bb3f073073558c3bb44b *DESCRIPTION 8d6739bf3e0ef6e7f5e5e1f6f6f87989 *NAMESPACE 281c7577f564a5acbecf046c5d1b8e64 *R/AAA.R 3cd212ef14c9566294542d691268fd5a *R/addtree.R 5ca7bd63f1ed85f171358c12fcf08e53 *R/agreement.R 61f26eec5666c409d3a7369f9cc0c99a *R/bag.R cd56914218fa9922aba0f76ff8b94909 *R/boot.R 74c617065ccf4f72df1353534f85da75 *R/classes.R e4c06eac28291bc97fb2e40603f3e23d *R/consensus.R f03ed842ca3417fb555be3254025d412 *R/dissimilarity.R c8a21520e911951d95d7ebd74e113265 *R/ensemble.R f4bbabdccc0b0dc31dbf14373ded5d11 *R/fuzziness.R 5999d867614d17cd53a043cbd99703c9 *R/hierarchy.R d67f188882f5aae752df482d3473fbd0 *R/lattice.R 285f76623c207f44342d7d1ca33d07e8 *R/lsap.R d10944e1875825af11bcea65571432fc *R/margin.R 4fd82d58960235a35f20d2b964149caf *R/medoid.R a3dccf831a311ed068578f817f38161e *R/membership.R 27369e3ebfc5ade758ebb2e49bb213fc *R/objects.R 4b8e8ee574a015903622e264e7560aa8 *R/partition.R 00c4dfcc2d401d810d70f246b7628f6b *R/pava.R 6131a8ffa97003764405701373a3bd48 *R/pclust.R fc4d256afc4ea0c926fe6e288f20ec65 *R/predict.R 44b2e289ec1ed4ea042eccd8816080c5 *R/prototypes.R 5eabc5a234802b0f5a9f4c244ebe9aa9 *R/proximity.R f1a133ffc362372bc24ff24581964b1e *R/registration.R 69049e632bf64e2a11ed5b4f0c276570 *R/sumt.R 8cfa16132f28d693fcd03d396a678deb *R/tabulate.R f415cbecc8d1694bca125998db11a2ae *R/ultrametric.R 6a62f57555cae254f1f7b3ed5d9f152c *R/utilities.R e869958a1b430996c06e7634afa0a1b6 *R/validity.R fb27cb371261c3b9e93cbab42a39ecd4 *build/partial.rdb 98faa10d8120137a32c87af8db73de8a *build/vignette.rds 209c9a8011de691f25197dd11f92eb94 *data/CKME.rda 9d76d93f63bb981fa904df00ebcc2d4d *data/Cassini.rda 9ab68c282368c93036193b8f9e5e36ee *data/GVME.rda ffb945160b3776f1c62502d0a891d2d2 *data/GVME_Consensus.rda 75ced9cb0e6bfe05d3f40a53b83f3603 *data/Kinship82.rda ef36450bbf3c58543ab404cda01fe7ef *data/Kinship82_Consensus.rda f7e3fb8170942579a9d01a417f777df5 *data/Phonemes.rda 2a6241c5a81a77397582d55686ebd255 *inst/CITATION 2b3700f2f68585ec07a963d8fe20535a *inst/REFERENCES.R 4ce2ff29ebfc819444d6c7eb2f09ff6b *inst/doc/clue.R 538ba9739bb7e2f39cc7bdb83e07f2cb *inst/doc/clue.Rnw 2a1083d080dad69cbcc86233a055e96a *inst/doc/clue.pdf fc5c32ebcb85203fa533853fb18d18d3 *inst/po/en@quot/LC_MESSAGES/R-clue.mo 6b382525256a1a059e58ce576eff7106 *man/CKME.Rd 0d61696816866774419c7fda98f05d5f *man/Cassini.Rd d5f5ecf31ab12e806f1e46843a1bd4c2 *man/GVME.Rd 329e0b958ae432ed7362890df0459c8a *man/GVME_Consensus.Rd bc76a451a426ecb5552def66b0ce35ee *man/Kinship82.Rd 43dc7c43ef5bcd36189620f0e96e60c4 *man/Kinship82_Consensus.Rd 2c7c3328fc17af1d5c667c712da49fa4 *man/Phonemes.Rd ad0cb91da513a04216015ce8bda0614f *man/addtree.Rd 90c7457b0989cf97dc6b3b0640beb66f *man/cl_agreement.Rd deb565768acd209e4c15ae06848322d6 *man/cl_bag.Rd 5dca26838651ac5caca862e459b4920f *man/cl_boot.Rd d4081e72f3447131afc6a61d0af7f3d2 *man/cl_classes.Rd 4b3e7e9adcbf80b7234d85c28f510d0b *man/cl_consensus.Rd 4321cb0fc0abe875cfbd3a7547814f5c *man/cl_dissimilarity.Rd 872ecad639c4ade222bba29873cb5465 *man/cl_ensemble.Rd f526b8cc6b9e3e6bc33730c6692e3d08 *man/cl_fuzziness.Rd af83eebbfd3d600999346facaa4308d5 *man/cl_margin.Rd 877d372debd206112f0f808508abab87 *man/cl_medoid.Rd e26070e22290e167ec900cdeea0567ac *man/cl_membership.Rd 2ddf43cfa7b4809e1b211e2f89080d5c *man/cl_object_names.Rd 4dbf99d2cbc40f3e6272fb75357495fb *man/cl_pam.Rd 462f7e7211d13b616ba7e61f1e4ea71a *man/cl_pclust.Rd 1eb04a9edb42f0c3ad50321b36475d6a *man/cl_predict.Rd 9e88e1119f27cc732e7b865471521f1f *man/cl_prototypes.Rd 931b58a667da8aab28dc441fd0c630f7 *man/cl_tabulate.Rd a79724c42916ad2db16343e6539e53b4 *man/cl_ultrametric.Rd 200d833bb5b2e01339f2387ab852f305 *man/cl_validity.Rd ffe8dcd2639eb402c485d2ae30ff7b55 *man/fit_ultrametric_target.Rd 3cbae2b63263993d541d67892e307696 *man/hierarchy.Rd 0484c6cc2e535d4ae73bfa7013b46818 *man/kmedoids.Rd eba40576d17a289e26b3afc7580a2f31 *man/l1_fit_ultrametric.Rd 0afb4a6cf169c38609f9d93b3c513075 *man/lattice.Rd 0411796a2f4bc01b6020362e468067df *man/ls_fit_addtree.Rd f18690356013cfd00c8f15cc35978c34 *man/ls_fit_sum_of_ultrametrics.Rd 0e596f4ba03c8dc72b2cd99a2c4817bd *man/ls_fit_ultrametric.Rd 115623ffe35fcef24928738206942374 *man/n_of_classes.Rd e4822d78d50337d163d881298c234bb1 *man/n_of_objects.Rd da27a64e2cd173b00f81361e10bcab81 *man/partition.Rd 758a1ae34f73da21afc16141789459b3 *man/pclust.Rd 2d6e0b72497d8068ba7455686fbd6ada *man/solve_LSAP.Rd 83a69df58fc8de10b50eb14d1afbee2e *man/sumt.Rd 6985140eb3d61464356b3a4ad86ec71c *po/R-clue.pot c1cb790e0fd0e4d3f38106f10318b585 *src/assignment.c 914912fa18b8403e505ac5d8e1f4ee29 *src/assignment.h e2f17003f4c681661ea31175a99503cf *src/clue.c 8cab4a18877c997d1af59596d2b40f4b *src/clue.h 1d83eaf5af08f3fc312d6dd0363e5c49 *src/init.c 76301856024f2491f73fee44641b5c86 *src/lsap.c 1db06fea8e5ba8856f5def041c22bf54 *src/trees.c 538ba9739bb7e2f39cc7bdb83e07f2cb *vignettes/clue.Rnw 6a62e9eb56e1b243aaae0f5015b2d992 *vignettes/cluster.bib clue/po/0000755000175100001440000000000012213262407011635 5ustar hornikusersclue/po/R-clue.pot0000644000175100001440000001223713142031604013510 0ustar hornikusersmsgid "" msgstr "" "Project-Id-Version: clue 0.3-54\n" "POT-Creation-Date: 2017-08-07 11:31\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "Argument 'weights' must be compatible with 'x'." msgstr "" msgid "Argument 'weights' has negative elements." msgstr "" msgid "Argument 'weights' has no positive elements." msgstr "" msgid "Non-identical weights currently not supported." msgstr "" msgid "All given orders must be valid permutations." msgstr "" msgid "Iterative projection run: %d" msgstr "" msgid "Iterative reduction run: %d" msgstr "" msgid "Cannot coerce to 'cl_addtree'." msgstr "" msgid "Cannot mix partitions and hierarchies." msgstr "" msgid "All clusterings must have the same number of objects." msgstr "" msgid "Can only handle hard partitions." msgstr "" msgid "Can only determine classes of partitions or hierarchies." msgstr "" msgid "Cannot compute consensus of empty ensemble." msgstr "" msgid "AOS run: %d" msgstr "" msgid "Iteration: 0 *** value: %g" msgstr "" msgid "Iteration: %d *** value: %g" msgstr "" msgid "Minimum: %g" msgstr "" msgid "AOG run: %d" msgstr "" msgid "Parameter 'p' must be in [1/2, 1]." msgstr "" msgid "Cannot compute prototype distances." msgstr "" msgid "All elements must have the same number of objects." msgstr "" msgid "Generic '%s' not defined for \"%s\" objects." msgstr "" msgid "Wrong class." msgstr "" msgid "Plotting not available for elements %s of the ensemble." msgstr "" msgid "Value '%s' is not a valid abbreviation for a fuzziness method." msgstr "" msgid "Unary '%s' not defined for \"%s\" objects." msgstr "" msgid "Hierarchies must have the same number of objects." msgstr "" msgid "Dendrograms must have the same number of objects." msgstr "" msgid "Arguments 'x' and 'y' must have the same number of objects." msgstr "" msgid "Cannot compute meet of given clusterings." msgstr "" msgid "Cannot compute join of given clusterings." msgstr "" msgid "Join of given n-trees does not exist." msgstr "" msgid "x must be a matrix with nonnegative entries." msgstr "" msgid "x must not have more rows than columns." msgstr "" msgid "Argument 'x' must be a partition." msgstr "" msgid "Cannot compute medoid of empty ensemble." msgstr "" msgid "Cannot compute medoid partition of empty ensemble." msgstr "" msgid "Class ids:" msgstr "" msgid "Criterion:" msgstr "" msgid "Medoid ids:" msgstr "" msgid "k cannot be less than the number of classes in x." msgstr "" msgid "Cannot extract object dissimilarities" msgstr "" msgid "Cannot infer class ids from given object." msgstr "" msgid "A hard partition of %d objects." msgstr "" msgid "A partition of %d objects." msgstr "" msgid "Partitions must have the same number of objects." msgstr "" msgid "Class ids must be atomic." msgstr "" msgid "Not a valid membership matrix." msgstr "" msgid "Cannot coerce to 'cl_hard_partition'." msgstr "" msgid "No information on exponent in consensus method used." msgstr "" msgid "No information on dissimilarity in consensus method used." msgstr "" msgid "A hard partition of a cluster ensemble with %d elements into %d classes." msgstr "" msgid "A soft partition (degree m = %f) of a cluster ensemble with %d elements into %d classes." msgstr "" msgid "Cannot determine how to modify prototypes." msgstr "" msgid "Invalid function to modify prototypes." msgstr "" msgid "Cannot determine how to subset prototypes." msgstr "" msgid "Invalid function to subset prototypes." msgstr "" msgid "Pclust run: %d" msgstr "" msgid "A hard partition of %d objects into %d classes." msgstr "" msgid "A soft partition (degree m = %f) of %d objects into %d classes." msgstr "" msgid "Cannot make new predictions." msgstr "" msgid "Standardization is currently not supported." msgstr "" msgid "Cannot determine prototypes." msgstr "" msgid "Invalid consensus method '%s'." msgstr "" msgid "Invalid dissimilarity method '%s'." msgstr "" msgid "Invalid agreement method '%s'." msgstr "" msgid "SUMT run: %d" msgstr "" msgid "Iteration: 0 Rho: %g P: %g" msgstr "" msgid "Iteration: %d Rho: %g P: %g" msgstr "" msgid "Not a valid ultrametric." msgstr "" msgid "Given ensemble contains no dissimilarities." msgstr "" msgid "Outer iteration: %d" msgstr "" msgid "Change: u: %g L: %g" msgstr "" msgid "Iteration: %d" msgstr "" msgid "Term: %d" msgstr "" msgid "Change: %g" msgstr "" msgid "Overall change: u: %g L: %g" msgstr "" msgid "An object of virtual class '%s', with representation:" msgstr "" msgid "An ensemble of %d partition of %d objects." msgid_plural "An ensemble of %d partitions of %d objects." msgstr[0] "" msgstr[1] "" msgid "An ensemble of %d dendrogram of %d objects." msgid_plural "An ensemble of %d dendrograms of %d objects." msgstr[0] "" msgstr[1] "" msgid "An ensemble of %d hierarchy of %d objects." msgid_plural "An ensemble of %d hierarchies of %d objects." msgstr[0] "" msgstr[1] "" msgid "An ensemble with %d element." msgid_plural "An ensemble with %d elements." msgstr[0] "" msgstr[1] "" clue/.aspell/0000755000175100001440000000000012462665664012577 5ustar hornikusersclue/.aspell/clue.rds0000644000175100001440000000156312462665506014241 0ustar hornikuserseUz6 S";yu֬nwK)A}sF'$~?'^w_oK`eъF dvzť]q|~|9nBkSb:4QkID匦ѓ-n/QQh哥(!/)1hE&,R6[0,@t* -:,1j~W||RvHC gG=(BZAl yɈтw$ p2f,CZp$noܙb۹fNExwg[:К}d.gip2GpuBQ':׆/&J<RkA[kEG^ ؜.""Np՝ Lɬ (wf1{]7Π;x\Z#0tH`p-(+c0^}62}D*JYXk;iwIΝYYah9PprZ /ʩ8'^ ն ^\clue/.aspell/defaults.R0000644000175100001440000000023113142056061014502 0ustar hornikusersRd_files <- vignettes <- R_files <- description <- list(encoding = "UTF-8", language = "en", dictionaries = c("en_stats", "clue")) clue/R/0000755000175100001440000000000014503541710011421 5ustar hornikusersclue/R/objects.R0000644000175100001440000001310113036461743013200 0ustar hornikusers### * n_of_objects ## Get the number of objects in a clustering. n_of_objects <- function(x) UseMethod("n_of_objects") ### ** Default method. n_of_objects.default <- function(x) length(cl_class_ids(x)) ## (Note that prior to R 2.1.0, kmeans() returned unclassed results, ## hence the best we can do for the *default* method is to look at a ## possibly existing "cluster" component. Using the class ids incurs ## another round of method dispatch, but avoids code duplication.) ### ** Partitioning methods. ## Package stats: kmeans() (R 2.1.0 or better). n_of_objects.kmeans <- n_of_objects.default ## Package cluster: clara(), fanny(), and pam() give objects of the ## respective class inheriting from class "partition". n_of_objects.partition <- n_of_objects.default ## Package cclust: cclust(). n_of_objects.cclust <- n_of_objects.default ## Package e1071: cmeans() gives objects of class "fclust". n_of_objects.fclust <- function(x) nrow(x$membership) ## Package e1071: cshell(). n_of_objects.cshell <- n_of_objects.fclust ## Package e1071: bclust(). n_of_objects.bclust <- n_of_objects.default ## Package mclust: Mclust(). n_of_objects.Mclust <- n_of_objects.default ### ** Hierarchical methods. ## Package stats: hclust(). n_of_objects.hclust <- function(x) length(x$order) ## Package cluster: agnes() and diana() give objects inheriting from ## class "twins". n_of_objects.twins <- n_of_objects.hclust ## Package cluster: mona(). n_of_objects.mona <- n_of_objects.hclust ## Package ape: class "phylo". n_of_objects.phylo <- function(x) length(x$tip.label) ### ** Others. ## Package stats: class "dist". n_of_objects.dist <- function(x) attr(x, "Size") ## Package clue: Ensembles. n_of_objects.cl_ensemble <- function(x) attr(x, "n_of_objects") ## Package clue: Memberships. n_of_objects.cl_membership <- nrow ## Package clue: pclust(). n_of_objects.pclust <- n_of_objects.default ## Package clue: Ultrametrics. n_of_objects.cl_ultrametric <- n_of_objects.dist ## Package clue: (virtual) class "cl_partition". n_of_objects.cl_partition <- function(x) .get_property_from_object_or_representation(x, "n_of_objects") ## Package clue: (virtual) class "cl_hierarchy". n_of_objects.cl_hierarchy <- function(x) .get_property_from_object_or_representation(x, "n_of_objects") ### * cl_object_names ## Determine the names of the objects in a clustering if available; give ## NULL otherwise. This is in sync with e.g. names() or dimnames(); au ## contraire, cl_object_labels() always gives labels even if no names ## are available. cl_object_names <- function(x) UseMethod("cl_object_names") ## ** Default method. cl_object_names.default <- function(x) names(cl_class_ids(x)) ## ** Partitions. ## There is really nothing special we can currently do. ## Most partitioning functions return no information on object names. ## This includes classes ## stats: kmeans ## cba: ccfkms, rock ## cclust: cclust ## e1071: bclust ## flexclust: kcca ## kernlab: specc ## mclust: Mclust ## The algorithms for which things "work" all give named class ids. ## RWeka: Weka_clusterer ## cluster: clara fanny pam ## e1071: cclust cshell ## ** Hierarchies. ## Package stats: hclust(). cl_object_names.hclust <- function(x) x$labels ## Package cluster: agnes(), diana() and mona() all return an object ## which has an 'order.lab' component iff "the original observations ## were labelled". We can use this together the the 'order' component ## to recreate the labels in their original order. Note that we cannot ## rely on dissimilarity or data components being available. cl_object_names.twins <- function(x) { if(!is.null(x$order.lab)) { out <- character(length = n_of_objects(x)) out[x$order] <- x$order.lab out } else NULL } cl_object_names.mona <- cl_object_names.twins ## Package ape: class "phylo". cl_object_names.phylo <- function(x) x$tip.label ## ** Others. ## Package stats: class "dist". ## (Raw object dissimilarities.) cl_object_names.dist <- function(x) attr(x, "Labels") ## Package clue: memberships. cl_object_names.cl_membership <- function(x) rownames(x) ## Package clue: ultrametrics. cl_object_names.cl_ultrametric <- function(x) attr(x, "Labels") ## Package clue: (virtual) class "cl_partition". cl_object_names.cl_partition <- function(x) cl_object_names(.get_representation(x)) ## Package clue: (virtual) class "cl_hierarchy". cl_object_names.cl_hierarchy <- function(x) cl_object_names(.get_representation(x)) ## Package clue: ensembles. cl_object_names.cl_ensemble <- function(x) { nms <- lapply(x, cl_object_names) ind <- which(lengths(nms) > 0L) if(any(ind)) nms[[ind[1L]]] else NULL } ### * cl_object_labels cl_object_labels <- function(x) { if(is.null(out <- cl_object_names(x))) out <- as.character(seq_len(n_of_objects(x))) out } ### * cl_object_dissimilarities ## Extract object dissimilarities from R objects containing such: this ## includes objects directly inheriting from "dist" as well as ## dendrograms or additive trees. cl_object_dissimilarities <- function(x) { ## Keep this in sync with .has_object_dissimilarities(). if(is.cl_dendrogram(x)) cl_ultrametric(x) else if(inherits(x, "dist")) x else stop("Cannot extract object dissimilarities") } .has_object_dissimilarities <- function(x) { ## Keep this in sync with cl_object_dissimilarities(). is.cl_dendrogram(x) || inherits(x, "dist") } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/registration.R0000644000175100001440000003271611754400707014274 0ustar hornikusers### ### At least currently, all registries are meant and used for all types ### of clusterings (for the time being, partitions and hierarchies) ### simultaneously. ### ### * Internal stuff. .make_db_key <- function(name, type) paste(type, name, sep = "_") ### * General-purpose stuff. ### ### This currently insists on a given type: maybe it should simply list ### everything split according to type. But hey, it's internal stuff ### anyway (at least for the time being ...) ### get_methods_from_db <- function(db, type) { type <- match.arg(type, c("partition", "hierarchy")) pattern <- sprintf("^%s_", type) sub(pattern, "", grep(pattern, objects(db), value = TRUE)) } get_method_from_db <- function(db, type, name, msg) { ## ## Keep 'msg' here so that gettext()ing could work ... ## type <- match.arg(type, c("partition", "hierarchy")) db_keys <- objects(db) ind <- pmatch(.make_db_key(tolower(name), type), tolower(db_keys)) if(is.na(ind)) stop(msg, call. = FALSE, domain = NA) db[[db_keys[ind]]] } put_method_into_db <- function(db, type, name, value) { type <- match.arg(type, c("partition", "hierarchy")) db[[.make_db_key(name, type)]] <- value } ### * Consensus Method Registration. cl_consensus_methods_db <- new.env() get_cl_consensus_methods <- function(type) get_methods_from_db(cl_consensus_methods_db, type) get_cl_consensus_method <- function(name, type) { get_method_from_db(cl_consensus_methods_db, type, name, gettextf("Invalid consensus method '%s'.", name)) } set_cl_consensus_method <- function(name, type, definition, ...) { ## Register a @code{type} consensus method called @code{name} with ## definition @code{definition}. Provide more information where ## appropriate, e.g., @code{dissimilarity} d and @code{exponent} e ## for methods minimizing \sum_b d(x_b, x) ^ e. put_method_into_db(cl_consensus_methods_db, type, name, .structure(c(list(definition = definition), list(...)), class = "cl_consensus_method")) } set_cl_consensus_method("DWH", "partition", .cl_consensus_partition_DWH, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("soft/euclidean", "partition", .cl_consensus_partition_soft_euclidean, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("SE", "partition", .cl_consensus_partition_soft_euclidean, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("hard/euclidean", "partition", .cl_consensus_partition_hard_euclidean, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("HE", "partition", .cl_consensus_partition_hard_euclidean, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("soft/manhattan", "partition", .cl_consensus_partition_soft_manhattan, dissimilarity = "manhattan", exponent = 1) set_cl_consensus_method("SM", "partition", .cl_consensus_partition_soft_manhattan, dissimilarity = "manhattan", exponent = 1) set_cl_consensus_method("hard/manhattan", "partition", .cl_consensus_partition_hard_manhattan, dissimilarity = "manhattan", exponent = 1) set_cl_consensus_method("HM", "partition", .cl_consensus_partition_hard_manhattan, dissimilarity = "manhattan", exponent = 1) set_cl_consensus_method("GV1", "partition", .cl_consensus_partition_GV1, dissimilarity = "GV1", exponent = 2) set_cl_consensus_method("GV3", "partition", .cl_consensus_partition_GV3, dissimilarity = "comemberships", exponent = 2) set_cl_consensus_method("soft/symdiff", "partition", .cl_consensus_partition_soft_symdiff, dissimilarity = "symdiff", exponent = 1) set_cl_consensus_method("hard/symdiff", "partition", .cl_consensus_partition_hard_symdiff, dissimilarity = "symdiff", exponent = 1) set_cl_consensus_method("cophenetic", "hierarchy", .cl_consensus_hierarchy_cophenetic, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("euclidean", "hierarchy", .cl_consensus_hierarchy_cophenetic, dissimilarity = "euclidean", exponent = 2) set_cl_consensus_method("manhattan", "hierarchy", .cl_consensus_hierarchy_manhattan, dissimilarity = "manhattan", exponent = 1) set_cl_consensus_method("majority", "hierarchy", .cl_consensus_hierarchy_majority, dissimilarity = "symdiff", exponent = 1) ### * Dissimilarity Method Registration. cl_dissimilarity_methods_db <- new.env() get_cl_dissimilarity_methods <- function(type) get_methods_from_db(cl_dissimilarity_methods_db, type) get_cl_dissimilarity_method <- function(name, type) get_method_from_db(cl_dissimilarity_methods_db, type, name, gettextf("Invalid dissimilarity method '%s'.", name)) set_cl_dissimilarity_method <- function(name, type, definition, description, ...) put_method_into_db(cl_dissimilarity_methods_db, type, name, .structure(c(list(definition = definition, description = description), list(...)), class = "cl_dissimilarity_method")) set_cl_dissimilarity_method("euclidean", "partition", .cl_dissimilarity_partition_euclidean, "minimal Euclidean membership distance") set_cl_dissimilarity_method("manhattan", "partition", .cl_dissimilarity_partition_manhattan, "minimal Manhattan membership distance") set_cl_dissimilarity_method("comemberships", "partition", .cl_dissimilarity_partition_comemberships, "Euclidean comembership distance") set_cl_dissimilarity_method("symdiff", "partition", .cl_dissimilarity_partition_symdiff, "symmetric difference distance") set_cl_dissimilarity_method("Rand", "partition", .cl_dissimilarity_partition_Rand, "Rand distance") set_cl_dissimilarity_method("GV1", "partition", .cl_dissimilarity_partition_GV1, "Gordon-Vichi Delta_1 dissimilarity") set_cl_dissimilarity_method("BA/A", "partition", .cl_dissimilarity_partition_BA_A, "Boorman/Arabie minimum element moves distance") set_cl_dissimilarity_method("BA/C", "partition", .cl_dissimilarity_partition_BA_C, "Boorman/Arabie minimum lattice moves distance") set_cl_dissimilarity_method("BA/D", "partition", .cl_dissimilarity_partition_BA_D, "Boorman/Arabie pair-bonds distance") set_cl_dissimilarity_method("BA/E", "partition", .cl_dissimilarity_partition_BA_E, "Boorman/Arabie normalized information distance") set_cl_dissimilarity_method("VI", "partition", .cl_dissimilarity_partition_VI, "Variation of information") set_cl_dissimilarity_method("Mallows", "partition", .cl_dissimilarity_partition_Mallows, "Mallows dissimilarity") set_cl_dissimilarity_method("CSSD", "partition", .cl_dissimilarity_partition_CSSD, "Cluster Similarity Sensitive Distance") set_cl_dissimilarity_method("euclidean", "hierarchy", .cl_dissimilarity_hierarchy_euclidean, "Euclidean ultrametric distance") set_cl_dissimilarity_method("manhattan", "hierarchy", .cl_dissimilarity_hierarchy_manhattan, "Manhattan ultrametric distance") set_cl_dissimilarity_method("cophenetic", "hierarchy", .cl_dissimilarity_hierarchy_cophenetic, "cophenetic correlations") set_cl_dissimilarity_method("gamma", "hierarchy", .cl_dissimilarity_hierarchy_gamma, "rate of inversions") set_cl_dissimilarity_method("symdiff", "hierarchy", .cl_dissimilarity_hierarchy_symdiff, "symmetric difference distance") set_cl_dissimilarity_method("Chebyshev", "hierarchy", .cl_dissimilarity_hierarchy_Chebyshev, "Chebyshev distance") set_cl_dissimilarity_method("Lyapunov", "hierarchy", .cl_dissimilarity_hierarchy_Lyapunov, "Lyapunov distance") set_cl_dissimilarity_method("BO", "hierarchy", .cl_dissimilarity_hierarchy_BO, "Boorman/Olivier m_delta tree distance") set_cl_dissimilarity_method("spectral", "hierarchy", .cl_dissimilarity_hierarchy_spectral, "spectral ultrametric distance") ### * Agreement Method Registration. cl_agreement_methods_db <- new.env() get_cl_agreement_methods <- function(type) get_methods_from_db(cl_agreement_methods_db, type) get_cl_agreement_method <- function(name, type) get_method_from_db(cl_agreement_methods_db, type, name, gettextf("Invalid agreement method '%s'.", name)) set_cl_agreement_method <- function(name, type, definition, description, ...) put_method_into_db(cl_agreement_methods_db, type, name, .structure(c(list(definition = definition, description = description), list(...)), class = "cl_agreement_method")) set_cl_agreement_method("euclidean", "partition", .cl_agreement_partition_euclidean, "minimal euclidean membership distance") set_cl_agreement_method("manhattan", "partition", .cl_agreement_partition_manhattan, "minimal manhattan membership distance") set_cl_agreement_method("Rand", "partition", .cl_agreement_partition_Rand, "Rand index") set_cl_agreement_method("cRand", "partition", .cl_agreement_partition_cRand, "corrected Rand index") set_cl_agreement_method("NMI", "partition", .cl_agreement_partition_NMI, "normalized mutual information") set_cl_agreement_method("KP", "partition", .cl_agreement_partition_KP, "Katz-Powell index") set_cl_agreement_method("angle", "partition", .cl_agreement_partition_angle, "maximal angle between memberships") set_cl_agreement_method("diag", "partition", .cl_agreement_partition_diag, "maximal co-classification rate") set_cl_agreement_method("FM", "partition", .cl_agreement_partition_FM, "Fowlkes-Mallows index") set_cl_agreement_method("Jaccard", "partition", .cl_agreement_partition_Jaccard, "Jaccard index") set_cl_agreement_method("purity", "partition", .cl_agreement_partition_purity, "purity") set_cl_agreement_method("PS", "partition", .cl_agreement_partition_PS, "Prediction Strength") set_cl_agreement_method("euclidean", "hierarchy", .cl_agreement_hierarchy_euclidean, "euclidean ultrametric distance") set_cl_agreement_method("manhattan", "hierarchy", .cl_agreement_hierarchy_manhattan, "manhattan ultrametric distance") set_cl_agreement_method("cophenetic", "hierarchy", .cl_agreement_hierarchy_cophenetic, "cophenetic correlations") set_cl_agreement_method("angle", "hierarchy", .cl_agreement_hierarchy_angle, "angle between ultrametrics") set_cl_agreement_method("gamma", "hierarchy", .cl_agreement_hierarchy_gamma, "rate of inversions") clue/R/addtree.R0000644000175100001440000003111014144530664013157 0ustar hornikusers### * ls_fit_addtree ls_fit_addtree <- function(x, method = c("SUMT", "IP", "IR"), weights = 1, control = list()) { if(!inherits(x, "dist")) x <- as.dist(x) ## Catch some special cases right away. if(attr(x, "Size") <= 3L) return(as.cl_addtree(x)) if(.non_additivity(x, max = TRUE) == 0) return(as.cl_addtree(x)) ## Handle argument 'weights'. ## This is somewhat tricky ... if(is.matrix(weights)) { weights <- as.dist(weights) if(length(weights) != length(x)) stop("Argument 'weights' must be compatible with 'x'.") } else weights <- rep_len(weights, length(x)) if(any(weights < 0)) stop("Argument 'weights' has negative elements.") if(!any(weights > 0)) stop("Argument 'weights' has no positive elements.") method <- match.arg(method) switch(method, SUMT = .ls_fit_addtree_by_SUMT(x, weights, control), IP = { .ls_fit_addtree_by_iterative_projection(x, weights, control) }, IR = { .ls_fit_addtree_by_iterative_reduction(x, weights, control) }) } ### ** .ls_fit_addtree_by_SUMT .ls_fit_addtree_by_SUMT <- function(x, weights = 1, control = list()) { ## Control parameters: ## gradient, gradient <- control$gradient if(is.null(gradient)) gradient <- TRUE ## nruns, nruns <- control$nruns ## start, start <- control$start ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } w <- weights / sum(weights) n <- attr(x, "Size") labels <- attr(x, "Labels") ## Handle missing values in x along the lines of de Soete (1984): ## set the corresponding weights to 0, and impute by the weighted ## mean. ind <- which(is.na(x)) if(any(ind)) { w[ind] <- 0 x[ind] <- weighted.mean(x, w, na.rm = TRUE) } L <- function(d) sum(w * (d - x) ^ 2) P <- .make_penalty_function_addtree(n) if(gradient) { grad_L <- function(d) 2 * w * (d - x) grad_P <- .make_penalty_gradient_addtree(n) } else { grad_L <- grad_P <- NULL } if(is.null(start)) { ## Initialize by "random shaking". Use sd() for simplicity. start <- replicate(nruns, x + rnorm(length(x), sd = sd(x) / sqrt(3)), simplify = FALSE) } ## And now ... d <- sumt(start, L, P, grad_L, grad_P, method = control$method, eps = control$eps, q = control$q, verbose = control$verbose, control = as.list(control$control))$x ## Round to enforce additivity, and hope for the best ... .cl_addtree_from_addtree_approximation(d, n, labels) } .make_penalty_function_addtree <- function(n) function(d) { (.non_additivity(.symmetric_matrix_from_veclh(d, n)) + sum(pmin(d, 0) ^ 2)) } .make_penalty_gradient_addtree <- function(n) function(d) { gr <- matrix(.C(C_deviation_from_additivity_gradient, as.double(.symmetric_matrix_from_veclh(d, n)), as.integer(n), gr = double(n * n))$gr, n, n) gr[row(gr) > col(gr)] + 2 * sum(pmin(d, 0)) } ### ** .ls_fit_addtree_by_iterative_projection ## ## Functions ## .ls_fit_addtree_by_iterative_projection() ## .ls_fit_addtree_by_iterative_reduction() ## are really identical apart from the name of the C routine they call. ## (But will this necessarily always be the case in the future?) ## Merge maybe ... ## .ls_fit_addtree_by_iterative_projection <- function(x, weights = 1, control = list()) { if(any(diff(weights))) warning("Non-identical weights currently not supported.") labels <- attr(x, "Labels") x <- as.matrix(x) n <- nrow(x) ## Control parameters: ## maxiter, maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 10000L ## nruns, nruns <- control$nruns ## order, order <- control$order ## tol, tol <- control$tol if(is.null(tol)) tol <- 1e-8 ## verbose. verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Handle order and nruns. if(!is.null(order)) { if(!is.list(order)) order <- as.list(order) if(!all(vapply(order, function(o) all(sort(o) == seq_len(n)), NA))) stop("All given orders must be valid permutations.") } else { if(is.null(nruns)) nruns <- 1L order <- replicate(nruns, sample(n), simplify = FALSE) } ind <- lower.tri(x) L <- function(d) sum(weights * (x - d)[ind] ^ 2) d_opt <- NULL v_opt <- Inf for(run in seq_along(order)) { if(verbose) message(gettextf("Iterative projection run: %d", run)) d <- .C(C_ls_fit_addtree_by_iterative_projection, as.double(x), as.integer(n), as.integer(order[[run]] - 1L), as.integer(maxiter), iter = integer(1L), as.double(tol), as.logical(verbose))[[1L]] v <- L(d) if(v < v_opt) { v_opt <- v d_opt <- d } } d <- matrix(d_opt, n) dimnames(d) <- list(labels, labels) .cl_addtree_from_addtree_approximation(as.dist(d)) } ### ** .ls_fit_addtree_by_iterative_reduction .ls_fit_addtree_by_iterative_reduction <- function(x, weights = 1, control = list()) { if(any(diff(weights))) warning("Non-identical weights currently not supported.") labels <- attr(x, "Labels") x <- as.matrix(x) n <- nrow(x) ## Control parameters: ## maxiter, maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 10000L ## nruns, nruns <- control$nruns ## order, order <- control$order ## tol, tol <- control$tol if(is.null(tol)) tol <- 1e-8 ## verbose. verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Handle order and nruns. if(!is.null(order)) { if(!is.list(order)) order <- as.list(order) if(!all(vapply(order, function(o) all(sort(o) == seq_len(n)), NA))) stop("All given orders must be valid permutations.") } else { if(is.null(nruns)) nruns <- 1L order <- replicate(nruns, sample(n), simplify = FALSE) } ind <- lower.tri(x) L <- function(d) sum(weights * (x - d)[ind] ^ 2) d_opt <- NULL v_opt <- Inf for(run in seq_along(order)) { if(verbose) message(gettextf("Iterative reduction run: %d", run)) d <- .C(C_ls_fit_addtree_by_iterative_reduction, as.double(x), as.integer(n), as.integer(order[[run]] - 1L), as.integer(maxiter), iter = integer(1L), as.double(tol), as.logical(verbose))[[1L]] v <- L(d) if(v < v_opt) { v_opt <- v d_opt <- d } } d <- matrix(d_opt, n) dimnames(d) <- list(labels, labels) .cl_addtree_from_addtree_approximation(as.dist(d)) } ### * .non_additivity .non_additivity <- function(x, max = FALSE) { if(!is.matrix(x)) x <- .symmetric_matrix_from_veclh(x) .C(C_deviation_from_additivity, as.double(x), as.integer(nrow(x)), fn = double(1L), as.logical(max))$fn } ### * ls_fit_centroid ls_fit_centroid <- function(x) { ## Fit a centroid additive tree distance along the lines of Carroll ## & Pruzansky (1980). In fact, solving ## ## \sum_{i,j: i \ne j} (\delta_{ij} - (g_i + g_j)) ^ 2 => min_g ## ## gives \sum_{j: j \ne i} (g_i + g_j - \delta_{ij}) = 0, or (also ## in Barthemely & Guenoche) ## ## (n - 2) g_i + \sum_j g_j = \sum_{j: j \ne i} \delta_{ij} ## ## which after summing over all i and some manipulations eventually ## gives ## ## g_i = \frac{1}{n-2} (v_i - m), ## ## v_i = \sum_{j: j \ne i} \delta_{ij} ## s = \frac{1}{2(n-1)} \sum_{i,j: j \ne i} \delta_{ij} n <- attr(x, "Size") if(n <= 2L) return(as.cl_addtree(0 * x)) x <- as.matrix(x) g <- rowSums(x) / (n - 2) - sum(x) / (2 * (n - 1) * (n - 2)) as.cl_addtree(as.dist(.make_centroid_matrix(g))) } .make_centroid_matrix <- function(g) { y <- outer(g, g, `+`) diag(y) <- 0 y } ### * as.cl_addtree as.cl_addtree <- function(x) UseMethod("as.cl_addtree") as.cl_addtree.default <- function(x) { if(inherits(x, "cl_addtree")) x else if(is.atomic(x) || inherits(x, "cl_ultrametric")) .cl_addtree_from_veclh(x) else if(is.matrix(x)) { ## Should actually check whether the matrix is symmetric, >= 0 ## and satisfies the 4-point conditions ... .cl_addtree_from_veclh(as.dist(x)) } else if(is.cl_dendrogram(x)) .cl_addtree_from_veclh(cl_ultrametric(x)) else stop("Cannot coerce to 'cl_addtree'.") } as.cl_addtree.phylo <- function(x) .cl_addtree_from_veclh(as.dist(cophenetic(x))) ## Phylogenetic trees with edge/branch lengths yield additive tree ## dissimilarities. ### * .cl_addtree_from_veclh .cl_addtree_from_veclh <- function(x, size = NULL, labels = NULL) { cl_proximity(x, "Additive tree distances", labels = labels, size = size, class = c("cl_addtree", "cl_dissimilarity", "cl_proximity", "dist")) } ### * .cl_addtree_from_addtree_approximation .cl_addtree_from_addtree_approximation <- function(x, size = NULL, labels = NULL) { ## Turn x into an addtree after possibly rounding to non-additivity ## significance (note that this is not guaranteed to work ...). mnum <- .non_additivity(x, max = TRUE) x <- round(x, floor(abs(log10(mnum)))) .cl_addtree_from_veclh(x, size = size, labels = labels) } ### * .decompose_addtree .decompose_addtree <- function(x, const = NULL) { ## Decompose an addtree into an ultrametric and a centroid ## distance. ## If 'const' is not given, we take the root as half way between the ## diameter of the addtree, and choose a minimal constant to ensure ## non-negativity (but not positivity) of the ultrametric. ## As this is all slightly dubious and it is not quite clear how ## much positivity we want in the ultrametric of the decomposition, ## we keep this hidden. For plotting addtrees, the choice of the ## constant does not seem to matter. x <- as.matrix(x) n <- nrow(x) ## Determine diameter. ind <- which.max(x) - 1 u <- ind %% n + 1 v <- ind %/% n + 1 if(!is.null(const)) g <- pmax(x[u, ], x[v, ]) - const else { g <- pmax(x[u, ], x[v, ]) - x[u, v] / 2 u <- x - .make_centroid_matrix(g) k <- - min(u) g <- g - k / 2 } u <- x - .make_centroid_matrix(g) names(g) <- rownames(x) ## Ensure a valid ultrametric. d <- .ultrametrify(as.dist(u)) u <- .cl_ultrametric_from_veclh(d, nrow(x), rownames(x)) ## Note that we return the centroid distances to the root, and not ## between the objects (as.dist(.make_centroid_matrix(g))) ... list(Ultrametric = as.cl_ultrametric(u), Centroid = g) } ### * plot.cl_addtree plot.cl_addtree <- function(x, ...) { ## Construct a dendrogram-style representation of the addtree with ## the root half way between the diameter, and plot. y <- .decompose_addtree(x, max(x)) u <- y$Ultrametric g <- y$Centroid ## We halve the scale of the ultrametric, and add the maximal g from ## the centroid. h <- hclust(as.dist(u / 2), "single") h$height <- h$height + max(g) d <- as.dendrogram(h) ## Now modify the heights of the leaves so that the objects giving ## the diameter of the addtree end up with height zero. g <- max(g) - g names(g) <- labels(g) d <- dendrapply(d, function(n) { if(!is.leaf(n)) return(n) attr(n, "height") <- g[attr(n, "label")] n }) ## And finally plot plot(d, ...) } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/boot.R0000644000175100001440000000255711304023136012511 0ustar hornikuserscl_boot <- function(x, B, k = NULL, algorithm = if(is.null(k)) "hclust" else "kmeans", parameters = list(), resample = FALSE) { clusterings <- if(!resample) { x <- rep.int(list(x), B) eval(as.call(c(list(as.name("lapply"), x, algorithm), if(!is.null(k)) list(k), parameters))) } else { replicate(B, expr = { algorithm <- match.fun(algorithm) ## ## This is not quite perfect. We have ## cl_predict() to encapsulate the process of ## assigning objects to classes, but for sampling ## from the objects we assume that they correspond ## to the *rows* of 'x'. Argh. ## ind <- sample(NROW(x), replace = TRUE) train <- if(length(dim(x)) == 2) x[ind, ] else x[ind] out <- eval(as.call(c(list(algorithm, train), if(!is.null(k)) list(k), parameters))) as.cl_partition(cl_predict(out, x, "memberships")) }, simplify = FALSE) } cl_ensemble(list = clusterings) } clue/R/bag.R0000644000175100001440000000276711304023136012302 0ustar hornikuserscl_bag <- function(x, B, k = NULL, algorithm = "kmeans", parameters = NULL, method = "DFBC1", control = NULL) { ## Currently, method 'DFBC1' (Dudoit-Fridlyand BagClust1) is the ## only one available, and argument 'control' is ignored. ## Construct reference partition. algorithm <- match.fun(algorithm) reference <- eval(as.call(c(list(algorithm, x), if(!is.null(k)) list(k), parameters))) ## Construct bootstrap ensemble. clusterings <- cl_boot(x, B, k, algorithm, parameters, resample = TRUE) ## Construct Dudoit-Fridlyand BagClust1 consensus partitions, ## suitably generalized ... ## ## In principle, this could be turned into a "constructive" method ## for cl_consensus(), also allowing for weights (straightforward). ## E.g., ## .cl_consensus_partition_DFBC1(clusterings, weights, control) ## where either 'control specifies a reference partition, or the ## first element of 'clusterings' is taken as such. ## k <- max(sapply(c(clusterings, reference), n_of_classes)) M_ref <- cl_membership(reference, k) M <- matrix(0, NROW(M_ref), k) for(b in seq_len(B)) { mem <- cl_membership(clusterings[[b]], k) ## Match classes to reference partition. ind <- solve_LSAP(crossprod(M_ref, mem), maximum = TRUE) M <- M + mem[, ind] } as.cl_partition(cl_membership(as.cl_membership(M / B), k)) } clue/R/ultrametric.R0000644000175100001440000006365313435044702014116 0ustar hornikusers### * cl_ultrametric cl_ultrametric <- function(x, size = NULL, labels = NULL) { if(inherits(x, "cl_hierarchy")) { ## ## Strictly, not every hierarchy corresponds to an ultrametric. ## return(cl_ultrametric(.get_representation(x), size = size, labels = labels)) } else if(!inherits(x, "cl_ultrametric")) { ## Try using cophenetic(). ## This starts by coercing to hclust, which has methods for all ## currently supported hierarchical classification methods. ## To support others, either provide as.hclust methods for ## these, or make cl_ultrametric() generic and add methods. ## Or use the fact that in R >= 2.1.0, stats::cophenetic() is ## generic. out <- cophenetic(x) } else { out <- x if(is.null(labels)) labels <- attr(x, "Labels") } .cl_ultrametric_from_veclh(out, labels = labels, size = size) } .cl_ultrametric_from_veclh <- function(x, size = NULL, labels = NULL, meta = NULL) { if(.non_ultrametricity(x) > 0) stop("Not a valid ultrametric.") u <- cl_proximity(x, "Ultrametric distances", labels = labels, size = size, class = c("cl_ultrametric", "cl_dissimilarity", "cl_proximity", "dist")) if(!is.null(meta)) attr(u, "meta") <- meta u } ### * as.cl_ultrametric as.cl_ultrametric <- function(x) UseMethod("as.cl_ultrametric") as.cl_ultrametric.default <- function(x) { if(inherits(x, "cl_ultrametric")) x else if(is.atomic(x)) .cl_ultrametric_from_veclh(x) else cl_ultrametric(x) } as.cl_ultrametric.matrix <- function(x) .cl_ultrametric_from_veclh(x[row(x) > col(x)], labels = rownames(x)) ### * as.dendrogram.cl_ultrametric as.dendrogram.cl_ultrametric <- function(object, ...) as.dendrogram(as.hclust(object), ...) ### * as.hclust.cl_ultrametric as.hclust.cl_ultrametric <- function(x, ...) { ## Hierarchical clustering with single linkage gives the minimal ## ultrametric dominated by a dissimilarity, see e.g. Bock (1974, ## Theorem 39.2). Hence, hclust(method = "single") on an ## ultrametric gives the hclust representation of the associated ## dendrogram. hclust(x, "single") } ### * cophenetic.cl_ultrametric cophenetic.cl_ultrametric <- function(x) as.dist(x) ### * plot.cl_ultrametric plot.cl_ultrametric <- function(x, ...) plot(as.dendrogram(x), ...) ### * ls_fit_ultrametric ls_fit_ultrametric <- function(x, method = c("SUMT", "IP", "IR"), weights = 1, control = list()) { if(inherits(x, "cl_ultrametric")) { return(.cl_ultrametric_with_meta_added(x, list(objval = 0))) } else if(is.cl_ensemble(x) || is.list(x)) { ## Might be given a list/ensemble of object dissimilarities. ## In this case, compute the suitably weighted average and ## proceed. if(length(x) == 0L) stop("Given ensemble contains no dissimilarities.") ## Let's be nice as usual ... ind <- !vapply(x, .has_object_dissimilarities, NA) if(any(ind)) x[ind] <- lapply(x[ind], as.dist) x <- .weighted_mean_of_object_dissimilarities(x, control$weights) } else if(!inherits(x, "dist")) x <- as.dist(x) ## Catch some special cases right away. if(attr(x, "Size") <= 2L) return(.cl_ultrametric_with_meta_added(as.cl_ultrametric(x), list(objval = 0))) if(.non_ultrametricity(x, max = TRUE) == 0) return(.cl_ultrametric_with_meta_added(as.cl_ultrametric(x), list(objval = 0))) ## Handle weights. ## This is somewhat tricky ... if(is.matrix(weights)) { weights <- as.dist(weights) if(length(weights) != length(x)) stop("Argument 'weights' must be compatible with 'x'.") } else weights <- rep_len(weights, length(x)) if(any(weights < 0)) stop("Argument 'weights' has negative elements.") if(!any(weights > 0)) stop("Argument 'weights' has no positive elements.") method <- match.arg(method) switch(method, SUMT = .ls_fit_ultrametric_by_SUMT(x, weights, control), IP = { .ls_fit_ultrametric_by_iterative_projection(x, weights, control) }, IR = { .ls_fit_ultrametric_by_iterative_reduction(x, weights, control) }) } ### ** .ls_fit_ultrametric_by_SUMT .ls_fit_ultrametric_by_SUMT <- function(x, weights = 1, control = list()) { ## Fit an ultrametric to a dissimilarity by minimizing euclidean ## dissimilarity subject to the ultrametric constraint, using the ## sequential algorithm of de Soete (1984) with a slight change: we ## try to ensure that what we obtain satisfies the constraints ## "exactly" rather than approximately. We (currently?) do that via ## rounding ... ## ## This fits and hence returns an ultrametric, *not* the hierarchy ## corresponding to the ultrametric. ## w <- weights / sum(weights) ## Control parameters: ## nruns, nruns <- control$nruns ## start. start <- control$start ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } ## If x is an ultrametric, or satisfies the ultrametricity ## constraints, return it. if(inherits(x, "cl_ultrametric") || (.non_ultrametricity(x, max = TRUE) == 0)) return(.cl_ultrametric_with_meta_added(as.cl_ultrametric(x), list(objval = 0))) ## For the time being, use a simple minimizer. n <- attr(x, "Size") labels <- attr(x, "Labels") ## Handle missing values in x along the lines of de Soete (1984): ## set the corresponding weights to 0, and impute by the weighted ## mean. ind <- which(is.na(x)) if(any(ind)) { w[ind] <- 0 x[ind] <- weighted.mean(x, w, na.rm = TRUE) } ## We follow de Soete's notation, and use the veclh's (vector of ## lower half, in S the same as x[lower.tri(x)]) of the respective ## proximity objects. L <- function(d) sum(w * (x - d) ^ 2) P <- .make_penalty_function_ultrametric(n) grad_L <- function(d) 2 * w * (d - x) grad_P <- .make_penalty_gradient_ultrametric(n) if(is.null(start)) { ## Initialize by "random shaking". Use sd() for simplicity. start <- replicate(nruns, x + rnorm(length(x), sd = sd(x) / sqrt(3)), simplify = FALSE) } ## And now ... out <- sumt(start, L, P, grad_L, grad_P, method = control$method, eps = control$eps, q = control$q, verbose = control$verbose, control = as.list(control$control)) d <- .ultrametrify(out$x) meta <- list(objval = L(d)) .cl_ultrametric_from_veclh(d, n, labels, meta) } .make_penalty_function_ultrametric <- function(n) function(d) { ## Smooth penalty function measuring the extent of violation of ## the ultrametricity constraint. Also ensure nonnegativity ... (.non_ultrametricity(.symmetric_matrix_from_veclh(d, n)) + sum(pmin(d, 0) ^ 2)) } .make_penalty_gradient_ultrametric <- function(n) function(d) { gr <- matrix(.C(C_deviation_from_ultrametricity_gradient, as.double(.symmetric_matrix_from_veclh(d, n)), as.integer(n), gr = double(n * n))$gr, n, n) gr[row(gr) > col(gr)] + 2 * sum(pmin(d, 0)) } ### ** .ls_fit_ultrametric_by_iterative_projection ## ## Functions ## .ls_fit_ultrametric_by_iterative_projection() ## .ls_fit_ultrametric_by_iterative_reduction() ## are really identical apart from the name of the C routine they call. ## (But will this necessarily always be the case in the future?) ## Merge maybe ... ## .ls_fit_ultrametric_by_iterative_projection <- function(x, weights = 1, control = list()) { if(any(diff(weights) != 0)) warning("Non-identical weights currently not supported.") labels <- attr(x, "Labels") n <- attr(x, "Size") x <- as.matrix(x) ## Control parameters: ## maxiter, maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 10000L ## nruns, nruns <- control$nruns ## order, order <- control$order ## tol, tol <- control$tol if(is.null(tol)) tol <- 1e-8 ## verbose. verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Handle order and nruns. if(!is.null(order)) { if(!is.list(order)) order <- as.list(order) if(!all(vapply(order, function(o) all(sort(o) == seq_len(n)), NA))) stop("All given orders must be valid permutations.") } else { if(is.null(nruns)) nruns <- 1L order <- replicate(nruns, sample(n), simplify = FALSE) } ## ## Adjust in case support for non-identical weights is added. L <- function(d) sum((x - d) ^ 2) ## d_opt <- NULL v_opt <- Inf for(run in seq_along(order)) { if(verbose) message(gettextf("Iterative projection run: %d", run)) d <- .C(C_ls_fit_ultrametric_by_iterative_projection, as.double(x), as.integer(n), as.integer(order[[run]] - 1L), as.integer(maxiter), iter = integer(1L), as.double(tol), as.logical(verbose))[[1L]] v <- L(d) if(v < v_opt) { v_opt <- v d_opt <- d } } d <- .ultrametrify(as.dist(matrix(d_opt, n))) meta <- list(objval = L(d)) .cl_ultrametric_from_veclh(d, n, labels, meta) } ### ** .ls_fit_ultrametric_by_iterative_reduction .ls_fit_ultrametric_by_iterative_reduction <- function(x, weights = 1, control = list()) { if(any(diff(weights) != 0)) warning("Non-identical weights currently not supported.") labels <- attr(x, "Labels") n <- attr(x, "Size") x <- as.matrix(x) ## Control parameters: ## maxiter, maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 10000L ## nruns, nruns <- control$nruns ## order, order <- control$order ## tol, tol <- control$tol if(is.null(tol)) tol <- 1e-8 ## verbose. verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Handle order and nruns. if(!is.null(order)) { if(!is.list(order)) order <- as.list(order) if(!all(vapply(order, function(o) all(sort(o) == seq_len(n)), NA))) stop("All given orders must be valid permutations.") } else { if(is.null(nruns)) nruns <- 1L order <- replicate(nruns, sample(n), simplify = FALSE) } ## ## Adjust in case support for non-identical weights is added. L <- function(d) sum((x - d) ^ 2) ## d_opt <- NULL v_opt <- Inf for(run in seq_along(order)) { if(verbose) message(gettextf("Iterative reduction run: %d", run)) d <- .C(C_ls_fit_ultrametric_by_iterative_reduction, as.double(x), as.integer(n), as.integer(order[[run]] - 1L), as.integer(maxiter), iter = integer(1L), as.double(tol), as.logical(verbose))[[1L]] v <- L(d) if(v < v_opt) { v_opt <- v d_opt <- d } } d <- .ultrametrify(as.dist(matrix(d_opt, n))) meta <- list(objval = L(d)) .cl_ultrametric_from_veclh(d, n, labels, meta) } ### * Ultrametric Target Fitters. ### ** ls_fit_ultrametric_target ls_fit_ultrametric_target <- function(x, y, weights = 1) { fitter <- if(identical(weights, 1)) # Default. function(x, w) mean(x) else function(x, w) weighted.mean(x, w) distfun <- function(x, u, w) sqrt(sum(w * (x - u) ^ 2)) .fit_ultrametric_target(x, y, weights, fitter, distfun) } ### ** l1_fit_ultrametric_target l1_fit_ultrametric_target <- function(x, y, weights = 1) { fitter <- if(identical(weights, 1)) # Default. function(x, w) median(x) else function(x, w) weighted_median(x, w) distfun <- function(x, u, w) sum(w * abs(x - u)) .fit_ultrametric_target(x, y, weights, fitter, distfun) } ### ** .fit_ultrametric_target .fit_ultrametric_target <- function(x, y, w, fitter, distfun = NULL) { w <- .handle_weights_for_ultrametric_target_fitters(w, x) ## The documentation says that x should inherit from dist, so coerce ## to this if needed but if not a matrix (as we will coerce back to ## a matrix right away). if(!inherits(x, "dist") && !is.matrix(x)) x <- as.dist(x) x <- as.matrix(x) y <- as.hclust(y) n <- length(y$order) ilist <- vector("list", n) out <- matrix(0, n, n) mat <- xlist <- wlist <- vector("list", n - 1L) for(i in seq_len(n - 1L)) { inds <- y$merge[i, ] ids1 <- if(inds[1L] < 0) -inds[1L] else ilist[[inds[1L]]] ids2 <- if(inds[2L] < 0) -inds[2L] else ilist[[inds[2L]]] ilist[[i]] <- c(ids1, ids2) mat[[i]] <- cbind(rep.int(ids1, rep.int(length(ids2), length(ids1))), rep.int(ids2, length(ids1))) xlist[[i]] <- x[mat[[i]]] wlist[[i]] <- w[mat[[i]]] } values <- pava(xlist, wlist, fitter) for(i in seq_len(n - 1L)) out[mat[[i]]] <- values[i] rownames(out) <- y$labels u <- as.cl_ultrametric(out + t(out)) if(!is.null(distfun)) attr(u, "meta") <- list(objval = distfun(as.dist(x), u, as.dist(w))) u } ### ** .handle_weights_for_ultrametric_target_fitters .handle_weights_for_ultrametric_target_fitters <- function(weights, x) { ## Handle weights for the ultrametric target fitters. ## This is somewhat tricky ... if(is.matrix(weights)) { if(any(dim(weights) != attr(x, "Size"))) stop("Argument 'weights' must be compatible with 'x'.") } else weights <- as.matrix(.dist_from_vector(rep_len(weights, length(x)))) if(any(weights < 0)) stop("Argument 'weights' has negative elements.") if(!any(weights > 0)) stop("Argument 'weights' has no positive elements.") weights } ### l1_fit_ultrametric l1_fit_ultrametric <- function(x, method = c("SUMT", "IRIP"), weights = 1, control = list()) { if(inherits(x, "cl_ultrametric")) return(.cl_ultrametric_with_meta_added(x, list(objval = 0))) if(!inherits(x, "dist")) x <- as.dist(x) ## Catch some special cases right away. if(attr(x, "Size") <= 2L) return(.cl_ultrametric_with_meta_added(as.cl_ultrametric(x), list(objval = 0))) if(.non_ultrametricity(x, max = TRUE) == 0) return(.cl_ultrametric_with_meta_added(as.cl_ultrametric(x), list(objval = 0))) ## Handle weights. ## This is somewhat tricky ... if(is.matrix(weights)) { weights <- as.dist(weights) if(length(weights) != length(x)) stop("Argument 'weights' must be compatible with 'x'.") } else weights <- rep_len(weights, length(x)) if(any(weights < 0)) stop("Argument 'weights' has negative elements.") if(!any(weights > 0)) stop("Argument 'weights' has no positive elements.") method <- match.arg(method) switch(method, SUMT = .l1_fit_ultrametric_by_SUMT(x, weights, control), IRIP = .l1_fit_ultrametric_by_IRIP(x, weights, control)) } ### ** .l1_fit_ultrametric_by_SUMT .l1_fit_ultrametric_by_SUMT <- function(x, weights = 1, control = list()) { ## Try a SUMT with "pseudo-gradients". w <- weights / sum(weights) ## Control parameters: ## gradient, gradient <- control$gradient if(is.null(gradient)) gradient <- TRUE ## nruns, nruns <- control$nruns ## start. start <- control$start ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } ## For the time being, use a simple minimizer. n <- attr(x, "Size") labels <- attr(x, "Labels") L <- function(d) sum(w * abs(d - x)) P <- .make_penalty_function_ultrametric(n) if(gradient) { grad_L <- function(d) w * sign(d - x) grad_P <- .make_penalty_gradient_ultrametric(n) } else grad_L <- grad_P <- NULL if(is.null(start)) { ## Initialize by "random shaking". Use sd() for simplicity. start <- replicate(nruns, x + rnorm(length(x), sd = sd(x) / sqrt(3)), simplify = FALSE) } ## And now ... out <- sumt(start, L, P, grad_L, grad_P, method = control$method, eps = control$eps, q = control$q, verbose = control$verbose, control = as.list(control$control)) d <- .ultrametrify(out$x) meta <- list(objval = L(d)) .cl_ultrametric_from_veclh(d, n, labels, meta) } ### ** .l1_fit_ultrametric_by_IRIP .l1_fit_ultrametric_by_IRIP <- function(x, weights = 1, control = list()) { ## An attempt of implementing "Iteratively Reweighted Iterative ## Projection" as described in Smith (2000, 2001), Journal of ## Classification. Note that this suggests using the Iterative ## Projection of Hubert and Arabie (1995), which we cannot as we ## have not (yet?) implemented this for the weighted case. Hence, ## we use our SUMT least squares ultrametric fitter instead. ## ## However, we never got this to converge properly ... w <- weights / sum(weights) ## Control parameters: ## MIN, MIN <- control$MIN if(is.null(MIN)) MIN <- 1e-3 ## (A rather small cut-off which worked best in the cases we tried.) ## eps, eps <- control$eps if(is.null(eps)) eps <- 1e-6 ## maxiter, maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 100L ## reltol, reltol <- control$reltol if(is.null(reltol)) reltol <- 1e-6 ## start, start <- control$start ## verbose. verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") n <- attr(x, "Size") labels <- attr(x, "Labels") L <- function(d) sum(w * abs(x - d)) ## Initialize by "random shaking" as for the L2 SUMT, but perhaps we ## should not do this? [Or do it differently?] u <- if(is.null(start)) x + rnorm(length(x), sd = sd(x) / 3) else start ## (No multiple runs for the time being.) L_new <- L(u) iter <- 1L while(iter <= maxiter) { if(verbose) message(gettextf("Outer iteration: %d", iter)) L_old <- L_new u_old <- u weights <- w / pmax(abs(u - x), MIN) u <- .ls_fit_ultrametric_by_SUMT(x, weights = weights, control = as.list(control$control)) ## Use some control arguments lateron ... L_new <- L(u) delta_L <- L_old - L_new delta_u <- max(abs(u_old - u)) if(verbose) message(gettextf("Change: u: %g L: %g", delta_u, delta_L)) if((delta_u < eps) || ((delta_L >= 0) && (delta_L <= reltol * (abs(L_old) + reltol)))) break iter <- iter + 1L } d <- .ultrametrify(u) meta <- list(objval = L(d), status = as.integer(iter == maxiter)) .cl_ultrametric_from_veclh(d, n, labels, meta) } ## * ls_fit_sum_of_ultrametrics ls_fit_sum_of_ultrametrics <- function(x, nterms = 1, weights = 1, control = list()) { if(!inherits(x, "dist")) x <- as.dist(x) ## We could catch some special cases right away: if x already is an ## ultrametric then the fit would be a list with x and nterms - 1 ## zero ultrametrics ... ## Control parameters: ## eps, eps <- control$eps if(is.null(eps)) eps <- 1e-6 ## maxiter, maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 100L ## method, method <- control$method if(is.null(method)) method <- "SUMT" ## reltol, reltol <- control$reltol if(is.null(reltol)) reltol <- 1e-6 ## verbose. verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Do this at last. control <- as.list(control$control) ## And be nice ... if(identical(method, "SUMT") && is.null(control$nruns)) control$nruns <- 10L L <- function(u) sum((x - rowSums(matrix(unlist(u), ncol = nterms))) ^ 2) ## Init. u <- rep.int(list(as.cl_ultrametric(0 * x)), nterms) L_new <- L(u) ## Loop. iter <- 1L while(iter <= maxiter) { if(verbose) message(gettextf("Iteration: %d", iter)) L_old <- L_new delta_u <- 0 for(i in seq_len(nterms)) { if(verbose) message(gettextf("Term: %d", i)) u_old <- u[[i]] ## Compute residual r = x - \sum_{j: j \ne i} u(j) r <- x - rowSums(matrix(unlist(u[-i]), ncol = nterms - 1L)) ## Fit residual. u[[i]] <- ls_fit_ultrametric(r, method, weights, control) ## Accumulate change. change <- max(abs(u[[i]] - u_old)) if(verbose) message(gettextf("Change: %g", change)) delta_u <- max(delta_u, change) } L_new <- L(u) delta_L <- L_old - L_new if(verbose) message(gettextf("Overall change: u: %g L: %g\n", delta_u, delta_L)) if((delta_u < eps) || ((delta_L >= 0) && (delta_L <= reltol * (abs(L_old) + reltol)))) break iter <- iter + 1L } .structure(u, objval = L_new, status = as.integer(iter == maxiter)) } ### * as.dist.hclust ## Using hclust() with methods 'median' or 'centroid' typically gives ## reversals and hence not valid hierarchies, i.e., distances which do ## not satisfy the ultrametricity conditions. The distances can be ## obtained via cophenetic(), but ls_fit_ultrametric() prefers using ## as.dist() [as arguably more appropriate] which in turn can be made to ## "work" by providing as.matrix() methods [bypassing the need to handle ## the extra arguments 'diag' and 'upper' for as.dist()]. as.matrix.hclust <- function(x, ...) as.matrix(cophenetic(x)) ### * .non_ultrametricity .non_ultrametricity <- function(x, max = FALSE) { if(!is.matrix(x)) x <- .symmetric_matrix_from_veclh(x) .C(C_deviation_from_ultrametricity, as.double(x), as.integer(nrow(x)), fn = double(1L), as.logical(max))$fn } ### * .cl_ultrametric_from_classes .cl_ultrametric_from_classes <- function(x) { ## Compute an ultrametric from a hierarchy of classes (i.e., an ## n-tree). labels <- attr(x, "labels") ## Ensure we have no duplicates. x <- x[!duplicated(x)] ## .get_classes_in_hierarchy() orders according to cardinality, but ## a consensus method may forget to ... x[] <- x[order(lengths(x))] ## Get the objects (unique codes in the classes). objects <- sort(unique(unlist(x))) ## (Could also look at the classes of length 1.) ## Recursively compute the heights of the classes. heights <- double(length = length(x)) for(i in which(lengths(x) > 1L)) { ## Find the relevant classes. j <- sapply(x[seq_len(i - 1L)], function(s) all(s %in% x[[i]])) heights[i] <- max(heights[j]) + 1 } ## Next, create an incidence matrix (objects by classes). incidences <- sapply(x, function(s) objects %in% s) ## Now that we have the heights and incidences, we can compute ## distances, using the idea that ## distance(i, j) = min(height(A): A contains i and j) n <- length(objects) d <- matrix(0, n, n) for(i in objects) d[i, ] <- heights[apply((rep(incidences[i, ], each = n) & incidences), 1L, which.max)] dimnames(d) <- rep.int(list(labels), 2L) as.cl_ultrametric(d) } ### * .cl_ultrametric_with_meta_added .cl_ultrametric_with_meta_added <- function(x, meta = NULL) { ## An alternative to adding a 'meta' argument to cl_ultrametric(). attr(x, "meta") <- meta x } ### .ultrametrify .ultrametrify <- function(x) { ## Ensure ultrametricity. ## In earlier versions, function ## .cl_ultrametric_from_ultrametric_approximation() tried rounding ## to non-ultrametric significance, using ## round(x, floor(abs(log10(.non_ultrametricity(x, max = TRUE))))) ## which is nice but does not guarantee ultrametricity (and may ## result in poorer approximations than what we use now). ## Hence, let us use single linkage hierarchical clustering which ## gives the best dominated ultrametric approximation. cophenetic(hclust(.dist_from_vector(x), "single")) } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/sumt.R0000644000175100001440000000722111304023136012527 0ustar hornikuserssumt <- function(x0, L, P, grad_L = NULL, grad_P = NULL, method = NULL, eps = NULL, q = NULL, verbose = NULL, control = list()) { ## Default values: make it nice for others to call us. if(is.null(eps)) eps <- sqrt(.Machine$double.eps) if(is.null(method)) method <- "CG" if(is.null(q)) q <- 10 if(is.null(verbose)) verbose <- getOption("verbose") Phi <- function(rho, x) L(x) + rho * P(x) if(is.null(grad_L) || is.null(grad_P)) { make_Phi <- function(rho) { function(x) Phi(rho, x) } make_grad_Phi <- function(rho) NULL } else { grad_Phi <- function(rho, x) grad_L(x) + rho * grad_P(x) make_Phi <- if(method == "nlm") { function(rho) { function(x) .structure(Phi(rho, x), gradient = grad_Phi(rho, x)) } } else function(rho) { function(x) Phi(rho, x) } make_grad_Phi <- function(rho) { function(x) grad_Phi(rho, x) } } ## ## For the penalized minimization, the Newton-type nlm() may be ## computationally infeasible (although it works much faster for ## fitting ultrametrics to the Phonemes data). ## De Soete recommends using Conjugate Gradients. ## We provide a simple choice: by default, optim(method = "CG") is ## used. If method is non-null and not "nlm", we use optim() with ## this method. In both cases, control gives the control parameters ## for optim(). ## If method is "nlm", nlm() is used, in which case control is ## ignored. Note that we call nlm() with checking analyticals ## turned off, as in some cases (e.g. when fitting ultrametrics) the ## penalty function is not even continuous ... optimize_with_penalty <- if(method == "nlm") function(rho, x) nlm(make_Phi(rho), x, check.analyticals = FALSE) $ estimate else { function(rho, x) optim(x, make_Phi(rho), gr = make_grad_Phi(rho), method = method, control = control) $ par } ## Note also that currently we do not check whether optimization was ## "successful" ... ## ## We currently require that x0 be a *list* of start values, the ## length of which gives the number of SUMT runs. But as always, ## let's be nice to users and developers, just in case ... if(!is.list(x0)) x0 <- list(x0) v_opt <- Inf x_opt <- NULL rho_opt <- NULL for(run in seq_along(x0)) { if(verbose) message(gettextf("SUMT run: %d", run)) x <- x0[[run]] ## ## Better upper/lower bounds for rho? rho <- max(L(x), 0.00001) / max(P(x), 0.00001) ## if(verbose) message(gettextf("Iteration: 0 Rho: %g P: %g", rho, P(x))) iter <- 1L repeat { ## ## Shouldnt't we also have maxiter, just in case ...? ## if(verbose) message(gettextf("Iteration: %d Rho: %g P: %g", iter, rho, P(x))) x_old <- x x <- optimize_with_penalty(rho, x) if(max(abs(x_old - x)) < eps) break iter <- iter + 1L rho <- q * rho } v <- Phi(rho, x) if(v < v_opt) { v_opt <- v x_opt <- x rho_opt <- rho } if(verbose) message(gettextf("Minimum: %g", v_opt)) } .structure(list(x = x_opt, L = L(x_opt), P = P(x_opt), rho = rho_opt, call = match.call()), class = "sumt") } clue/R/pclust.R0000644000175100001440000004307013036513600013057 0ustar hornikusers### * cl_pclust cl_pclust <- function(x, k, method = NULL, m = 1, weights = 1, control = list()) { ## Partition a cluster ensemble x into (at most) k classes by ## minimizing ## \sum_b \sum_j w_b u_{bj}^m d(x_b, p_j) ^ e ## for "suitable" prototypes p_1, ..., p_k, where 1 <= m < \infty, ## with 1 corresponding to hard (secondary) partitions, and d a ## dissimilarity measure (such as Euclidean dissimilarity of ## partitions or hierarchies). ## ## The algorithm works whenever there is a consensus method for ## solving ## \sum_b u_{bj}^m d(x_b, p) ^ e => \min_p ## ## As we refer to consensus methods by their *name* (e.g., 'HBH'), ## we rely on the registration mechanism (set_cl_consensus_method()) ## to provide the required information about d and e. clusterings <- as.cl_ensemble(x) type <- .cl_ensemble_type(clusterings) if(type == "partition") { ## Canonicalize by turning into an ensemble of partitions ## represented by membership matrices with the same (minimal) ## number of columns. memberships <- lapply(clusterings, cl_membership, max(sapply(clusterings, n_of_classes))) clusterings <- cl_ensemble(list = lapply(memberships, as.cl_partition)) } if(!inherits(method, "cl_consensus_method")) { ## Get required information on d and e from the registry. if(is.null(method)) method <- .cl_consensus_method_default(type) method <- get_cl_consensus_method(method, type) ## Note that this avoids registry lookup in subsequent calls to ## cl_consensus(). if(is.null(method$exponent)) stop("No information on exponent in consensus method used.") e <- method$exponent if(is.null(method$dissimilarity)) stop("No information on dissimilarity in consensus method used.") d <- function(x, y = NULL) cl_dissimilarity(x, y, method = method$dissimilarity) } family <- pclust_family(D = d, C = method$definition, e = e) out <- pclust(x, k, family, m, weights, control) ## Massage the results a bit. dissimilarities <- as.matrix(d(clusterings) ^ e) out$call <- match.call() out <- .structure(c(out, list(silhouette = silhouette(out$cluster, dmatrix = dissimilarities), validity = cl_validity(cl_membership(out), dissimilarities), ## ## Information about d and e is also in the ## family returned, of course. Trying to be ## nice to users by "directly" providing d ## and e is currently of limited usefulness ## as the pclust representation is not ## directly available to users. d = d, e = e ## )), class = unique(c("cl_pclust", class(out)))) as.cl_partition(out) } print.cl_pclust <- function(x, ...) { txt <- if(x$m == 1) gettextf("A hard partition of a cluster ensemble with %d elements into %d classes.", n_of_objects(x), n_of_classes(x)) else gettextf("A soft partition (degree m = %f) of a cluster ensemble with %d elements into %d classes.", x$m, n_of_objects(x), n_of_classes(x)) writeLines(strwrap(txt)) NextMethod("print", x, header = FALSE) print(x$validity, ...) invisible(x) } ### * pclust pclust <- function(x, k, family, m = 1, weights = 1, control = list()) { ## A general purpose alternating optimization algorithm for ## prototype-based partitioning. ## For now, assume family specifies three functions: ## * A dissimilarity function D() for data and prototypes. ## * A consensus function C() for data, weights and control. ## * An init function init() of data and k giving an initial object ## of k prototypes. ## ## We use k as the second argument as this seems to be common ## practice for partitioning algorithms. ## ## We assume that consensus functions can all handle WEIGHTS ## (formals: x, weights, control; only used positionally). ## ## ## We now allow for arbitrary representations/objects of prototypes. ## What is needed are functions to modify a *single* prototype and ## subset the prototypes. By default, list and matrix (with the ## usual convention that rows are "objects") representations are ## supported. Otherwise, the family needs to provide suitable ## .modify() and .subset() functions. ## The approach relies on having the initializer of the family ## (init()) return an appropriate object of prototypes. ## It would be possible to have default initializers as well to ## randomly subset the data (i.e., select elements of lists or rows ## of matrices, respectively). ## ## ## The 'prototypes' are not necessarily objects of the same kind as ## the data objects. Therefore, D() is really a 2-argument ## cross-dissimilarity function. ## It would also be useful to have a way of computing the pairwise ## dissimilarities between objects: but this is something different ## from D() is objects and prototypes are not of the same kind. ## A "clean" solution could consist in specifying the family either ## via a (non-symmetric) cross-dissimilarity function X(), or a ## symmetric D() which when called with a single argument gives the ## pairwise object dissimilarities. ## I.e., ## pclust_family(D = NULL, C, init = NULL, X = NULL, ......) ## using ## * If both D and X are not given => TROUBLE. ## * If only D is given: use for X as well. ## * If only X is given: only use as such. ## Something for the future ... ## ## ## If people have code for computing cross-dissimilarities for the ## data and a *single* prototype (say, xd()), they can easily wrap ## into what is needed using ## t(sapply(prototypes, function(p) xd(x, p))) ## Assuming symmetry of the dissimilarity, they could also do ## t(sapply(prototypes, xd, x)) ## ## Perhaps check whether 'family' is a feasible/suitable pclust ## family (object). D <- family$D C <- family$C e <- family$e .modify <- family$.modify .subset <- family$.subset maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 100L nruns <- control$nruns reltol <- control$reltol if(is.null(reltol)) reltol <- sqrt(.Machine$double.eps) start <- control$start verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Do this at last ... control <- as.list(control$control) ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } nruns <- length(start) } else { if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } start <- replicate(nruns, family$init(x, k), simplify = FALSE) } ## Initialize. ## We need to do this here because it is (currently) the only way to ## figure out the number B of objects to be partitioned (which is ## needed for getting the object weights to the right length). prototypes <- start[[1L]] dissimilarities <- D(x, prototypes) ^ e B <- NROW(dissimilarities) ## Also try to figure out (if necessary) how to modify a single ## prototype and to subset the prototypes. Note that we can only ## check this *after* prototypes were obtained (and not when the ## family object is created). if(is.null(.modify)) { if(is.list(prototypes)) .modify <- function(x, i, value) { x[[i]] <- value x } else if(is.matrix(prototypes)) .modify <- function(x, i, value) { x[i, ] <- value x } else stop("Cannot determine how to modify prototypes.") } else if(!is.function(.modify) || !identical(formals(args(.modify)), c("x", "i", "value"))) stop("Invalid function to modify prototypes.") if(is.null(.subset)) { if(is.list(prototypes)) .subset <- `[` else if(is.matrix(prototypes)) .subset <- function(x, i) x[i, , drop = FALSE] else stop("Cannot determine how to subset prototypes.") } else if(!is.function(.subset) || !identical(formals(args(.subset)), c("x", "i"))) stop("Invalid function to subset prototypes.") weights <- rep_len(weights, B) if(any(weights < 0)) stop("Argument 'weights' has negative elements.") if(!any(weights > 0)) stop("Argument 'weights' has no positive elements.") ## A little helper. .make_unit_weights <- function(B, i) { out <- double(B) out[i] <- 1 out } if(m == 1) { ## Hard partitions. value <- if(all(weights == 1)) function(dissimilarities, ids) sum(.one_entry_per_column(dissimilarities, ids)) else function(dissimilarities, ids) sum(weights * .one_entry_per_column(dissimilarities, ids)) opt_value <- Inf run <- 1L if(verbose && (nruns > 1L)) message(gettextf("Pclust run: %d", run)) repeat { class_ids <- max.col( - dissimilarities ) old_value <- value(dissimilarities, class_ids) if(verbose) message(gettextf("Iteration: 0 *** value: %g", old_value)) iter <- 1L while(iter <= maxiter) { class_ids_used <- unique(class_ids) for(j in class_ids_used) prototypes <- .modify(prototypes, j, C(x, weights * (class_ids %in% j), control)) dissimilarities <- D(x, prototypes) ^ e class_ids <- max.col( - dissimilarities ) ## Try avoiding degenerate solutions. if(length(class_ids_used) < k) { ## Find the k - l largest ## object-to-assigned-prototype dissimilarities. o <- order(.one_entry_per_column(dissimilarities, class_ids), decreasing = TRUE) ## Find and recompute unused prototypes. unused <- setdiff(seq_len(k), class_ids_used) for(j in seq_along(unused)) prototypes <- .modify(prototypes, unused[j], C(x, .make_unit_weights(B, o[j]), control)) dissimilarities[, unused] <- D(x, .subset(prototypes, unused)) ^ e class_ids <- max.col( - dissimilarities ) ## For the time being, do not retry in case the ## solution is still degenerate. } new_value <- value(dissimilarities, class_ids) if(verbose) message(gettextf("Iteration: %d *** value: %g", iter, new_value)) if(abs(old_value - new_value) < reltol * (abs(old_value) + reltol)) break old_value <- new_value iter <- iter + 1L } if(new_value < opt_value) { converged <- (iter <= maxiter) opt_value <- new_value opt_class_ids <- class_ids opt_prototypes <- prototypes } if(run >= nruns) break run <- run + 1L if(verbose) message(gettextf("Pclust run: %d", run)) prototypes <- start[[run]] dissimilarities <- D(x, prototypes) ^ e } ## We should really have a suitable "sparse matrix" class for ## representing the memberships of hard partitions. For now: opt_u <- NULL ## opt_u <- matrix(0, B, k) ## opt_u[cbind(seq_len(B), opt_class_ids)] <- 1 } else { ## Soft partitions. value <- if(all(weights == 1)) function(dissimilarities, u) sum(u ^ m * dissimilarities) else function(dissimilarities, u) sum(weights * u ^ m * dissimilarities) opt_value <- Inf run <- 1L if(verbose && (nruns > 1L)) message(gettextf("Pclust run: %d", run)) repeat { u <- .memberships_from_cross_dissimilarities(dissimilarities, m) old_value <- value(dissimilarities, u) if(verbose) message(gettextf("Iteration: 0 *** value: %g", old_value)) iter <- 1L while(iter <= maxiter) { ## Update the prototypes. ## This amounts to solving, for each j: ## \sum_b w_b u_{bj}^m D(x_b, p) ^ e => \min_p ## I.e., p_j is the *weighted* consensus of the x_b with ## corresponding weights u_{bj}^m. for(j in seq_len(k)) { prototypes <- .modify(prototypes, j, C(x, weights * u[, j] ^ m, control)) } ## Update u. dissimilarities <- D(x, prototypes) ^ e u <- .memberships_from_cross_dissimilarities(dissimilarities, m) new_value <- value(dissimilarities, u) if(verbose) message(gettextf("Iteration: %d *** value: %g", iter, new_value)) if(abs(old_value - new_value) < reltol * (abs(old_value) + reltol)) break old_value <- new_value iter <- iter + 1L } if(new_value < opt_value) { converged <- (iter <= maxiter) opt_value <- new_value opt_prototypes <- prototypes opt_u <- u } if(run >= nruns) break run <- run + 1L if(verbose) message(gettextf("Pclust run: %d", run)) prototypes <- start[[run]] dissimilarities <- D(x, prototypes) ^ e } opt_class_ids <- max.col(opt_u) ## Ensure that opt_u is a stochastic matrix. opt_u <- pmax(opt_u, 0) opt_u <- opt_u / rowSums(opt_u) rownames(opt_u) <- rownames(dissimilarities) opt_u <- cl_membership(as.cl_membership(opt_u), k) } names(opt_class_ids) <- rownames(dissimilarities) pclust_object(prototypes = opt_prototypes, membership = opt_u, cluster = opt_class_ids, family = family, m = m, value = opt_value, call = match.call(), attributes = list("converged" = converged)) } print.pclust <- function(x, header = TRUE, ...) { is_hard <- (x$m == 1) class_ids <- cl_class_ids(x) if(header) { txt <- if(is_hard) gettextf("A hard partition of %d objects into %d classes.", length(class_ids), n_of_classes(x)) else gettextf("A soft partition (degree m = %f) of %d objects into %d classes.", x$m, length(class_ids), n_of_classes(x)) writeLines(strwrap(txt)) } if(is_hard) { print(class_ids, ...) } else { writeLines("Class memberships:") print(cl_membership(x), ...) writeLines("Class ids of closest hard partition:") print(unclass(class_ids), ...) } invisible(x) } ### * pclust_family pclust_family <- function(D, C, init = NULL, description = NULL, e = 1, .modify = NULL, .subset = NULL) { ## Add checking formals (lengths) eventually ... if(is.null(init)) { ## Works for list representations ... init <- function(x, k) sample(x, k) } .structure(list(description = description, D = D, C = C, init = init, e = e, .modify = .modify, .subset = .subset), class = "pclust_family") } ### * pclust_object pclust_object <- function(prototypes, membership, cluster, family, m = 1, value, ..., classes = NULL, attributes = NULL) { out <- c(list(prototypes = prototypes, membership = membership, cluster = cluster, family = family, m = m, value = value), list(...)) attributes(out) <- c(attributes(out), attributes) classes <- unique(as.character(classes)) class(out) <- c(classes[classes != "pclust"], "pclust") out } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/tabulate.R0000644000175100001440000000061411304023136013337 0ustar hornikuserscl_tabulate <- function(x) { values <- unique(x) counts <- tabulate(match(x, values)) ## Still a bit tricky to create a data frame with a list "column" ## which is not protected by I(); otherwise, we oculd simply do ## data.frame(values = I(values), counts = counts) out <- data.frame(values = double(length(values)), counts = counts) out$values <- values out } clue/R/margin.R0000644000175100001440000000060511304023136013013 0ustar hornikuserscl_margin <- function(x) { if(is.cl_hard_partition(x)) out <- rep.int(1, n_of_objects(x)) else if(is.cl_partition(x)) { x <- cl_membership(x) i <- seq_len(nrow(x)) j <- cbind(i, max.col(x)) out <- x[j] x[j] <- 0 out <- out - x[cbind(i, max.col(x))] } else stop("Argument 'x' must be a partition.") out } clue/R/AAA.R0000644000175100001440000000037411304023136012123 0ustar hornikusers## Things which must come first in the package code. ### * Internal utilities. .false <- function(x) FALSE .true <- function(x) TRUE ## A fast version of structure(). .structure <- function(x, ...) `attributes<-`(x, c(attributes(x), list(...))) clue/R/agreement.R0000644000175100001440000002713313435044376013532 0ustar hornikusers### * cl_agreement cl_agreement <- function(x, y = NULL, method = "euclidean", ...) { ## ## This code is repeated from cl_dissimilarity(), mutatis mutandis. ## Not really a big surprise ... ## x <- as.cl_ensemble(x) is_partition_ensemble <- (inherits(x, "cl_partition_ensemble") || all(vapply(x, .has_object_memberships, NA))) ## Be nice. if(is.character(y) || is.function(y)) { method <- y y <- NULL } if(is.function(method)) method_name <- "user-defined method" else { if(!inherits(method, "cl_agreement_method")) { ## Get the method definition and description from the ## registry. type <- ifelse(is_partition_ensemble, "partition", "hierarchy") method <- get_cl_agreement_method(method, type) } method_name <- method$description method <- method$definition } if(!is.null(y)) { y <- as.cl_ensemble(y) is_partition_ensemble_y <- (inherits(y, "cl_partition_ensemble") || all(vapply(x, .has_object_memberships, NA))) if(!identical(is_partition_ensemble, is_partition_ensemble_y)) stop("Cannot mix partitions and hierarchies.") if(n_of_objects(x) != n_of_objects(y)) stop("All clusterings must have the same number of objects.") ## Build a cross-proximity object of cross-agreements. d <- matrix(0, length(x), length(y)) for(j in seq_along(y)) d[, j] <- sapply(x, method, y[[j]], ...) dimnames(d) <- list(names(x), names(y)) return(cl_cross_proximity(d, method_name, class = "cl_cross_agreement")) } ## Otherwise, build a proximity object of dissimilarities. n <- length(x) d <- vector("list", length = n - 1L) ind <- seq_len(n) while(length(ind) > 1L) { j <- ind[1L] ind <- ind[-1L] d[[j]] <- sapply(x[ind], method, x[[j]], ...) } ## ## We assume that self-agreements are always one ... ## cl_proximity(unlist(d), method_name, labels = names(x), self = rep.int(1, length(x)), size = n, class = "cl_agreement") } ### ** .cl_agreement_partition_euclidean .cl_agreement_partition_euclidean <- function(x, y) { ## ## Upper bound for maximal dissimilarity, maybe improve eventually. d_max <- sqrt(2 * n_of_objects(x)) ## 1 - .cl_dissimilarity_partition_euclidean(x, y) / d_max } ### ** .cl_agreement_partition_manhattan .cl_agreement_partition_manhattan <- function(x, y) { ## ## Upper bound for maximal dissimilarity, maybe improve eventually. d_max <- 2 * n_of_objects(x) ## 1 - .cl_dissimilarity_partition_manhattan(x, y) / d_max } ### ** .cl_agreement_partition_Rand .cl_agreement_partition_Rand <- function(x, y) { n <- n_of_objects(x) ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) x <- table(cl_class_ids(x), cl_class_ids(y)) ## ## The number A of concordant pairs is given by ## A = choose(n,2) + \sum_{i,j} x_{ij}^2 ## - (1/2) * (\sum_i x_{i.}^2 + \sum_j x_{.j}^2) ## = choose(n,2) + 2 \sum_{i,j} choose(x_{ij},2) ## - (\sum_i choose(x_{i.},2) + \sum_j choose(x_{.j},2) ## with the first version certainly much faster to compute. ## 1 + (sum(x^2) - (sum(rowSums(x)^2) + sum(colSums(x)^2)) / 2) / choose(n, 2) } ### ** .cl_agreement_partition_cRand .cl_agreement_partition_cRand <- function(x, y) { if(!is.cl_hard_partition(x) || !is.cl_hard_partition(y)) stop("Can only handle hard partitions.") n <- n_of_objects(x) x <- table(cl_class_ids(x), cl_class_ids(y)) ## ## The basic formula is ## (Sxy - E) / ((Sx. + S.y) / 2 - E) ## where ## Sxy = \sum_{i,j} choose(x_{ij}, 2) ## Sx. = \sum_i choose(x_{i.}, 2) ## S.y = \sum_j choose(x_{.j}, 2) ## and ## E = Sx. * S.y / choose(n, 2) ## We replace the bincoefs by the corresponding sums of squares, ## getting ## (Txy - F) / ((Tx. + T.y) / 2 - F) ## where ## Txy = \sum_{i,j} x_{ij}^2 - n ## Tx. = \sum_i x_{i.}^2 - n ## T.y = \sum_j x_{.j}^2 - n ## and ## F = Tx. * T.y / (n^2 - n) ## Txy <- sum(x ^ 2) - n Tx. <- sum(rowSums(x) ^ 2) - n T.y <- sum(colSums(x) ^ 2) - n F <- Tx. * T.y / (n ^ 2 - n) (Txy - F) / ((Tx. + T.y) / 2 - F) } ### ** .cl_agreement_partition_NMI .cl_agreement_partition_NMI <- function(x, y) { if(!is.cl_hard_partition(x) || !is.cl_hard_partition(y)) stop("Can only handle hard partitions.") x <- table(cl_class_ids(x), cl_class_ids(y)) x <- x / sum(x) m_x <- rowSums(x) m_y <- colSums(x) y <- outer(m_x, m_y) i <- which((x > 0) & (y > 0)) out <- sum(x[i] * log(x[i] / y[i])) e_x <- sum(m_x * log(ifelse(m_x > 0, m_x, 1))) e_y <- sum(m_y * log(ifelse(m_y > 0, m_y, 1))) out / sqrt(e_x * e_y) } ### ** .cl_agreement_partition_KP .cl_agreement_partition_KP <- function(x, y) { ## Agreement measure due to Katz & Powell (1953, Psychometrika), see ## also Messatfa (1992, Journal of Classification). n <- n_of_objects(x) ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) x <- table(cl_class_ids(x), cl_class_ids(y)) A_xy <- sum(x ^ 2) A_x. <- sum(rowSums(x) ^ 2) A_.y <- sum(colSums(x) ^ 2) (n^2 * A_xy - A_x. * A_.y) / sqrt(A_x. * (n^2 - A_x.) * A_.y * (n^2 - A_.y)) } ### ** .cl_agreement_partition_angle .cl_agreement_partition_angle <- function(x, y) { ## Maximal angle between the matched memberships. k <- max(n_of_classes(x), n_of_classes(y)) M_x <- cl_membership(x, k) M_y <- cl_membership(y, k) ## Match classes from conforming memberships. ind <- solve_LSAP(crossprod(M_x, M_y), maximum = TRUE) sum(M_x * M_y[, ind]) / sqrt(sum(M_x ^ 2) * sum(M_y ^ 2)) } ### ** .cl_agreement_partition_diag .cl_agreement_partition_diag <- function(x, y) { ## Maximal co-classification rate. k <- max(n_of_classes(x), n_of_classes(y)) M_x <- cl_membership(x, k) M_y <- cl_membership(y, k) ## Match classes from conforming memberships. ind <- solve_LSAP(crossprod(M_x, M_y), maximum = TRUE) sum(M_x * M_y[, ind]) / n_of_objects(x) } ### ** .cl_agreement_partition_FM .cl_agreement_partition_FM <- function(x, y) { ## Fowlkes-Mallows index. n <- n_of_objects(x) ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) x <- table(cl_class_ids(x), cl_class_ids(y)) (sum(x ^ 2) - n) / sqrt((sum(rowSums(x) ^ 2) - n) * (sum(colSums(x) ^ 2) - n)) } ### ** .cl_agreement_partition_Jaccard .cl_agreement_partition_Jaccard <- function(x, y) { ## Jaccard index. n <- n_of_objects(x) ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) x <- table(cl_class_ids(x), cl_class_ids(y)) Z <- sum(x ^ 2) (Z - n) / (sum(rowSums(x) ^ 2) + sum(colSums(x) ^ 2) - n - Z) } ### ** .cl_agreement_partition_purity .cl_agreement_partition_purity <- function(x, y) { ## Purity of classes of x with respect to those of y: relative ## fraction of "optimally matched and collapsed" joint class ## frequencies, i.e., \sum_i \max_j c_{ij} / n. n <- n_of_objects(x) ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) x <- table(cl_class_ids(x), cl_class_ids(y)) sum(apply(x, 1L, max)) / n } .cl_agreement_partition_PS <- function(x, y) { ## Prediction Strength as used in Tibshirani and Walter (2005), ## "Cluster Validation by Prediction Strength", JCGS. ## See Eqn 2.1 in the reference: this is ## min_l rate of different objects in the same class in partition ## A and in class l in partition B, ## where the min is taken over all classes l of partition B. x <- table(cl_class_ids(x), cl_class_ids(y)) s <- rowSums(x) min((rowSums(x ^ 2) - s) / (s * (s - 1)), na.rm = TRUE) } ## Some computations useful for interpreting some of the above. ## ## Consider two hard partitions A and B and write ## a_{ik} ... indicator of object i in class k for partition A ## b_{il} ... indicator of object i in class l for partition B ## (so that the a_{ik} and b_{il} are of course the membership matrices ## of the partitions). ## ## Then obviously ## \sum_i a_{ik} b_{il} = m_{kl} ## is the number of objects in class k for A and in class l for B, and ## \sum_i a_{ik} = m_{k.} = # objects in class k for A ## \sum_i b_{il} = m_{.l} = # objects in class l for B ## ## Number of pairs of objects in the same classes for both A and B: ## \sum_{i, j, k, l} a_{ik} a_{jk} b_{il} b_{jl} ## = \sum_{k, l} \sum_i a_{ik} b_{il} \sum_j a_{jk} b_{jl} ## = \sum_{k, l} m_{kl} ^ 2 ## This includes the n pairs with identical objects, hence: ## Number of distinct pairs of objects in the same classes for both A ## and B: ## (\sum_{k, l} m_{kl} ^ 2 - n) / 2 ## ## Number of pairs of objects in the same class for A: ## \sum_{i, j, k} a_{ik} a_{jk} ## = \sum_k \sum_i a_{ik} \sum_j a_{jk} ## = \sum_k m_{k.} ^ 2 ## Again, this includes the n pairs with identical objects, hence: ## Number of distinct pairs of objects in the same class for A: ## (\sum_k m_{k.} ^ 2 - n) / 2 ## ## Similarly, \sum_l m_{.l} ^ 2 corresponds to the number of pairs of ## objects in the same class for B. ## ## Finally, to get the number of pairs of objects in different classes ## for both A and B, we note that this is the total number of pairs, ## minus the sum of the numbers of those in the same class for A and for ## B, respectively, plus the number of pairs in the same class for both ## A and B. ## ## This makes e.g. the interpretation of some of the Fowlkes-Mallows or ## Rand agreement indices rather straightforward. ### ** .cl_agreement_hierarchy_euclidean .cl_agreement_hierarchy_euclidean <- function(x, y) 1 / (1 + .cl_dissimilarity_hierarchy_euclidean(x, y)) ### ** .cl_agreement_hierarchy_manhattan .cl_agreement_hierarchy_manhattan <- function(x, y) 1 / (1 + .cl_dissimilarity_hierarchy_manhattan(x, y)) ### ** .cl_agreement_hierarchy_cophenetic .cl_agreement_hierarchy_cophenetic <- function(x, y) { ## Cophenetic correlation. if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) cor(cl_object_dissimilarities(x), cl_object_dissimilarities(y)) } ### ** .cl_agreement_hierarchy_angle .cl_agreement_hierarchy_angle <- function(x, y) { ## Angle between ultrametrics. if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u_x <- cl_object_dissimilarities(x) u_y <- cl_object_dissimilarities(y) sum(u_x * u_y) / sqrt(sum(u_x ^ 2) * sum(u_y ^ 2)) } ### ** .cl_agreement_hierarchy_gamma .cl_agreement_hierarchy_gamma <- function(x, y) 1 - .cl_dissimilarity_hierarchy_gamma(x, y) ### * [.cl_agreement "[.cl_agreement" <- function(x, i, j) { y <- NextMethod("[") if(!inherits(y, "cl_agreement")) { description <- attr(x, "description") return(cl_cross_proximity(y, description = description, class = "cl_cross_agreement")) } y } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/utilities.R0000644000175100001440000001015014144531004013550 0ustar hornikusers### * Matrix/vector utilities ### * .dist_from_vector .dist_from_vector <- function(x, n = NULL, labels = NULL) { ## This might be useful as as.dist.vector, perhaps without the extra ## argument n then which we only have for minimal performance gains. if(is.null(n)) n <- as.integer((sqrt(1 + 8 * length(x)) + 1) / 2) attr(x, "Size") <- n if(!is.null(labels)) attr(x, "Labels") <- labels class(x) <- "dist" x } ### ** .one_entry_per_column .one_entry_per_column <- function(x, j) { ## For a matrix x and a vector of column indices j_1, ..., j_n where ## n is the number of rows of x, get x[1,j_1], ..., x[n,j_n]. ## ## This used to have ## if(!is.matrix(x)) ## stop("Argument 'x' must be a matrix.") ## but that will fail for sparse matrix classes. ## So let us hope for the best ... ## x[cbind(seq_len(nrow(x)), j)] } ".one_entry_per_column<-" <- function(x, j, value) { ## ## This used to have ## if(!is.matrix(x)) ## stop("Argument 'x' must be a matrix.") ## but that will fail for sparse matrix classes. ## So let us hope for the best ... ## x[cbind(seq_len(nrow(x)), j)] <- value x } ### * .symmetric_matrix_from_veclh .symmetric_matrix_from_veclh <- function(x, n = NULL) { ## In essence the same as as.matrix.dist, but without handling the ## additional attributes that dist objects might have. if(is.null(n)) n <- as.integer((sqrt(1 + 8 * length(x)) + 1) / 2) M <- matrix(0, n, n) M[row(M) > col(M)] <- x M + t(M) } ### * .weighted_mean_of_object_dissimilarities .weighted_mean_of_object_dissimilarities <- function(x, w = NULL) { w <- if(is.null(w)) { rep.int(1, length(x)) } else { rep_len(w, length(x)) } ## (Need the latter because we want w / sum(w) ...) dissimilarities <- lapply(x, cl_object_dissimilarities) m <- rowSums(mapply(`*`, dissimilarities, w / sum(w))) labels <- attr(dissimilarities[[1L]], "Labels") .dist_from_vector(m, labels = labels) } ### ** .weighted_sum_of_matrices .weighted_sum_of_matrices <- function(x, w = NULL, nr = NULL) { ## Quite often we need to compute weighted sums \sum_b w_b X_b of ## conforming matrices \{ X_b \}. If x is a list containing the ## matrices and w the vector of weights, it seems that one ## reasonably efficient way of doing this is the following. if(is.null(w)) w <- rep.int(1, length(x)) if(is.null(nr)) nr <- NROW(x[[1L]]) matrix(rowSums(mapply(`*`, x, w)), nr) } ### ** .weighted_sum_of_vectors .weighted_sum_of_vectors <- function(x, w = NULL) { ## See above. if(is.null(w)) w <- rep.int(1, length(x)) rowSums(mapply(`*`, x, w)) } ### * Containers ## Creator. .make_container <- function(x, classes, properties = NULL) { out <- list(.Data = x, .Meta = properties) class(out) <- unique(classes) out } ## Getters. .get_representation <- function(x) x$.Data .get_properties <- function(x) x$.Meta .get_property <- function(x, which) x$.Meta[[which]] .has_property <- function(x, which) which %in% names(x$.Meta) .get_property_from_object_or_representation <- function(x, which, getter) { if(.has_property(x, which)) .get_property(x, which) else { if(missing(getter)) getter <- get(which) getter(.get_representation(x)) } } ## Methods (sort of). .print_container <- function(x, cls, ...) { writeLines(gettextf("An object of virtual class '%s', with representation:\n", cls)) print(.get_representation(x), ...) invisible(x) } ### * Others weighted_median <- function(x, w = 1, na.rm = FALSE) { w <- rep_len(w, length(x)) if(na.rm && any(ind <- is.na(x))) { x <- x[!ind] w <- w[!ind] } if(any(is.na(x)) || !length(x)) return(NA) w <- w / sum(w) ind <- order(x) x <- x[ind] w <- w[ind] x[which.min(x * (cumsum(w) - 0.5) - cumsum(w * x))] } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/consensus.R0000644000175100001440000011507514144530711013575 0ustar hornikusers### * cl_consensus cl_consensus <- function(x, method = NULL, weights = 1, control = list()) { ## ## Interfaces are a matter of taste. ## E.g., one might want to have a 'type' argument indication whether ## hard or soft partitions are sought. One could then do ## cl_consensus(x, method = "euclidean", type = "hard") ## to look for an optimal median (or least squares) hard partition ## (for euclidean dissimilarity). ## For us, "method" really indicates a certain algorithm, with its ## bells and whistles accessed via the 'control' argument. ## clusterings <- as.cl_ensemble(x) if(!length(clusterings)) stop("Cannot compute consensus of empty ensemble.") weights <- rep_len(weights, length(clusterings)) if(any(weights < 0)) stop("Argument 'weights' has negative elements.") if(!any(weights > 0)) stop("Argument 'weights' has no positive elements.") if(!is.function(method)) { if(!inherits(method, "cl_consensus_method")) { ## Get the method definition from the registry. type <- .cl_ensemble_type(clusterings) if(is.null(method)) method <- .cl_consensus_method_default(type) method <- get_cl_consensus_method(method, type) } method <- method$definition } method(clusterings, weights, control) } ### * .cl_consensus_partition_DWH .cl_consensus_partition_DWH <- function(clusterings, weights, control) { ## ## Could make things more efficient by subscripting on positive ## weights. ## (Note that this means control$order has to be subscripted as ## well.) ## max_n_of_classes <- max(sapply(clusterings, n_of_classes)) ## Control parameters. k <- control$k if(is.null(k)) k <- max_n_of_classes order <- control$order if(is.null(order)) order <- sample(seq_along(clusterings)) clusterings <- clusterings[order] weights <- weights[order] k_max <- max(k, max_n_of_classes) s <- weights / cumsum(weights) s[is.na(s)] <- 0 # Division by zero ... M <- cl_membership(clusterings[[1L]], k_max) for(b in seq_along(clusterings)[-1L]) { mem <- cl_membership(clusterings[[b]], k_max) ## Match classes from conforming memberships. ind <- solve_LSAP(crossprod(M, mem), maximum = TRUE) M <- (1 - s[b]) * M + s[b] * mem[, ind] if(k < k_max) M <- .project_to_leading_columns(M, k) } M <- .cl_membership_from_memberships(M[, seq_len(k), drop = FALSE], k) as.cl_partition(M) } ### * .cl_consensus_partition_AOS .cl_consensus_partition_AOS <- function(clusterings, weights, control, type = c("SE", "HE", "SM", "HM")) { ## The start of a general purpose optimizer for determining ## consensus partitions by minimizing ## \sum_b w_b d(M, M_b) ^ e ## = \sum_b \min_{P_b} w_b f(M, M_b P_b) ^ e ## for the special case where the criterion function is based on ## M and M_b P_b (i.e., column permutations of M_b), as opposed to ## the general case where d(M, M_b) = \min_{P_b} f(M, P_b, M_b) ## handled by .cl_consensus_partition_AOG(). ## ## The AO ("alternative optimization") proceeds by alternatively ## matching the M_b to M by minimizing f(M, M_b P_b) over P_b, and ## fitting M by minimizing \sum_b w_b f(M, M_b P_b) ^ e for fixed ## matchings. ## ## Such a procedure requires three ingredients: a function for ## matching M_b to M (in fact simply replacing M_b by the matched ## M_b P_b); a function for fitting M to the \{M_b P_b\}, and a ## function for computing the value of the criterion function ## corresponding to this fit (so that one can stop if the relative ## improvement is small enough). ## ## For the time being, we only use this to determine soft and hard ## Euclidean least squares consensus partitions (soft and hard ## Euclidean means), so the interface does not yet reflect the ## generality of the approach (which would either pass the three ## functions, or even set up family objects encapsulating the three ## functions). ## ## This special case is provided for efficiency and convenience. ## Using the special form of the criterion function, we can simply ## always work memberships with the same maximal number of columns, ## and with the permuted \{ M_b P_b \}. ## For the time being ... type <- match.arg(type) w <- weights / sum(weights) n <- n_of_objects(clusterings) k_max <- max(sapply(clusterings, n_of_classes)) ## Control parameters. k <- control$k if(is.null(k)) k <- k_max maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 100 nruns <- control$nruns reltol <- control$reltol if(is.null(reltol)) reltol <- sqrt(.Machine$double.eps) start <- control$start verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } nruns <- length(start) } else { if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } start <- replicate(nruns, .random_stochastic_matrix(n, k), simplify = FALSE) } ## The maximal (possible) number of classes in M and the \{ M_b \}. k_all <- max(k, k_max) value <- switch(type, SE = , HE = function(M, memberships, w) { sum(w * sapply(memberships, function(u) sum((u - M) ^ 2))) }, SM = , HM = function(M, memberships, w) { sum(w * sapply(memberships, function(u) sum(abs(u - M)))) }) ## Return the M[, ind] column permutation of M optimally matching N. match_memberships <- switch(type, SE = , HE = function(M, N) { M[, solve_LSAP(crossprod(N, M), maximum = TRUE), drop = FALSE] }, SM = , HM = function(M, N) { M[, solve_LSAP(.cxdist(N, M, "manhattan")), drop = FALSE] }) ## Function for fitting M to (fixed) memberships \{ M_b P_b \}. ## As we use a common number of columns for all membership matrices ## involved, we need to pass the desired 'k' ... fit_M <- switch(type, SE = function(memberships, w, k) { ## Update M as \sum w_b M_b P_b. M <- .weighted_sum_of_matrices(memberships, w, nrow(M)) ## If k < k_all, "project" as indicated in Gordon & ## Vichi (2001), p. 238. if(k < ncol(M)) M <- .project_to_leading_columns(M, k) M }, HE = , HM = function(memberships, w, k) { ## Compute M as \sum w_b M_b P_b. M <- .weighted_sum_of_matrices(memberships, w, nrow(M)) ## And compute a closest hard partition H(M) from ## that, using the first k columns of M. ids <- max.col(M[ , seq_len(k), drop = FALSE]) .cl_membership_from_class_ids(ids, ncol(M)) }, SM = .l1_fit_M) memberships <- lapply(clusterings, cl_membership, k_all) V_opt <- Inf M_opt <- NULL for(run in seq_along(start)) { if(verbose && (nruns > 1L)) message(gettextf("AOS run: %d", run)) M <- start[[run]] if(k < k_all) M <- cbind(M, matrix(0, nrow(M), k_all - k)) memberships <- lapply(memberships, match_memberships, M) old_value <- value(M, memberships, w) if(verbose) message(gettextf("Iteration: 0 *** value: %g", old_value)) iter <- 1L while(iter <= maxiter) { ## Fit M to the M_b P_b. M <- fit_M(memberships, w, k) ## Match the \{ M_b P_b \} to M. memberships <- lapply(memberships, match_memberships, M) ## Update value. new_value <- value(M, memberships, w) if(verbose) message(gettextf("Iteration: %d *** value: %g", iter, new_value)) if(abs(old_value - new_value) < reltol * (abs(old_value) + reltol)) break old_value <- new_value iter <- iter + 1L } if(new_value < V_opt) { converged <- (iter <= maxiter) V_opt <- new_value M_opt <- M } if(verbose) message(gettextf("Minimum: %g", V_opt)) } M <- .stochastify(M_opt) rownames(M) <- rownames(memberships[[1L]]) meta <- list(objval = value(M, memberships, w), converged = converged) M <- .cl_membership_from_memberships(M[, seq_len(k), drop = FALSE], k, meta) as.cl_partition(M) } .random_stochastic_matrix <- function(n, k) { M <- matrix(runif(n * k), n, k) M / rowSums(M) } .l1_fit_M <- function(memberships, w, k) { ## Determine stochastic matrix M with at most k leading nonzero ## columns such that ## ## \sum_b w_b \sum_{i,j} | m_{ij}(b) - m_{ij} | => min ## ## where the sum over j goes from 1 to k. ## ## Clearly, this can be done separately for each row, where we need ## to minimize ## ## \sum_b w_b \sum_j | y_j(b) - x_j | => min ## ## over all probability vectors x. Such problems can e.g. be solved ## via the following linear program: ## ## \sum_b \sum_j w_b e'(u(b) + v(b)) => min ## ## subject to ## ## u(1), v(1), ..., u(B), v(B), x >= 0 ## x + u(b) - v(b) = y(b), b = 1, ..., B ## e'x = 1 ## ## (where e = [1, ..., 1]). ## ## So we have one long vector z of "variables": ## ## z = [u(1)', v(1)', ..., u(B)', v(B)', x']' ## ## of length (2B + 1) k, with x the object of interest. ## Rather than providing a separate function for weighted L1 fitting ## of probability vectors we prefer doing "everything" at once, in ## order to avoid recomputing the coefficients and constraints of ## the associated linear program. B <- length(memberships) L <- (2 * B + 1) * k ## Set up associated linear program. ## Coefficients in the objective function. objective_in <- c(rep(w, each = 2 * k), rep.int(0, k)) ## Constraints. constr_mat <- rbind(diag(1, L), cbind(kronecker(diag(1, B), cbind(diag(1, k), diag(-1, k))), kronecker(rep.int(1, B), diag(1, k))), c(rep.int(0, 2 * B * k), rep.int(1, k))) constr_dir <- c(rep.int(">=", L), rep.int("==", B * k + 1L)) ind <- seq.int(from = 2 * B * k + 1L, length.out = k) nr <- NROW(memberships[[1L]]) nc <- NCOL(memberships[[1L]]) M <- matrix(0, nrow = nr, ncol = k) ## Put the memberships into one big array so that we can get their ## rows more conveniently (and efficiently): memberships <- array(unlist(memberships), c(nr, nc, B)) for(i in seq_len(nr)) { out <- lpSolve::lp("min", objective_in, constr_mat, constr_dir, c(rep.int(0, L), memberships[i, seq_len(k), ], 1)) M[i, ] <- out$solution[ind] } ## Add zero columns if necessary. if(k < nc) M <- cbind(M, matrix(0, nr, nc - k)) M } ### ** .cl_consensus_partition_soft_euclidean .cl_consensus_partition_soft_euclidean <- function(clusterings, weights, control) .cl_consensus_partition_AOS(clusterings, weights, control, "SE") ### ** .cl_consensus_partition_hard_euclidean .cl_consensus_partition_hard_euclidean <- function(clusterings, weights, control) .cl_consensus_partition_AOS(clusterings, weights, control, "HE") ### ** .cl_consensus_partition_soft_manhattan .cl_consensus_partition_soft_manhattan <- function(clusterings, weights, control) .cl_consensus_partition_AOS(clusterings, weights, control, "SM") ### ** .cl_consensus_partition_hard_manhattan .cl_consensus_partition_hard_manhattan <- function(clusterings, weights, control) .cl_consensus_partition_AOS(clusterings, weights, control, "HM") ### * .cl_consensus_partition_AOG .cl_consensus_partition_AOG <- function(clusterings, weights, control, type = c("GV1")) { ## The start of a general purpose optimizer for determining ## consensus partitions by minimizing ## \sum_b w_b d(M, M_b) ^ p ## = \sum_b \min_{P_b} w_b f(M, M_b, P_b) ^ e ## for general dissimilarity matrices which involve class matching ## via permutation matrices P_b. ## ## The AO ("Alternative Optimization") proceeds by alternating ## between determining the optimal permutations P_b by minimizing ## f(M, M_b, P_b) ## for fixed M, and fitting M by minimizing ## \sum_b w_b f(M, M_b, P_b) ^ e ## for fixed \{ P_b \}. ## ## We encapsulate this into functions fit_P() and fit_M() (and a ## value() function for the criterion function to be minimized with ## respect to both M and \{ P_b \}, even though the current ## interface does not yet reflect the generality of the approach. ## ## Note that rather than passing on information about the numbers of ## classes (e.g., needed for GV1) and representing all involved ## membership matrices with the same maximal number of columns, we ## use "minimal" representations with no dummy classes (strictly ## speaking, with the possible exception of M, for which the given k ## is used). ## For the time being ... type <- match.arg(type) w <- weights / sum(weights) n <- n_of_objects(clusterings) k_max <- max(sapply(clusterings, n_of_classes)) ## Control parameters. k <- control$k if(is.null(k)) k <- k_max maxiter <- control$maxiter if(is.null(maxiter)) maxiter <- 100L nruns <- control$nruns reltol <- control$reltol if(is.null(reltol)) reltol <- sqrt(.Machine$double.eps) start <- control$start verbose <- control$verbose if(is.null(verbose)) verbose <- getOption("verbose") ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } nruns <- length(start) } else { if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } start <- replicate(nruns, .random_stochastic_matrix(n, k), simplify = FALSE) } ## ## For the given memberships, we can simply use ncol() in the ## computations (rather than n_of_classes(), because we used ## cl_membership() to create them. For M, the number of classes ## could be smaller than the given k "target". ## value <- function(M, permutations, memberships, w) { k <- .n_of_nonzero_columns(M) d <- function(u, p) { ## Compute the squared GV1 dissimilarity between M and u ## based on the M->u class matching p. nc_u <- ncol(u) if(nc_u == k) { ## Simple case: all classes are matched. sum((u[, p] - M) ^ 2) } else { ## Only include the matched non-dummy classes of M .. ind <- seq_len(k) ## ... which are matched to non-dummy classes of u. ind <- ind[p[ind] <= nc_u] sum((u[, p[ind]] - M[, ind]) ^ 2) } } sum(w * mapply(d, memberships, permutations)) } fit_P <- function(u, M) { ## Return a permutation representing a GV1 optimal matching of ## the columns of M to the columns of u (note the order of the ## arguments), using a minimal number of dummy classes (i.e., p ## has max(.n_of_nonzero_columns(M), n_of_classes(u)) entries). ## See also .cl_dissimilarity_partition_GV1(). C <- outer(colSums(M ^ 2), colSums(u ^ 2), `+`) - 2 * crossprod(M, u) nc_M <- .n_of_nonzero_columns(M) nc_u <- ncol(u) ## (See above for ncol() vs n_of_classes().) if(nc_M < nc_u) C <- rbind(C, matrix(0, nrow = nc_u - nc_M, ncol = nc_u)) else if(nc_M > nc_u) C <- cbind(C, matrix(0, nrow = nc_M, ncol = nc_M - nc_u)) solve_LSAP(C) } fit_M <- function(permutations, memberships, w) { ## Here comes the trickiest part ... ## ## In general, M = [m_{iq}] is determined as follows. ## Write value(M, permutations, memberships, w) as ## \sum_b \sum_i \sum_{p=1}^{k_b} \sum_{q=1}^k ## w_b (u_{ip}(b) - m_{iq})^2 x_{pq}(b) ## where U(b) and X(b) are the b-th membership matrix and the ## permutation matrix representing the M->U(b) non-dummy class ## matching (as always, note the order of the arguments). ## ## Let ## \beta_{iq} = \sum_b \sum_{p=1}^{k_b} w_b u_{ip}(b) x_{pq}(b) ## \alpha_q = \sum_b \sum_{p=1}^{k_b} w_b x_{pq}(b) ## and ## \bar{m}_{iq} = ## \cases{\beta_{iq}/\alpha_q, & $\alpha_q > 0$ \cr ## 0 & otherwise}. ## Then, as the cross-product terms cancel out, the value ## function rewrites as ## \sum_b \sum_i \sum_{p=1}^{k_b} \sum_{q=1}^k ## w_b (u_{ip}(b) - \bar{m}_{iq})^2 x_{pq}(b) ## + \sum_i \sum_q \alpha_q (\bar{m}_{iq} - m_{iq}) ^ 2, ## where the first term is a constant, and the minimum is found ## by solving ## \sum_q \alpha_q (\bar{m}_{iq} - m_{iq}) ^ 2 => min! ## s.t. ## m_{i1}, ..., m_{ik} >= 0, \sum_{iq} m_{iq} = 1. ## ## We can distinguish three cases. ## A. If S_i = \sum_q \bar{m}_{iq} = 1, things are trivial. ## B. If S_i = \sum_q \bar{m}_{iq} < 1. ## B1. If some \alpha_q are zero, then we can choose ## m_{iq} = \bar{m}_{iq} for those q with \alpha_q = 0; ## m_{iq} = 1 / number of zero \alpha's, otherwise. ## B2. If all \alpha_q are positive, we can simply ## equidistribute 1 - S_i over all classes as written ## in G&V. ## C. If S_i > 1, things are not so clear (as equidistributing ## will typically result in violations of the non-negativity ## constraint). We currently revert to using solve.QP() from ## package quadprog, as constrOptim() already failed in very ## simple test cases. ## ## Now consider \sum_{p=1}^{k_b} x_{pq}(b). If k <= k_b for all ## b, all M classes from 1 to k are matched to one of the k_b ## classes in U(b), hence the sum and also \alpha_q are one. ## But then ## \sum_q \bar{m}_{iq} ## = \sum_b \sum_{p=1}^{k_b} w_b u_{ip}(b) x_{pq}(b) ## <= \sum_b \sum_{p=1}^{k_b} w_b u_{ip}(b) ## = 1 ## with equality if k = k_b for all b. I.e., ## * If k = \min_b k_b = \max k_b, we are in case A. ## * If k <= \min_b k_b, we are in case B2. ## And it makes sense to handle these cases explicitly for ## efficiency reasons. ## And now for something completely different ... the code. k <- .n_of_nonzero_columns(M) nr_M <- nrow(M) nc_M <- ncol(M) nc_memberships <- sapply(memberships, ncol) if(k <= min(nc_memberships)) { ## Compute the weighted means \bar{M}. M <- .weighted_sum_of_matrices(mapply(function(u, p) u[ , p[seq_len(k)]], memberships, permutations, SIMPLIFY = FALSE), w, nr_M) ## And add dummy classes if necessary. if(k < nc_M) M <- cbind(M, matrix(0, nr_M, nc_M - k)) ## If we always got the same number of classes, we are ## done. Otherwise, equidistribute ... if(k < max(nc_memberships)) M <- pmax(M + (1 - rowSums(M)) / nc_M, 0) return(M) } ## Here comes the general case. ## First, compute the \alpha and \beta. alpha <- rowSums(rep(w, each = k) * mapply(function(p, n) p[seq_len(k)] <= n, permutations, nc_memberships)) ## Alternatively (more literally): ## X <- lapply(permutations, .make_X_from_p) ## alpha1 <- double(length = k) ## for(b in seq_along(permutations)) { ## alpha1 <- alpha1 + ## w[b] * colSums(X[[b]][seq_len(nc_memberships[b]), ]) ## } ## A helper function giving suitably permuted memberships. pmem <- function(u, p) { ## Only matched classes, similar to the one used in value(), ## maybe merge eventually ... v <- matrix(0, nr_M, k) ind <- seq_len(k) ind <- ind[p[ind] <= ncol(u)] if(any(ind)) v[ , ind] <- u[ , p[ind]] v } beta <- .weighted_sum_of_matrices(mapply(pmem, memberships, permutations, SIMPLIFY = FALSE), w, nr_M) ## Alternatively (more literally): ## beta1 <- matrix(0, nr_M, nc_M) ## for(b in seq_along(permutations)) { ## ind <- seq_len(nc_memberships[b]) ## beta1 <- beta1 + ## w[b] * memberships[[b]][, ind] %*% X[[b]][ind, ] ## } ## Compute the weighted means \bar{M}. M <- .cscale(beta, ifelse(alpha > 0, 1 / alpha, 0)) ## Alternatively (see comments for .cscale()): ## M1 <- beta %*% diag(ifelse(alpha > 0, 1 / alpha, 0)) ## And add dummy classes if necessary. if(k < nc_M) M <- cbind(M, matrix(0, nr_M, nc_M - k)) S <- rowSums(M) ## Take care of those rows with row sums < 1. ind <- (S < 1) if(any(ind)) { i_0 <- alpha == 0 if(any(i_0)) M[ind, i_0] <- 1 / sum(i_0) else M[ind, ] <- pmax(M[ind, ] + (1 - S[ind]) / nc_M, 0) } ## Take care of those rows with row sums > 1. ind <- (S > 1) if(any(ind)) { ## Argh. Call solve.QP() for each such i. Alternatively, ## could set up on very large QP, but is this any better? Dmat <- diag(alpha, nc_M) Amat <- t(rbind(rep.int(-1, nc_M), diag(1, nc_M))) bvec <- c(-1, rep.int(0, nc_M)) for(i in which(ind)) M[i, ] <- quadprog::solve.QP(Dmat, alpha * M[i, ], Amat, bvec)$solution } M } memberships <- lapply(clusterings, cl_membership) V_opt <- Inf M_opt <- NULL for(run in seq_along(start)) { if(verbose && (nruns > 1L)) message(gettextf("AOG run: %d", run)) M <- start[[run]] permutations <- lapply(memberships, fit_P, M) old_value <- value(M, permutations, memberships, w) message(gettextf("Iteration: 0 *** value: %g", old_value)) iter <- 1L while(iter <= maxiter) { ## Fit M. M <- fit_M(permutations, memberships, w) ## Fit \{ P_b \}. permutations <- lapply(memberships, fit_P, M) ## Update value. new_value <- value(M, permutations, memberships, w) if(verbose) message(gettextf("Iteration: %d *** value: %g", iter, new_value)) if(abs(old_value - new_value) < reltol * (abs(old_value) + reltol)) break old_value <- new_value iter <- iter + 1L } if(new_value < V_opt) { converged <- (iter <= maxiter) V_opt <- new_value M_opt <- M } if(verbose) message(gettextf("Minimum: %g", V_opt)) } M <- .stochastify(M_opt) ## Seems that M is always kept a k columns ... if not, use ## M <- .stochastify(M_opt[, seq_len(k), drop = FALSE]) rownames(M) <- rownames(memberships[[1L]]) ## Recompute the value, just making sure ... permutations <- lapply(memberships, fit_P, M) meta <- list(objval = value(M, permutations, memberships, w), converged = converged) M <- .cl_membership_from_memberships(M, k, meta) as.cl_partition(M) } ### ** .cl_consensus_partition_GV1 .cl_consensus_partition_GV1 <- function(clusterings, weights, control) .cl_consensus_partition_AOG(clusterings, weights, control, "GV1") ### * .cl_consensus_partition_GV3 .cl_consensus_partition_GV3 <- function(clusterings, weights, control) { ## Use a SUMT to solve ## \| Y - M M' \|_F^2 => min ## where M is a membership matrix and Y = \sum_b w_b M_b M_b'. n <- n_of_objects(clusterings) max_n_of_classes <- max(sapply(clusterings, n_of_classes)) ## Control parameters: ## k, k <- control$k if(is.null(k)) k <- max_n_of_classes ## nruns, nruns <- control$nruns ## start. start <- control$start w <- weights / sum(weights) comemberships <- lapply(clusterings, function(x) { ## No need to force a common k here. tcrossprod(cl_membership(x)) }) Y <- .weighted_sum_of_matrices(comemberships, w, n) ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else { if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } e <- eigen(Y, symmetric = TRUE) ## Use M <- U_k \lambda_k^{1/2}, or random perturbations ## thereof. M <- e$vectors[, seq_len(k), drop = FALSE] * rep(sqrt(e$values[seq_len(k)]), each = n) m <- c(M) start <- c(list(m), replicate(nruns - 1L, m + rnorm(length(m), sd = sd(m) / sqrt(3)), simplify = FALSE)) } y <- c(Y) L <- function(m) sum((y - tcrossprod(matrix(m, n))) ^ 2) P <- .make_penalty_function_membership(n, k) grad_L <- function(m) { M <- matrix(m, n) 4 * c((tcrossprod(M) - Y) %*% M) } grad_P <- .make_penalty_gradient_membership(n, k) out <- sumt(start, L, P, grad_L, grad_P, method = control$method, eps = control$eps, q = control$q, verbose = control$verbose, control = as.list(control$control)) M <- .stochastify(matrix(out$x, n)) rownames(M) <- rownames(cl_membership(clusterings[[1L]])) meta <- list(objval = L(c(M))) M <- .cl_membership_from_memberships(M, k, meta) as.cl_partition(M) } ### * .cl_consensus_partition_soft_symdiff .cl_consensus_partition_soft_symdiff <- function(clusterings, weights, control) { ## Use a SUMT to solve ## \sum_b w_b \sum_{ij} | c_{ij}(b) - c_{ij} | => min ## where C(b) = comembership(M(b)) and C = comembership(M) and M is ## a membership matrix. ## Control parameters: ## gradient, gradient <- control$gradient if(is.null(gradient)) gradient <- TRUE ## k, k <- control$k ## nruns, nruns <- control$nruns ## start. start <- control$start ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } max_n_of_classes <- max(sapply(clusterings, n_of_classes)) if(is.null(k)) k <- max_n_of_classes B <- length(clusterings) n <- n_of_objects(clusterings) w <- weights / sum(weights) comemberships <- lapply(clusterings, function(x) { ## No need to force a common k here. tcrossprod(cl_membership(x)) }) ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else { if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } ## Try using a rank k "root" of the weighted median of the ## comemberships as starting value. Y <- apply(array(unlist(comemberships), c(n, n, B)), c(1, 2), weighted_median, w) e <- eigen(Y, symmetric = TRUE) ## Use M <- U_k \lambda_k^{1/2}, or random perturbations ## thereof. M <- e$vectors[, seq_len(k), drop = FALSE] * rep(sqrt(e$values[seq_len(k)]), each = n) m <- c(M) start <- c(list(m), replicate(nruns - 1L, m + rnorm(length(m), sd = sd(m) / sqrt(3)), simplify = FALSE)) } L <- function(m) { M <- matrix(m, n) C_M <- tcrossprod(M) ## Note that here (as opposed to hard/symdiff) we take soft ## partitions as is without replacing them by their closest hard ## partitions. sum(w * sapply(comemberships, function(C) sum(abs(C_M - C)))) } P <- .make_penalty_function_membership(n, k) if(gradient) { grad_L <- function(m) { M <- matrix(m, n) C_M <- tcrossprod(M) .weighted_sum_of_matrices(lapply(comemberships, function(C) 2 * sign(C_M - C) %*% M), w, n) } grad_P <- .make_penalty_gradient_membership(n, k) } else grad_L <- grad_P <- NULL out <- sumt(start, L, P, grad_L, grad_P, method = control$method, eps = control$eps, q = control$q, verbose = control$verbose, control = as.list(control$control)) M <- .stochastify(matrix(out$x, n)) rownames(M) <- rownames(cl_membership(clusterings[[1L]])) meta <- list(objval = L(c(M))) M <- .cl_membership_from_memberships(M, k, meta) as.cl_partition(M) } ### * .cl_consensus_partition_hard_symdiff .cl_consensus_partition_hard_symdiff <- function(clusterings, weights, control) { ## ## This is mostly duplicated from relations. ## Once this is on CRAN, we could consider having clue suggest ## relations ... ## comemberships <- lapply(clusterings, function(x) { ## Here, we always turn possibly soft partitions to ## their closest hard partitions. ids <- cl_class_ids(x) outer(ids, ids, `==`) ## (Simpler than using tcrossprod() on ## cl_membership().) }) ## Could also create a relation ensemble from the comemberships and ## call relation_consensus(). B <- relations:::.make_fit_relation_symdiff_B(comemberships, weights) k <- control$k control <- control$control ## Note that currently we provide no support for finding *all* ## consensus partitions (but allow for specifying the solver). control$all <- FALSE I <- if(!is.null(k)) { ## ## We could actually get the memberships directly in this case. relations:::fit_relation_LP_E_k(B, k, control) ## } else relations:::fit_relation_LP(B, "E", control) ids <- relations:::get_class_ids_from_incidence(I) names(ids) <- cl_object_names(clusterings) as.cl_hard_partition(ids) } ### * .cl_consensus_hierarchy_cophenetic .cl_consensus_hierarchy_cophenetic <- function(clusterings, weights, control) { ## d <- .weighted_mean_of_object_dissimilarities(clusterings, weights) ## Alternatively: ## as.cl_dendrogram(ls_fit_ultrametric(d, control = control)) control <- c(list(weights = weights), control) as.cl_dendrogram(ls_fit_ultrametric(clusterings, control = control)) } ### * .cl_consensus_hierarchy_manhattan .cl_consensus_hierarchy_manhattan <- function(clusterings, weights, control) { ## Control parameters: ## gradient, gradient <- control$gradient if(is.null(gradient)) gradient <- TRUE ## nruns, nruns <- control$nruns ## start. start <- control$start ## Handle start values and number of runs. if(!is.null(start)) { if(!is.list(start)) { ## Be nice to users. start <- list(start) } } else if(is.null(nruns)) { ## Use nruns only if start is not given. nruns <- 1L } w <- weights / sum(weights) B <- length(clusterings) ultrametrics <- lapply(clusterings, cl_ultrametric) if(B == 1L) return(as.cl_dendrogram(ultrametrics[[1L]])) n <- n_of_objects(ultrametrics[[1L]]) labels <- cl_object_names(ultrametrics[[1L]]) ## We need to do ## ## \sum_b w_b \sum_{i,j} | u_{ij}(b) - u_{ij} | => min ## ## over all ultrametrics u. Let's use a SUMT (for which "gradients" ## can optionally be switched off) ... L <- function(d) { sum(w * sapply(ultrametrics, function(u) sum(abs(u - d)))) ## Could also do something like ## sum(w * sapply(ultrametrics, cl_dissimilarity, d, ## "manhattan")) } P <- .make_penalty_function_ultrametric(n) if(gradient) { grad_L <- function(d) { ## "Gradient" is \sum_b w_b sign(d - u(b)). .weighted_sum_of_vectors(lapply(ultrametrics, function(u) sign(d - u)), w) } grad_P <- .make_penalty_gradient_ultrametric(n) } else grad_L <- grad_P <- NULL if(is.null(start)) { ## Initialize by "random shaking" of the weighted median of the ## ultrametrics. Any better ideas? ## ## Using var(x) / 3 is really L2 ... ## x <- apply(matrix(unlist(ultrametrics), ncol = B), 1, weighted_median, w) start <- replicate(nruns, x + rnorm(length(x), sd = sd(x) / sqrt(3)), simplify = FALSE) } out <- sumt(start, L, P, grad_L, grad_P, method = control$method, eps = control$eps, q = control$q, verbose = control$verbose, control = as.list(control$control)) d <- .ultrametrify(out$x) meta <- list(objval = L(d)) d <- .cl_ultrametric_from_veclh(d, n, labels, meta) as.cl_dendrogram(d) } ### * .cl_consensus_hierarchy_majority .cl_consensus_hierarchy_majority <- function(clusterings, weights, control) { w <- weights / sum(weights) p <- control$p if(is.null(p)) p <- 1 / 2 else if(!is.numeric(p) || (length(p) != 1) || (p < 1 / 2) || (p > 1)) stop("Parameter 'p' must be in [1/2, 1].") classes <- lapply(clusterings, cl_classes) all_classes <- unique(unlist(classes, recursive = FALSE)) gamma <- double(length = length(all_classes)) for(i in seq_along(classes)) gamma <- gamma + w[i] * !is.na(match(all_classes, classes[[i]])) ## Rescale to [0, 1]. gamma <- gamma / max(gamma) maj_classes <- if(p == 1) { ## Strict consensus tree. all_classes[gamma == 1] } else all_classes[gamma > p] attr(maj_classes, "labels") <- attr(classes[[1L]], "labels") ## ## Stop auto-coercing that to dendrograms once we have suitable ways ## of representing n-trees. as.cl_hierarchy(.cl_ultrametric_from_classes(maj_classes)) ## } ### * Utilities ### ** .cl_consensus_method_default .cl_consensus_method_default <- function(type) { switch(type, partition = "SE", hierarchy = "euclidean", NULL) } ### ** .project_to_leading_columns .project_to_leading_columns <- function(x, k) { ## For a given matrix stochastic matrix x, return the stochastic ## matrix y which has columns from k+1 on all zero which is closest ## to x in the Frobenius distance. y <- x[, seq_len(k), drop = FALSE] y <- cbind(pmax(y + (1 - rowSums(y)) / k, 0), matrix(0, nrow(y), ncol(x) - k)) ## (Use the pmax to ensure that entries remain nonnegative.) } ### ** .make_X_from_p .make_X_from_p <- function(p) { ## X matrix corresponding to permutation p as needed for the AO ## algorithms. I.e., x_{ij} = 1 iff j->p(j)=i. X <- matrix(0, length(p), length(p)) i <- seq_along(p) X[cbind(p[i], i)] <- 1 X } ### ** .n_of_nonzero_columns ## ## Could turn this into n_of_classes.matrix(). .n_of_nonzero_columns <- function(x) sum(colSums(x) > 0) ## ### ** .cscale ## ## Move to utilities eventually ... .cscale <- function(A, x) { ## Scale the columns of matrix A by the elements of vector x. ## Formally, A %*% diag(x), but faster. ## Could also use sweep(A, 2, x, "*") rep(x, each = nrow(A)) * A } ## ## .make_penalty_function_membership .make_penalty_function_membership <- function(nr, nc) function(m) { sum(pmin(m, 0) ^ 2) + sum((rowSums(matrix(m, nr)) - 1) ^ 2) } ## .make_penalty_gradient_membership .make_penalty_gradient_membership <- function(nr, nc) function(m) { 2 * (pmin(m, 0) + rep.int(rowSums(matrix(m, nr)) - 1, nc)) } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/validity.R0000644000175100001440000001002314144531021013360 0ustar hornikusers## A slightly polymorphic function, similar to cluster::silhouette() and ## its methods. cl_validity <- function(x, ...) UseMethod("cl_validity") cl_validity.default <- function(x, d, ...) { ## Note that providing methods for classes "cl_partition" and ## "cl_hierarchy" is not good enough ... out <- list() if(.has_object_memberships(x)) { v <- .cl_validity_partition_d_a_f(cl_membership(x), as.matrix(d)) out <- list("Dissimilarity accounted for" = v) } else if(.has_object_dissimilarities(x)) { x <- cl_object_dissimilarities(x) d <- as.dist(d) out <- list("Variance accounted for" = .cl_validity_hierarchy_variance_a_f(x, d), "Deviance accounted for" = .cl_validity_hierarchy_deviance_a_f(x, d)) ## Consider adding e.g. the Agglomerative Coefficient or ## Divisive Coeffcient for more than cluster::agnes() and ## cluster::diana(), respectively. } class(out) <- "cl_validity" out } ## Package cluster: agnes(). cl_validity.agnes <- function(x, ...) { out <- list("Agglomerative coefficient" = x$ac) ## According to the docs, agnes objects always have a diss ## component, but let's be defensive ... if(!is.null(d <- x$diss)) out <- c(out, cl_validity.default(x, d)) class(out) <- "cl_validity" out } ## Package cluster: diana(). cl_validity.diana <- function(x, ...) { out <- list("Divisive coefficient" = x$dc) ## According to the docs, diana objects always have a diss ## component, but let's be defensive ... if(!is.null(d <- x$diss)) out <- c(out, cl_validity.default(x, d)) class(out) <- "cl_validity" out } ## Package clue: (virtual) class "cl_partition". cl_validity.cl_partition <- function(x, ...) cl_validity(.get_representation(x), ...) ## Package clue: class pclust. ## So that this works for all classes extending pclust ... cl_validity.pclust <- function(x, ...) x$validity print.cl_validity <- function(x, ...) { for(nm in names(x)) cat(nm, ": ", x[[nm]], "\n", sep = "") invisible(x) } .cl_validity_partition_d_a_f <- function(m, d) { ## "Dissimilarity accounted for". ## Internal function for computing 1 - a / mean(d), where the ## "average within dissimilarity" a is given by ## \frac{\sum_{i,j} \sum_k m_{ik}m_{jk} d(i,j)} ## {\sum_{i,j} \sum_k m_{ik}m_{jk}} ## where m is the membership matrix and d a *symmetric* matrix of ## dissimilarities. within_sums <- rowSums(sapply(seq_len(ncol(m)), function(k) { z <- m[, k] w <- outer(z, z) c(sum(w * d), sum(w)) })) average_within_d <- within_sums[1L] / within_sums[2L] 1 - average_within_d / mean(d) } .cl_validity_hierarchy_variance_a_f <- function(u, d) { ## *Variance accounted for*. ## See e.g. Hubert, Arabie, & Meulman (2006), The structural ## representation of proximity matrices with MATLAB: ## variance_accounted_for = ## 1 - \frac{\sum_{i < j} (d_{ij} - u_{ij}) ^ 2} ## {\sum_{i < j} (d_{ij} - mean(d)) ^ 2} ## As this can be arbitrarily negative, we cut at 0. max(1 - sum((d - u) ^ 2) / sum((d - mean(d)) ^ 2), 0) } .cl_validity_hierarchy_deviance_a_f <- function(u, d) { ## *Deviance accounted for* (i.e., absolute deviation). ## See e.g. Smith (2001), Constructing ultrametric and additive ## trees based on the ${L}_1$ norm, Journal of Classification. ## deviance_accounted_for = ## 1 - \frac{\sum_{i < j} |d_{ij} - u_{ij}|} ## {\sum_{i < j} |d_{ij} - median(d)|} ## As this can be arbitrarily negative, we cut at 0. max(1 - sum(abs(d - u)) / sum(abs(d - median(d))), 0) } ## Silhouette methods silhouette.cl_partition <- function(x, ...) silhouette(.get_representation(x), ...) silhouette.cl_pclust <- function(x, ...) x$silhouette clue/R/fuzziness.R0000644000175100001440000000435013435044610013606 0ustar hornikuserscl_fuzziness <- function(x, method = NULL, normalize = TRUE) { x <- as.cl_ensemble(x) out <- double(length(x)) ## ## The docs say that we should only have partitions ... attr(out, "description") <- "Fuzziness" class(out) <- "cl_fuzziness" parties <- vapply(x, is.cl_partition, NA) if(!(length(x) || any(parties))) { ## Currently, no fuzzy hierarchies ... return(out) } ## if(!is.function(method)) { builtin_methods <- c("PC", "PE") builtin_method_names <- c("partition coefficient", "partition entropy") if(is.null(method)) ind <- 1 else if(is.na(ind <- pmatch(tolower(method), tolower(builtin_methods)))) stop(gettextf("Value '%s' is not a valid abbreviation for a fuzziness method.", method), domain = NA) method <- paste0(".cl_fuzziness_partition_", builtin_methods[ind]) method_name <- builtin_method_names[ind] if(normalize) method_name <- paste("normalized", method_name) } else method_name <- "user-defined method" out[parties] <- as.numeric(sapply(x[parties], method, normalize)) attr(out, "description") <- paste("Fuzziness using", method_name) out } .cl_fuzziness_partition_PC <- function(x, normalize = TRUE) { ## Dunn's Partition Coefficient, see also ?fanny. ## Note that we normalize differently ... if(!.maybe_is_proper_soft_partition(x) && is.cl_hard_partition(x)) return(1 - normalize) pc <- sum(cl_membership(x) ^ 2) / n_of_objects(x) if(normalize) pc <- (1 - pc) / (1 - 1 / n_of_classes(x)) pc } .cl_fuzziness_partition_PE <- function(x, normalize = TRUE) { ## Bezdek's Partition Entropy. ## Note that we normalize differently ... if(!.maybe_is_proper_soft_partition(x) && is.cl_hard_partition(x)) return(0) M <- cl_membership(x) pe <- - sum(ifelse(M > 0, M * log(M), 0)) / n_of_objects(x) if(normalize) pe <- pe / log(n_of_classes(x)) pe } print.cl_fuzziness <- function(x, ...) { cat(attr(x, "description"), ":\n", sep = "") print(as.vector(x), ...) invisible(x) } clue/R/membership.R0000644000175100001440000001737311633352676013726 0ustar hornikusers### * cl_membership ## Get the class membership matrix from a partition. ## ## We could use sparse matrices for the memberships of hard partitions. ## Not sure if this is really that important, though, as we typically ## use memberships in a context where dense matrices (memberships of ## soft partitions) occur. ## ## ## Currently, the number of classes to be used for the memberships must ## not be less than the number of classes in the partition. We might ## eventually change this so that "optimal" collapsing of classes is ## performed (but note that optimality needs to be relative to some ## dissimilarity measure) ... ## However, from the discussion of the second method in Gordon and Vichi ## (2001) we note that whereas optimal assignment is "simple", optimal ## collapsing (equivalent to partitioning into an arbitrary number of ## partitions) is of course very hard. ## cl_membership <- function(x, k = n_of_classes(x)) { if(k < n_of_classes(x)) stop("k cannot be less than the number of classes in x.") UseMethod("cl_membership") } ## Default method. cl_membership.default <- function(x, k = n_of_classes(x)) .cl_membership_from_class_ids(cl_class_ids(x), k) ## Package stats: kmeans() (R 2.1.0 or better). cl_membership.kmeans <- cl_membership.default ## Package cluster: clara(), fanny(), and pam() give objects of the ## respective class inheriting from class "partition". cl_membership.fanny <- function(x, k = n_of_classes(x)) .cl_membership_from_memberships(x$membership, k) cl_membership.partition <- cl_membership.default ## Package cclust: cclust(). cl_membership.cclust <- cl_membership.default ## Package e1071: cmeans() gives objects of class "fclust". cl_membership.fclust <- cl_membership.fanny ## Package e1071: cshell(). cl_membership.cshell <- cl_membership.fanny ## Package e1071: bclust(). cl_membership.bclust <- cl_membership.default ## Package flexmix: class "flexmix". ## ## We used to be able to call flexmix::posterior(), but this now only ## has S4 methods for modeltools::posterior() S4 generic. Let's call ## this one, and hope that flexmix has been loaded ... ## cl_membership.flexmix <- function(x, k = n_of_classes(x)) .cl_membership_from_memberships(modeltools::posterior(x), k) ## Package mclust: Mclust(). cl_membership.Mclust <- function(x, k = n_of_classes(x)) .cl_membership_from_memberships(x$z, k) ## Package clue: Memberships. cl_membership.cl_membership <- function(x, k = n_of_classes(x)) .cl_membership_from_memberships(x, k) ## (Note: we cannot simply return x in case k equals n_of_classes(x), ## because ncol(x) might be different.) ## Package clue: pclust(). cl_membership.pclust <- function(x, k = n_of_classes(x)) { ## We should really have a suitable "sparse matrix" class for ## representing the memberships of hard partitions. In case we ## decide not to fill the membership "slot" for such: if(is.null(m <- x$membership)) .cl_membership_from_class_ids(x$cluster, k) else .cl_membership_from_memberships(m, k) } ## Package clue: (virtual) class "cl_partition". cl_membership.cl_partition <- function(x, k = n_of_classes(x)) cl_membership(.get_representation(x), k) ## Package movMF: class "movMF". cl_membership.movMF <- function(x, k = n_of_classes(x)) .cl_membership_from_memberships(x$P, k) ### * .make_cl_membership ## A low-level common creator. .make_cl_membership <- function(x, n_of_classes, is_cl_hard_partition, meta = NULL) { attr(x, "n_of_classes") <- n_of_classes attr(x, "is_cl_hard_partition") <- is_cl_hard_partition attr(x, "meta") <- meta class(x) <- "cl_membership" x } ### * .cl_membership_from_class_ids .cl_membership_from_class_ids <- function(x, k = NULL, meta = NULL) { x <- factor(x) n_of_objects <- length(x) n_of_classes <- nlevels(x) if(is.null(k)) k <- n_of_classes else if(k < n_of_classes) stop("k cannot be less than the number of classes in x.") ## ## Should really use a sparse encoding of this ... M <- matrix(0, n_of_objects, k) ## (Could also use .one_entry_per_column(M, as.numeric(x)) <- 1 for ## the time being.) M[cbind(seq_len(n_of_objects), as.numeric(x))] <- 1 ## But note that we also need to handle NAs ... M[is.na(x), ] <- NA ## if(nlevels(x) == k) colnames(M) <- levels(x) if(!is.null(nm <- names(x))) rownames(M) <- nm .make_cl_membership(M, n_of_classes, TRUE, meta) } ### * .cl_membership_from_memberships .cl_membership_from_memberships <- function(x, k = NULL, meta = NULL) { ## ## Dropping and re-filling of ## zero columns in case k is given may ## seem unnecessary, but really canonicalizes by moving zero columns ## last ... ## x <- x[ , colSums(x, na.rm = TRUE) > 0, drop = FALSE] n_of_classes <- ncol(x) if(!is.null(k)) { if(k < n_of_classes) stop("k cannot be less than the number of classes in x.") if(k > n_of_classes) { ## Fill up with zero columns. x <- cbind(x, matrix(0, nrow(x), k - n_of_classes)) ## Handle NAs if necessary. x[apply(is.na(x), 1, any), ] <- NA } } .make_cl_membership(x, n_of_classes, all(rowSums(x == 1, na.rm = TRUE) > 0), meta) } ### * as.cl_membership as.cl_membership <- function(x) UseMethod("as.cl_membership") as.cl_membership.default <- function(x) { if(inherits(x, "cl_membership")) x else if(is.atomic(x)) .cl_membership_from_class_ids(x) else cl_membership(x) } as.cl_membership.matrix <- function(x) .cl_membership_from_memberships(x) ### * .memberships_from_cross_dissimilarities .memberships_from_cross_dissimilarities <- function(d, power = 2) { ## For a given matrix of cross-dissimilarities [d_{bj}], return a ## matrix [u_{bj}] such that \sum_{b,j} u_{bj}^p d_{bj}^q => min! ## under the constraint that u is a stochastic matrix. ## If only one power is given, it is taken as p, with q as 1. ## ## This returns a plain matrix of membership values and not a ## cl_membership object (so that it does not deal with possibly ## dropping or re-introducing unused classes). ## exponent <- if(length(power) == 1L) 1 / (1 - power) else power[2L] / (1 - power[1L]) u <- matrix(0, nrow(d), ncol(d)) zero_incidences <- !(d > 0) n_of_zeroes <- rowSums(zero_incidences) if(any(ind <- (n_of_zeroes > 0))) u[ind, ] <- zero_incidences[ind, , drop = FALSE] / n_of_zeroes[ind] if(any(!ind)) { ## Compute d_{bj}^e / \sum_k d_{bk}^e without overflow from very ## small d_{bj} values. d <- exponent * log(d[!ind, , drop = FALSE]) d <- exp(d - d[cbind(seq_len(nrow(d)), max.col(d))]) u[!ind, ] <- d / rowSums(d) } u } ### * print.cl_membership print.cl_membership <- function(x, ...) { writeLines("Memberships:") print(matrix(as.vector(x), nrow = nrow(x), dimnames = dimnames(x)), ...) invisible(x) } ### .has_object_memberships ## Be nice to users when computing proximities: all measures for ## "partitions" we currently consider really only assume that we can ## compute memberships and/or class ids. ## Note that the cl_membership() default method works for cl_class_ids. .has_object_memberships <- function(x) (is.cl_partition(x) || inherits(x, "cl_membership") || inherits(x, "cl_class_ids")) ### * .stochastify .stochastify <- function(x) { ## Try to ensure that a stochastic matrix is returned. x <- pmax(x, 0) x / rowSums(x) } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/classes.R0000644000175100001440000000460613434542602013212 0ustar hornikuserscl_classes <- function(x) UseMethod("cl_classes") cl_classes.default <- function(x) { ## Be nice to users ... if(is.cl_partition(x)) cl_classes(as.cl_partition(x)) else if(is.cl_dendrogram(x)) cl_classes(as.cl_dendrogram(x)) else stop("Can only determine classes of partitions or hierarchies.") } cl_classes.cl_partition <- function(x) { n <- n_of_objects(x) out <- split(seq_len(n), cl_class_ids(x)) class(out) <- c("cl_classes_of_partition_of_objects", "cl_classes_of_objects") attr(out, "n_of_objects") <- n attr(out, "labels") <- cl_object_labels(x) out } cl_classes.cl_hierarchy <- function(x) { ## Assume a valid hierarchy/dendrogram. x <- as.hclust(x) n <- n_of_objects(x) labels <- seq_len(n) ## Only use the "maximal" partitions for each height (relevant in ## case of non-binary trees). groups <- cutree(x, h = unique(c(0, x$height))) ## Give a list with the (unique) sets of numbers of the objects. ## Note that objects may already be merged at height zero. out <- unique(unlist(c(as.list(labels), lapply(split(groups, col(groups)), function(k) split(labels, k))), recursive = FALSE, use.names = FALSE)) ## Preserve labels if possible, and re-order according to ## cardinality. out <- out[order(lengths(out))] class(out) <- c("cl_classes_of_hierarchy_of_objects", "cl_classes_of_objects") attr(out, "n_of_objects") <- n attr(out, "labels") <- cl_object_labels(x) out } ## Be nice to users of ultrametric fitters ... which should really fit ## dendrograms (which inherit from hierarchies). cl_classes.cl_ultrametric <- cl_classes.cl_hierarchy print.cl_classes_of_partition_of_objects <- function(x, ...) { labels <- attr(x, "labels") y <- lapply(x, function(i) paste(labels[i], collapse = ", ")) writeLines(formatDL(names(x), sprintf("{%s}", unlist(y)), style = "list", ...)) invisible(x) } print.cl_classes_of_hierarchy_of_objects <- function(x, ...) { labels <- attr(x, "labels") y <- lapply(x, function(i) paste(labels[i], collapse = ", ")) y <- strwrap(sprintf("{%s},", unlist(y)), exdent = 2) y[length(y)] <- sub(",$", "", y[length(y)]) writeLines(y) invisible(x) } clue/R/lsap.R0000644000175100001440000000147012036747337012521 0ustar hornikuserssolve_LSAP <- function(x, maximum = FALSE) { if(!is.matrix(x) || any(x < 0)) stop("x must be a matrix with nonnegative entries.") nr <- nrow(x) nc <- ncol(x) if(nr > nc) stop("x must not have more rows than columns.") if(nc > nr) x <- rbind(x, matrix(2 * sum(x), nc - nr, nc)) if(maximum) x <- max(x) - x storage.mode(x) <- "double" out <- .C(C_solve_LSAP, x, as.integer(nc), p = integer(nc))$p + 1 out <- out[seq_len(nr)] class(out) <- "solve_LSAP" out } print.solve_LSAP <- function(x, ...) { writeLines(c("Optimal assignment:", gsub("x", " ", strwrap(paste(seq_along(x), x, sep = "x=>x", collapse = ", "))))) invisible(x) } clue/R/hierarchy.R0000644000175100001440000002341713036464014013532 0ustar hornikusers### * is.cl_hierarchy ## Determine whether an object is a hierarchy. ## Note that hierarchies are n-trees, which can naturally be represented ## by their classes (as done via cl_classes()) or internal ultrametric ## obtained by assigning height one to all splits (as done by ## .cl_ultrametric_from_classes()). ## We typically used the latter, but note that this is an *internal* ## reprsentation. ## User-level, cl_dendrogram objects are indexed hierarchies, and ## cl_hierarchy objects are n-trees. The latter can be "converted" into ## the former (using height one splits) via as.cl_dendrogram(). is.cl_hierarchy <- function(x) UseMethod("is.cl_hierarchy") ## Default method. is.cl_hierarchy.default <- .false ## Package stats: hclust(). is.cl_hierarchy.hclust <- function(x) !is.unsorted(x$height) ## Package cluster: agnes() and diana() give objects inheriting from ## class "twins". is.cl_hierarchy.twins <- .true ## Package cluster: mona(). is.cl_hierarchy.mona <- .true ## Package ape: class "phylo". is.cl_hierarchy.phylo <- function(x) ape::is.ultrametric(x) ## Package clue: (virtual) class "cl_hierarchy". ## Note that "raw" cl_ultrametric objects are *not* hierarchies, as ## these are meant for numeric computations. ## ## Is this really a good idea? ## We can as.hclust() a cl_dendrogram and then it is a cl_hierarchy ... ## is.cl_hierarchy.cl_hierarchy <- .true ### * as.cl_hierarchy ## Note that cl_hierarchy conceptually is a virtual class, so there are ## no prototypes and no cl_hierarchy() creator. .cl_hierarchy_classes <- "cl_hierarchy" as.cl_hierarchy <- function(x) { if(is.cl_hierarchy(x)) { if(!inherits(x, "cl_hierarchy")) .make_container(x, .cl_hierarchy_classes) else x } else .make_container(as.cl_ultrametric(x), .cl_hierarchy_classes) } ### * print.cl_hierarchy print.cl_hierarchy <- function(x, ...) .print_container(x, "cl_hierarchy", ...) ### * Complex.cl_hierarchy ## No Complex() for any kind of hierarchy. Complex.cl_hierarchy <- function(z) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ### * Math.cl_hierarchy ## No Math() for any kind of hierarchy. Math.cl_hierarchy <- function(x, ...) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ### * Ops.cl_hierarchy Ops.cl_hierarchy <- function(e1, e2) { if(nargs() == 1L) stop(gettextf("Unary '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ## Only comparisons are supprorted. if(!(as.character(.Generic) %in% c("<", "<=", ">", ">=", "==", "!="))) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) if(n_of_objects(e1) != n_of_objects(e2)) stop("Hierarchies must have the same number of objects.") c1 <- cl_classes(e1) c2 <- cl_classes(e2) switch(.Generic, "<=" = all(is.finite(match(c1, c2))), "<" = all(is.finite(match(c1, c2))) && any(is.na(match(c2, c1))), ">=" = all(is.finite(match(c2, c1))), ">" = all(is.finite(match(c2, c1))) && any(is.na(match(c1, c2))), "==" = all(is.finite(match(c1, c2))) && all(is.finite(match(c2, c1))), "!=" = any(is.na(match(c1, c2))) || any(is.na(match(c2, c1)))) } ### * Summary.cl_hierarchy ## ## This is really the same as Summary.cl_partition(). ## Summary.cl_hierarchy <- function(..., na.rm = FALSE) { ok <- switch(.Generic, max = , min = , range = TRUE, FALSE) if(!ok) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) args <- list(...) switch(.Generic, "min" = cl_meet(cl_ensemble(list = args)), "max" = cl_join(cl_ensemble(list = args)), "range" = { cl_ensemble(min = cl_meet(cl_ensemble(list = args)), max = cl_join(cl_ensemble(list = args))) }) } ### * as.hclust.cl_hierarchy as.hclust.cl_hierarchy <- function(x, ...) as.hclust(.get_representation(x), ...) ### * is.cl_dendrogram ## ## Once we have cl_dendrogram testing, we can simplify cl_hierarchy ## testing. E.g., ## is.cl_hierachy.default <- is.cl_dendrogram ## should be ok, and we can add cl_hierarchy predicates for hierarchies ## which are not dendrograms on top of that. ## is.cl_dendrogram <- function(x) UseMethod("is.cl_dendrogram") ## Default method. is.cl_dendrogram.default <- .false ## Package stats: hclust(). is.cl_dendrogram.hclust <- function(x) !is.unsorted(x$height) ## Package cluster: agnes() and diana() give objects inheriting from ## class "twins". is.cl_dendrogram.twins <- .true ## Package cluster: mona(). is.cl_dendrogram.mona <- .true ## Package ape: class "phylo". is.cl_dendrogram.phylo <- function(x) ape::is.ultrametric(x) ## (We could also support ape's class "matching" via coercion to class ## "phylo".) ## Package clue: (virtual) class "cl_dendrogram". is.cl_dendrogram.cl_dendrogram <- .true ### * as.cl_dendrogram .cl_dendrogram_classes <- c("cl_dendrogram", "cl_hierarchy") as.cl_dendrogram <- function(x) { if(is.cl_dendrogram(x)) { if(!inherits(x, "cl_dendrogram")) .make_container(x, .cl_dendrogram_classes) else x } else .make_container(as.cl_ultrametric(x), .cl_dendrogram_classes) } ### * print.cl_dendrogram print.cl_dendrogram <- function(x, ...) .print_container(x, "cl_dendrogram", ...) ### * plot.cl_dendrogram plot.cl_dendrogram <- function(x, ...) plot(cl_ultrametric(.get_representation(x)), ...) ### * Group methods for cl_dendrogram objects. Ops.cl_dendrogram <- function(e1, e2) { if(nargs() == 1L) stop(gettextf("Unary '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ## Only comparisons are supprorted. if(!(as.character(.Generic) %in% c("<", "<=", ">", ">=", "==", "!="))) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) u1 <- cl_ultrametric(e1) u2 <- cl_ultrametric(e2) if(length(u1) != length(u2)) stop("Dendrograms must have the same number of objects.") switch(.Generic, "<=" = all(u1 <= u2), "<" = all(u1 <= u2) && any(u1 < u2), ">=" = all(u1 >= u2), ">" = all(u1 >= u2) && any(u1 > u2), "==" = all(u1 == u2), "!=" = any(u1 != u2)) } ### * Summary.cl_dendrogram ## ## This is really the same as Summary.cl_hierarchy() ... ## We cannot really call the poset specific internal meet and join ## functions from here as e.g. max(D, H) (D a dendrogram, H an n-tree) ## should use the n-tree poset functions ... ## However, dispatch for cl_dendrogram should not be needed if we also ## dispatch on cl_hierarchy ... ## ## Summary.cl_dendrogram <- ## function(..., na.rm = FALSE) ## { ## ok <- switch(.Generic, max = , min = , range = TRUE, FALSE) ## if(!ok) ## stop(gettextf("Generic '%s' not defined for \"%s\" objects.", ## .Generic, .Class)) ## args <- list(...) ## switch(.Generic, ## "min" = cl_meet(cl_ensemble(list = args)), ## "max" = cl_join(cl_ensemble(list = args)), ## "range" = { ## cl_ensemble(min = cl_meet(cl_ensemble(list = args)), ## max = cl_join(cl_ensemble(list = args))) ## }) ## } ### * as.hclust.cl_dendrogram ## ## This is really the same as as.hclust.cl_hierarchy() ... ## Dispatch for cl_dendrogram should not be needed if we also dispatch ## on cl_hierarchy ... ## ## as.hclust.cl_dendrogram <- ## function(x, ...) ## as.hclust(.get_representation(x), ...) ### ** cut.cl_dendrogram ## Not perfect as this perhaps return something more "classed" in the ## spirit of clue ... cut.cl_dendrogram <- function(x, ...) cutree(as.hclust(x), ...) ### * Utilities ## To turn a mona object into a cl_dendrogram, we need to be able to ## compute its associated ultrametric. Hence, provide a cophenetic() ## method for mona objects ... cophenetic.mona <- function(x) { no <- length(x$order) ns <- max(x$step) + 1 m <- matrix(NA, no, no) FOO <- function(ind, step, s) { if(length(ind) <= 1) return() grp <- c(0, cumsum(step == s)) ind <- split(ind, grp) len <- length(ind) for(a in seq_len(len)) { for(b in seq_len(a - 1L)) { ## Need both as we currently cannot assume that the ## indices are sorted. Alternatively, work with the ## sequence from one to the number of objects, and ## reorder at the end ... m[ind[[a]], ind[[b]]] <<- s m[ind[[b]], ind[[a]]] <<- s } } ind <- ind[lengths(ind) > 1L] pos <- which(step == s) step <- split(step[-pos], grp[-1][-pos]) if(is.null(step)) return() for(a in seq_along(ind)) FOO(ind[[a]], step[[a]], s + 1) } FOO(x$order, x$step, 1) m[is.na(m)] <- ns m <- ns - m rownames(m) <- rownames(x$data) as.dist(m) } ## And while we're at it ... ## (Of course, as.hclust() should really "know" that a cophenetic() ## method is available ...) as.hclust.mona <- function(x, ...) hclust(cophenetic(x), "single") ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/ensemble.R0000644000175100001440000001567013435044575013361 0ustar hornikuserscl_ensemble <- function(..., list = NULL) { clusterings <- c(list(...), list) if(!length(clusterings)) { ## Return an empty cl_ensemble. ## In this case, we cannot additionally know whether it contains ## partitions or hierarchies ... attr(clusterings, "n_of_objects") <- as.integer(NA) class(clusterings) <- "cl_ensemble" return(clusterings) } ## Previously, we used to require that the elements of the ensemble ## either all be partitions, or all be hierarchies. We no longer do ## this, as it makes sense to also allow e.g. object dissimilarities ## (raw "dist" objects or additive distances) as elements (e.g., ## when computing proximities), and it is rather cumbersome to ## decide in advance which combinations of elements might be useful ## and hence should be allowed. All we enforce is that all elements ## correspond to the same number of objects (as we typically cannot ## verify that they relate to the *same* objects). For "pure" ## ensembles of partitions or hierarchies we add additional class ## information. if(all(vapply(clusterings, is.cl_partition, NA))) class(clusterings) <- c("cl_partition_ensemble", "cl_ensemble") else if(all(vapply(clusterings, is.cl_dendrogram, NA))) class(clusterings) <- c("cl_dendrogram_ensemble", "cl_hierarchy_ensemble", "cl_ensemble") else if(all(vapply(clusterings, is.cl_hierarchy, NA))) class(clusterings) <- c("cl_hierarchy_ensemble", "cl_ensemble") else class(clusterings) <- "cl_ensemble" n <- sapply(clusterings, n_of_objects) if(any(diff(n))) stop("All elements must have the same number of objects.") attr(clusterings, "n_of_objects") <- as.integer(n[1L]) clusterings } is.cl_ensemble <- function(x) inherits(x, "cl_ensemble") ## ## In the old days, kmeans() results were unclassed lists, hence such ## objects were taken as representing a single clustering. Nowadays, we ## take these as lists of clusterings. as.cl_ensemble <- function(x) { if(is.cl_ensemble(x)) x else if(is.list(x) && !is.object(x)) cl_ensemble(list = x) else cl_ensemble(x) } ## c.cl_ensemble <- function(..., recursive = FALSE) { clusterings <- unlist(lapply(list(...), as.cl_ensemble), recursive = FALSE) cl_ensemble(list = clusterings) } "[.cl_ensemble" <- function(x, i) { ## Make subscripting empty ensembles a noop. if(length(x) == 0L) return(x) cl_ensemble(list = NextMethod("[")) } rep.cl_ensemble <- function(x, times, ...) cl_ensemble(list = NextMethod("rep")) print.cl_partition_ensemble <- function(x, ...) { msg <- sprintf(ngettext(length(x), "An ensemble of %d partition of %d objects.", "An ensemble of %d partitions of %d objects."), length(x), n_of_objects(x)) writeLines(strwrap(msg)) invisible(x) } Summary.cl_partition_ensemble <- function(..., na.rm = FALSE) { ok <- switch(.Generic, max = , min = , range = TRUE, FALSE) if(!ok) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) args <- list(...) ## Combine the given partition ensembles. x <- do.call(c, args) switch(.Generic, "min" = cl_meet(x), "max" = cl_join(x), "range" = cl_ensemble(min = cl_meet(x), max = cl_join(x))) } print.cl_dendrogram_ensemble <- function(x, ...) { msg <- sprintf(ngettext(length(x), "An ensemble of %d dendrogram of %d objects.", "An ensemble of %d dendrograms of %d objects."), length(x), n_of_objects(x)) writeLines(strwrap(msg)) invisible(x) } print.cl_hierarchy_ensemble <- function(x, ...) { msg <- sprintf(ngettext(length(x), "An ensemble of %d hierarchy of %d objects.", "An ensemble of %d hierarchies of %d objects."), length(x), n_of_objects(x)) writeLines(strwrap(msg)) invisible(x) } print.cl_ensemble <- function(x, ...) { writeLines(sprintf(ngettext(length(x), "An ensemble with %d element.", "An ensemble with %d elements."), length(x))) invisible(x) } plot.cl_ensemble <- function(x, ..., main = NULL, layout = NULL) { if(!is.cl_ensemble(x)) stop("Wrong class.") ## What we can definitely plot is are cl_addtree, cl_dendrogram and ## cl_ultrametric objects. (We could also add simple methods for ## plotting raw dissimilarities, but of course seriation::dissplot() ## would be the thing to use.) What we cannot reasonably plot is ## partitions (in particular, as these do not know about the ## underlying dissimilarities. But then we could perhaps provide ## silhoutte plots etc for ensembles of partitions ... ## ## Think about this. ## ## So let us check for the things we can plot. ## (Note that currently there is neither is.cl_ultrametric() nor ## is.cl_addtree().) ok <- vapply(x, function(e) (is.cl_dendrogram(e) || inherits(e, c("cl_addtree", "cl_ultrametric"))), NA) if(!all(ok)) stop(gettextf("Plotting not available for elements %s of the ensemble.", paste(which(!ok), collapse = " ")), domain = NA) ## Prefer dendrogram plot methods to those for hclust objects. ind <- vapply(x, is.cl_dendrogram, NA) if(any(ind)) x[ind] <- lapply(x, as.cl_dendrogram) ## Now the usual layouting ... same as for plotting relation ## ensembles. ## Number of elements. n <- length(x) ## Layout. byrow <- TRUE if(is.null(layout)) { nc <- ceiling(sqrt(n)) nr <- ceiling(n / nc) } else { layout <- c(as.list(layout), byrow)[seq_len(3)] if(is.null(names(layout))) names(layout) <- c("nr", "nc", "byrow") nr <- layout[["nr"]] nc <- layout[["nc"]] byrow <- layout[["byrow"]] } op <- if(byrow) par(mfrow = c(nr, nc)) else par(mfcol = c(nr, nc)) on.exit(par(op)) ## Try recycling main (might want the same for others as well). if(!is.list(main)) { main <- if(is.null(main)) vector("list", length = n) else rep.int(as.list(main), n) } for(i in seq_along(x)) plot(x[[i]], main = main[[i]], ...) } unique.cl_ensemble <- function(x, incomparables = FALSE, ...) cl_ensemble(list = NextMethod("unique")) .cl_ensemble_type <- function(x) { if(inherits(x, "cl_partition_ensemble")) "partition" else if(inherits(x, "cl_hierarchy_ensemble")) "hierarchy" else NULL } clue/R/dissimilarity.R0000644000175100001440000004506114335746635014460 0ustar hornikusers### * cl_dissimilarity cl_dissimilarity <- function(x, y = NULL, method = "euclidean", ...) { x <- as.cl_ensemble(x) is_partition_ensemble <- (inherits(x, "cl_partition_ensemble") || all(vapply(x, .has_object_memberships, NA))) ## Be nice. if(is.character(y) || is.function(y)) { method <- y y <- NULL } if(is.function(method)) method_name <- "user-defined method" else { if(!inherits(method, "cl_dissimilarity_method")) { ## Get the method definition and description from the ## registry. type <- ifelse(is_partition_ensemble, "partition", "hierarchy") method <- get_cl_dissimilarity_method(method, type) } method_name <- method$description method <- method$definition } if(!is.null(y)) { y <- as.cl_ensemble(y) is_partition_ensemble_y <- (inherits(y, "cl_partition_ensemble") || all(vapply(x, .has_object_memberships, NA))) if(!identical(is_partition_ensemble, is_partition_ensemble_y)) stop("Cannot mix partitions and hierarchies.") if(n_of_objects(x) != n_of_objects(y)) stop("All clusterings must have the same number of objects.") ## Build a cross-proximity object of cross-dissimilarities. d <- matrix(0, length(x), length(y)) for(j in seq_along(y)) d[, j] <- sapply(x, method, y[[j]], ...) dimnames(d) <- list(names(x), names(y)) return(cl_cross_proximity(d, method_name, class = "cl_cross_dissimilarity")) } ## Otherwise, build a proximity object of dissimilarities. n <- length(x) d <- vector("list", length = n - 1L) ind <- seq_len(n) while(length(ind) > 1L) { j <- ind[1L] ind <- ind[-1L] d[[j]] <- sapply(x[ind], method, x[[j]], ...) } cl_proximity(unlist(d), method_name, labels = names(x), size = n, class = c("cl_dissimilarity", "cl_proximity", "dist")) } ### ** .cl_dissimilarity_partition_euclidean .cl_dissimilarity_partition_euclidean <- function(x, y) { k <- max(n_of_classes(x), n_of_classes(y)) M_x <- cl_membership(x, k) M_y <- cl_membership(y, k) ## Match classes from conforming memberships. ind <- solve_LSAP(crossprod(M_x, M_y), maximum = TRUE) sqrt(sum((M_x - M_y[, ind]) ^ 2)) } ### ### ** .cl_dissimilarity_partition_manhattan .cl_dissimilarity_partition_manhattan <- function(x, y) { k <- max(n_of_classes(x), n_of_classes(y)) M_x <- cl_membership(x, k) M_y <- cl_membership(y, k) C <- .cxdist(M_x, M_y, "manhattan") ind <- solve_LSAP(C) sum(C[cbind(seq_along(ind), ind)]) } ### ** .cl_dissimilarity_partition_comemberships .cl_dissimilarity_partition_comemberships <- function(x, y) { ## We used to have the straightforward ## C_x <- tcrossprod(cl_membership(x)) # M_x M_x' ## C_y <- tcrossprod(cl_membership(y)) # M_y M_y' ## sum((C_x - C_y) ^ 2) / n_of_objects(x) ^ 2 ## But note that ## \| AA' - BB' \|^2 ## = tr((AA' - BB')'(AA' - BB') ## = tr(A'A A'A) - 2 tr(A'B B'A) + tr(B'B B'B) ## = \| A'A \|^2 - 2 \| A'B \|^2 + \| B'B \|^2 ## which can be computed much more efficiently as all involved cross ## product matrices are "small" ... k <- max(n_of_classes(x), n_of_classes(y)) M_x <- cl_membership(x, k) M_y <- cl_membership(y, k) sqrt(sum(crossprod(M_x) ^ 2) - 2 * sum(crossprod(M_x, M_y) ^ 2) + sum(crossprod(M_y) ^ 2)) } ### ** .cl_dissimilarity_partition_symdiff .cl_dissimilarity_partition_symdiff <- function(x, y) { ## Cardinality of the symmetric difference of the partitions ## regarded as binary equivalence relations, i.e., the number of ## discordant pairs. ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) n <- n_of_objects(x) .cl_dissimilarity_partition_Rand(x, y) * choose(n, 2) } ### ** .cl_dissimilarity_partition_Rand .cl_dissimilarity_partition_Rand <- function(x, y) { ## Handle soft partitions using the corresponding hard ones. ## (At least, for the time being.) 1 - .cl_agreement_partition_Rand(x, y) } ### ** .cl_dissimilarity_partition_GV1 .cl_dissimilarity_partition_GV1 <- function(x, y) { k_x <- n_of_classes(x) k_y <- n_of_classes(y) M_x <- cl_membership(x, k_x) M_y <- cl_membership(y, k_y) C <- outer(colSums(M_x ^ 2), colSums(M_y ^ 2), `+`) - 2 * crossprod(M_x, M_y) if(k_x < k_y) C <- rbind(C, matrix(0, nrow = k_y - k_x, ncol = k_y)) else if(k_x > k_y) C <- cbind(C, matrix(0, nrow = k_x, ncol = k_x - k_y)) ind <- solve_LSAP(C) sqrt(sum(C[cbind(seq_along(ind), ind)])) ## (Note that this sum really only includes matched non-dummy ## classes.) } ### ** .cl_dissimilarity_partition_BA_A .cl_dissimilarity_partition_BA_A <- function(x, y) { .cl_dissimilarity_partition_manhattan(as.cl_hard_partition(x), as.cl_hard_partition(y)) / 2 ## Could to this more efficiently, of course ... } ### ** .cl_dissimilarity_partition_BA_C .cl_dissimilarity_partition_BA_C <- function(x, y) { n_of_classes(x) + n_of_classes(y) - 2 * n_of_classes(cl_join(x, y)) } ### ** .cl_dissimilarity_partition_BA_D .cl_dissimilarity_partition_BA_D <- .cl_dissimilarity_partition_Rand ### ** .cl_dissimilarity_partition_BA_E .cl_dissimilarity_partition_BA_E <- function(x, y) { z <- table(cl_class_ids(x), cl_class_ids(y)) z <- z / sum(z) ## Average mutual information between the partitions. y <- outer(rowSums(z), colSums(z)) i <- which((z > 0) & (y > 0)) I <- sum(z[i] * log(z[i] / y[i])) ## Entropy of meet(x, y). i <- which(z > 0) H <- - sum(z[i] * log(z[i])) 1 - I / H } ### ** .cl_dissimilarity_partition_VI .cl_dissimilarity_partition_VI <- function(x, y, weights = 1) { ## Variation of information for general "soft clusterings", cf ## Section 5.2. in Meila (2002). weights <- rep_len(weights, n_of_objects(x)) weights <- weights / sum(weights) M_x <- cl_membership(x) ## Weighted marginal distribution of x: m_x <- colSums(weights * M_x) M_y <- cl_membership(y) ## Weighted marginal distribution of y: m_y <- colSums(weights * M_y) gamma <- crossprod(weights * M_x, M_y) delta <- outer(m_x, m_y) ## Entropy of x: H_x <- - sum(m_x * log(ifelse(m_x > 0, m_x, 1))) ## Entropy of y: H_y <- - sum(m_y * log(ifelse(m_y > 0, m_y, 1))) ## VI is H_x + H_y minus twice the (weighted) joint information. i <- which((gamma > 0) & (delta > 0)) H_x + H_y - 2 * sum(gamma[i] * log(gamma[i] / delta[i])) } ### ** .cl_dissimilarity_partition_Mallows .cl_dissimilarity_partition_Mallows <- function(x, y, p = 1, alpha = NULL, beta = NULL) { ## Currently, no "real" primal-dual solver for minimum cost flow ## problems, and lpSolve::lp.transport() seems to work only for ## integer bounds. Hence, rather than using ## ## C <- .cxdist(cl_membership(x), cl_membership(y), ## "minkowski", p) ^ p ## n_x <- nrow(C) ## n_y <- ncol(C) ## if(is.null(alpha)) ## alpha <- rep.int(1 / n_x, n_x) ## else { ## alpha <- rep_len(alpha, n_x) ## alpha <- alpha / sum(alpha) ## } ## ## etc right away, ensure a square cost matrix so that we can have ## integer bounds for at least the default case. k <- max(n_of_classes(x), n_of_classes(y)) M_x <- cl_membership(x, k) M_y <- cl_membership(y, k) C <- .cxdist(M_x, M_y, "minkowski", p) ^ p if(is.null(alpha)) alpha <- rep.int(1, k) if(is.null(beta)) beta <- rep.int(1, k) lpSolve::lp.transport(C, "min", rep.int("==", k), alpha, rep.int("==", k), beta, integers = NULL)$objval ^ (1 / p) } ### ** .cl_dissimilarity_partition_CSSD .cl_dissimilarity_partition_CSSD <- function(x, y, L = NULL, alpha = NULL, beta = NULL, ...) { ## Cluster Similarity Sensitive Distance. ## Reference: D. Zhou, J. Li and H. Zha (2005), ## A new Mallows distance based metric for comparing clusterings. ## See .cl_dissimilarity_partition_Mallows() re solving cost flow ## problems. ## Dissimilarity is defined by minimizing ## \sum_{k,l} (1 - 2 w_{kl} / (alpha_k + beta_l)) L_{kl} ## where ## L_{kl} = \sum_i m_{x;ik} m_{y;il} distance(p_{x;k}, p_{y;l}) ## with m and p the memberships and prototypes, respectively. ## If we get matrices of prototypes, use .rxdist; otherwise, the ## user needs to specify an L function or matrix. k_x <- n_of_classes(x) k_y <- n_of_classes(y) M_x <- cl_membership(x, k_x) M_y <- cl_membership(y, k_y) if(!is.matrix(L)) { p_x <- cl_prototypes(x) p_y <- cl_prototypes(y) if(is.matrix(p_x) && is.matrix(p_y) && is.null(L)) L <- .rxdist(p_x, p_y, ...) else if(is.function(L)) L <- L(p_x, p_y) else stop("Cannot compute prototype distances.") } C <- crossprod(M_x, M_y) * L if(is.null(alpha)) alpha <- rep.int(1, k_x) if(is.null(beta)) beta <- rep.int(1, k_y) sum(C) - 2 * lpSolve::lp.transport(C / outer(alpha, beta, `+`), "max", rep.int("==", k_x), alpha, rep.int("==", k_y), beta, integers = NULL)$objval } ### ** .cl_dissimilarity_hierarchy_euclidean .cl_dissimilarity_hierarchy_euclidean <- function(x, y, weights = 1) { if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u <- cl_object_dissimilarities(x) v <- cl_object_dissimilarities(y) sqrt(sum(weights * (u - v) ^ 2)) } ### ** .cl_dissimilarity_hierarchy_manhattan .cl_dissimilarity_hierarchy_manhattan <- function(x, y, weights = 1) { if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u <- cl_object_dissimilarities(x) v <- cl_object_dissimilarities(y) sum(weights * abs(u - v)) } ### ** .cl_dissimilarity_hierarchy_cophenetic .cl_dissimilarity_hierarchy_cophenetic <- function(x, y) { if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u <- cl_object_dissimilarities(x) v <- cl_object_dissimilarities(y) 1 - cor(u, v) ^ 2 } ### ** .cl_dissimilarity_hierarchy_gamma .cl_dissimilarity_hierarchy_gamma <- function(x, y) { ## ## This is a dissimilarity measure that works for arbitrary ## dissimilarities, see e.g. Bock. ## (And the current implementation finally respects this ...) ## if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u <- cl_object_dissimilarities(x) v <- cl_object_dissimilarities(y) n <- length(u) .C(C_clue_dissimilarity_count_inversions, as.double(u), as.double(v), as.integer(n), count = double(1L)) $ count / choose(n, 2) } ### ** .cl_dissimilarity_hierarchy_symdiff .cl_dissimilarity_hierarchy_symdiff <- function(x, y) { ## Cardinality of the symmetric difference of the n-trees when ## regarded as sets of subsets (classes) of the set of objects. x <- cl_classes(x) y <- cl_classes(y) sum(is.na(match(x, y))) + sum(is.na(match(y, x))) } ### ** .cl_dissimilarity_hierarchy_Chebyshev .cl_dissimilarity_hierarchy_Chebyshev <- function(x, y) { if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u <- cl_object_dissimilarities(x) v <- cl_object_dissimilarities(y) max(abs(u - v)) } ### ** .cl_dissimilarity_hierarchy_Lyapunov .cl_dissimilarity_hierarchy_Lyapunov <- function(x, y) { if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) q <- cl_object_dissimilarities(x) / cl_object_dissimilarities(y) if(is.matrix(q)) q <- q[lower.tri(q)] log(max(q) / min(q)) } ### ** .cl_dissimilarity_hierarchy_BO .cl_dissimilarity_hierarchy_BO <- function(x, y, delta, ...) { ## Compute Boorman-Olivier (1973) dendrogram ("valued tree") ## dissimilarities of the form ## ## m_\delta(T_1, T_2) ## = \int_0^\infty \delta(P_1(\alpha), P_2(\alpha)) d\alpha ## ## where the trees (dendrograms) are defined as right-continuous ## maps from [0, \Infty) to the partition lattice. ## We can compute this as follows. Take the ultrametrics and use ## as.hclust() to detemine the heights \alpha_1(k) and \alpha_2(l) ## of the splits. Let \alpha_i be the sequence obtained by ## combining these two. Then ## ## m_\delta ## = \sum_{i=0}^{L-1} (\alpha_{i+1} - \alpha_i) ## \delta(P_1(\alpha_i), P_2(\alpha_i)) ## ## We use cutree() for computing the latter partitions. As we ## already have the hclust representations, we should be able to do ## things more efficiently ... if(inherits(x, "hclust")) t_x <- x else if(inherits(x, "cl_ultrametric")) t_x <- as.hclust(x) else if(is.cl_dendrogram(x)) t_x <- as.hclust(cl_ultrametric(x)) else return(NA) if(inherits(y, "hclust")) t_y <- y else if(inherits(y, "cl_ultrametric")) t_y <- as.hclust(y) else if(is.cl_dendrogram(y)) t_y <- as.hclust(cl_ultrametric(y)) else return(NA) if(is.unsorted(t_x$height) || is.unsorted(t_y$height)) return(NA) alpha <- sort(unique(c(t_x$height, t_y$height))) cuts_x <- cutree(t_x, h = alpha) cuts_y <- cutree(t_y, h = alpha) deltas <- mapply(cl_dissimilarity, lapply(split(cuts_x, col(cuts_x)), as.cl_partition), lapply(split(cuts_y, col(cuts_y)), as.cl_partition), MoreArgs = list(delta, ...)) sum(diff(alpha) * deltas[-length(deltas)]) } ### ** .cl_dissimilarity_hierarchy_spectral .cl_dissimilarity_hierarchy_spectral <- function(x, y) { if(!.has_object_dissimilarities(x) || !.has_object_dissimilarities(y)) return(NA) u <- cl_object_dissimilarities(x) v <- cl_object_dissimilarities(y) svd(as.matrix(u - v))$d[1L] } ### * as.dist.cl_dissimilarity as.dist.cl_dissimilarity <- function(m, diag = FALSE, upper = FALSE) { y <- c(m) ## Fill non-inherited attributes with default values. attributes(y) <- c(attributes(m)[c("Size", "Labels")], Diag = diag, Upper = upper, call = match.call()) ## (Note that as.dist.default() does not automatically add ## 'method'.) class(y) <- "dist" y } ### * [.cl_dissimilarity "[.cl_dissimilarity" <- function(x, i, j) { y <- NextMethod("[") if(!inherits(y, "cl_dissimilarity")) { description <- attr(x, "description") return(cl_cross_proximity(y, description = description, class = "cl_cross_dissimilarity")) } y } ### .cxdist .cxdist <- function(A, B, method = c("euclidean", "manhattan", "minkowski"), ...) { ## Return the column cross distance matrix of A and B. ## I.e., the matrix C = [c_{j,k}] with ## c_{j,k} = distance(A[, j], B[, k]) ## Currently, only Manhattan (L1) distances are provided. ## Extensions to Minkowski or even more distances (a la dist()) ## could be added eventually. ## ## Possible implementations include ## ## foo_a <- function(A, B) ## apply(B, 2, function(u) colSums(abs(A - u))) ## foo_d <- function(A, B) { ## out <- as.matrix(dist(rbind(t(A), t(B)), "manhattan")) ## dimnames(out) <- NULL ## nc_B <- NCOL(B) ## out[seq(from = NCOL(A) + 1, length.out = nc_B), seq_len(nc_B)] ## } ## foo_f <- function(A, B) { ## out <- matrix(0, NCOL(A), NCOL(B)) ## for(j in seq_len(NCOL(A))) ## for(k in seq_len(NCOL(B))) ## out[j, k] = sum(abs(A[, j] - B[, k])) ## out ## } ## ## The one actually used seems to be the best performer, with the ## "for" version a close second (note that "typically", A and B have ## much fewer columns than rows). ## only few columns method <- match.arg(method) ## Workhorse. FOO <- switch(method, "euclidean" = function(M) sqrt(colSums(M ^ 2)), "manhattan" = function(M) colSums(abs(M)), "minkowski" = { ## Power needs to be given. p <- list(...)[[1L]] function(M) (colSums(abs(M) ^ p)) ^ (1 / p) }) out <- matrix(0, NCOL(A), NCOL(B)) for(k in seq_len(NCOL(B))) out[, k] <- FOO(A - B[, k]) out } ### .rxdist .rxdist <- function(A, B, method = c("euclidean", "manhattan", "minkowski"), ...) { ## Return the row cross distance matrix of A and B. ## I.e., the matrix C = [c_{j,k}] with ## c_{j,k} = distance(A[j, ], B[k, ]) ## ## Could also do something like ## ind <- seq_len(NROW(B)) ## as.matrix(dist(rbind(B, A)))[-ind, ind] ## but that is *very* inefficient for the "usual" data by prototype ## case (where NROW(B) << NROW(A)). ## ## No fancy pmatching for methods for the time being. method <- match.arg(method) ## Workhorse: Full A, single row of b. FOO <- switch(method, "euclidean" = function(A, b) sqrt(rowSums(sweep(A, 2, b) ^ 2)), "manhattan" = function(A, b) rowSums(abs(sweep(A, 2, b))), "minkowski" = { ## Power needs to be given. p <- list(...)[[1L]] function(A, b) (rowSums(abs(sweep(A, 2, b)) ^ p)) ^ (1 / p) }) out <- matrix(0, NROW(A), NROW(B)) ## Be nice: thanks to Wael Salem ZRAFI for ## suggesting this improvement. if(!is.null(cnA <- colnames(A)) && !is.null(cnB <- colnames(B)) && !identical(cnA, cnB)) A <- A[, cnB, drop = FALSE] for(k in seq_len(NROW(B))) out[, k] <- FOO(A, B[k, ]) out } ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/partition.R0000644000175100001440000004352212212427231013557 0ustar hornikusers### * n_of_classes ## Get the number of classes in a (hard or soft) partition. ## ## We generally allow for classes to be empty, unlike the current ## version of kmeans(). Package cclust has a version of k-means which ## does not stop in case of empty classes. ## However, we only count NON-EMPTY classes here. ## n_of_classes <- function(x) UseMethod("n_of_classes") ## Default method. n_of_classes.default <- function(x) length(unique(cl_class_ids(x))) ## Package stats: kmeans() (R 2.1.0 or better). n_of_classes.kmeans <- n_of_classes.default ## Package cluster: clara(), fanny(), and pam() give objects of the ## respective class inheriting from class "partition". n_of_classes.fanny <- function(x) sum(colSums(x$membership, na.rm = TRUE) > 0) n_of_classes.partition <- n_of_classes.default ## Package cclust: cclust(). n_of_classes.cclust <- n_of_classes.default ## Package e1071: cmeans() gives objects of class "fclust". n_of_classes.fclust <- n_of_classes.fanny ## Package e1071: cshell(). n_of_classes.cshell <- n_of_classes.fanny ## Package e1071: bclust(). n_of_classes.bclust <- n_of_classes.default ## Package mclust: Mclust(). n_of_classes.Mclust <- n_of_classes.default ## Package clue: Memberships. n_of_classes.cl_membership <- function(x) attr(x, "n_of_classes") ## Package clue: pclust(). n_of_classes.pclust <- function(x) { if(is.null(m <- x$membership)) length(unique(cl_class_ids(x))) else sum(colSums(m, na.rm = TRUE) > 0) } ## Package clue: (virtual) class "cl_partition". n_of_classes.cl_partition <- function(x) n_of_classes(.get_representation(x)) ### * cl_class_ids ## Get ids of classes in a partition. ## ## Currently, all supported soft partitioning methods provide a softmax ## hard partitioning as well. ## cl_class_ids <- function(x) UseMethod("cl_class_ids") ## Default method. cl_class_ids.default <- function(x) { stop("Cannot infer class ids from given object.") } ## Package stats: kmeans() (R 2.1.0 or better). cl_class_ids.kmeans <- function(x) as.cl_class_ids(x$cluster) ## Package cluster: clara(), fanny(), and pam() give objects of the ## respective class inheriting from class "partition". cl_class_ids.partition <- function(x) as.cl_class_ids(x$clustering) ## Package RWeka: clusterers return objects inheriting from ## "Weka_clusterer". cl_class_ids.Weka_clusterer <- function(x) as.cl_class_ids(x$class_ids) ## Package cba: ccfkms(). cl_class_ids.ccfkms <- function(x) as.cl_class_ids(as.vector(x$cl)) ## Package cba: rockCluster() returns objects of class "rock". cl_class_ids.rock <- function(x) as.cl_class_ids(as.vector(x$cl)) ## Package cclust: cclust(). cl_class_ids.cclust <- cl_class_ids.kmeans ## Package e1071: cmeans() gives objects of class "fclust". cl_class_ids.fclust <- cl_class_ids.kmeans ## Package e1071: cshell(). cl_class_ids.cshell <- cl_class_ids.kmeans ## Package e1071: bclust(). cl_class_ids.bclust <- cl_class_ids.kmeans ## Package flexclust: kcca() returns objects of S4 class "kcca" which ## extends S4 class "flexclust". ## ## We used to be able to call flexclust::cluster(), but this now only ## has S4 methods for modeltools::clusters() S4 generic. Let's call this ## one, and hope that flexclust has been loaded ... ## cl_class_ids.kcca <- function(x) as.cl_class_ids(modeltools::clusters(x)) ## Package flexmix: class "flexmix". ## ## We used to be able to call flexmix::cluster(), but this now only has ## S4 methods for modeltools::clusters() S4 generic. Let's call this ## one, and hope that flexmix has been loaded ... ## cl_class_ids.flexmix <- function(x) as.cl_class_ids(modeltools::clusters(x)) ## Package kernlab: specc() and kkmeans() return objects of S4 class ## "specc". cl_class_ids.specc <- function(x) { tmp <- unclass(x) as.cl_class_ids(.structure(as.vector(tmp), names = names(tmp))) } ## Package mclust: Mclust(). cl_class_ids.Mclust <- function(x) as.cl_class_ids(x$classification) ## Package relations: equivalence and preference relations. cl_class_ids.relation <- function(x) as.cl_class_ids(relations::relation_class_ids(x)) ## Package clue: Class ids. cl_class_ids.cl_class_ids <- identity ## Package clue: Memberships. cl_class_ids.cl_membership <- function(x) as.cl_class_ids(.structure(max.col(x), names = rownames(x))) ## (Cannot do cl_class_ids.cl_membership <- max.col for generic/method ## consistency.) ## Package clue: cl_pam(). cl_class_ids.cl_pam <- cl_class_ids.kmeans ## Package clue: cl_partition_by_class_ids(). cl_class_ids.cl_partition_by_class_ids <- function(x) .get_representation(x) ## Package clue: kmedoids(). cl_class_ids.kmedoids <- cl_class_ids.kmeans ## Package clue: pclust(). cl_class_ids.pclust <- cl_class_ids.kmeans ## Package clue: (virtual) class "cl_partition". cl_class_ids.cl_partition <- function(x) cl_class_ids(.get_representation(x)) ## Package movMF: class "movMF". cl_class_ids.movMF <- function(x) as.cl_class_ids(max.col(x$P)) ### * as.cl_class_ids as.cl_class_ids <- function(x) { ## For the time being, handle only "raw" class ids. ## Maybe add methods handling factors lateron (if necessary). ## ## This could also be used to canonicalize returned class ids ## according to the docs (vector of integers with the class ids), ## using someting like ## match(ids, unique(ids)) ## .structure(unclass(x), class = "cl_class_ids") } ### * print.cl_class_ids print.cl_class_ids <- function(x, ...) { writeLines("Class ids:") print(unclass(x), ...) invisible(x) } ### * cl_class_labels cl_class_labels <- function(x) UseMethod("cl_class_labels") ### * is.cl_partition ## Determine whether an object is a (generalized) partition. ## Note that this includes both hard and soft partitions, and allows ## sums of memberships of objects to be less than one. is.cl_partition <- function(x) UseMethod("is.cl_partition") ## Default method. is.cl_partition.default <- .false ## Package stats: kmeans() (R 2.1.0 or better). is.cl_partition.kmeans <- .true ## Package cluster: clara(), fanny(), and pam() give objects of the ## respective class inheriting from class "partition". is.cl_partition.partition <- .true ## Package RWeka: clusterers return objects inheriting from ## "Weka_clusterer". ## (Note that Cobweb internally uses a classification tree, but ## definitely does not expose this structure.) is.cl_partition.Weka_clusterer <- .true ## Package cba: ccfkms(). is.cl_partition.ccfkms <- .true ## Package cba: rockCluster() returns objects of class "rock". is.cl_partition.rock <- .true ## Package cclust: cclust(). is.cl_partition.cclust <- .true ## Package e1071: cmeans() gives objects of class "fclust". is.cl_partition.fclust <- .true ## Package e1071: cshell(). is.cl_partition.cshell <- .true ## Package e1071: bclust(). is.cl_partition.bclust <- .true ## Package flexclust: kcca() returns objects of S4 class "kcca" which ## extends S4 class "flexclust". is.cl_partition.kcca <- .true ## Package flexmix: class "flexmix". is.cl_partition.flexmix <- .true ## Package kernlab: specc() and kkmeans() return objects of S4 class ## "specc". is.cl_partition.specc <- .true ## Package mclust: Mclust(). is.cl_partition.Mclust <- .true ## Package clue: (virtual) class "cl_partition". ## Note that "raw" cl_membership objects are *not* partitions, as they ## are meant for numeric computations. is.cl_partition.cl_partition <- .true ## Package clue: kmedoids(). is.cl_partition.kmedoids <- .true ## Package clue: pclust(). is.cl_partition.pclust <- .true ## Package movMF: class "movMF". is.cl_partition.movMF <- .true ### * as.cl_partition ## Note that cl_partition conceptually is a virtual class, so there are ## no prototypes and no cl_partition() creator. .cl_partition_classes <- "cl_partition" as.cl_partition <- function(x) { if(is.cl_partition(x)) { if(!inherits(x, "cl_partition")) .make_container(x, .cl_partition_classes) else x } else cl_partition_by_memberships(as.cl_membership(x)) } ### * print.cl_partition print.cl_partition <- function(x, ...) .print_container(x, "cl_partition", ...) ### * print.cl_partition_by_class_ids print.cl_partition_by_class_ids <- function(x, ...) { writeLines(gettextf("A hard partition of %d objects.", n_of_objects(x))) print(cl_class_ids(x), ...) invisible(x) } ### * print.cl_partition_by_memberships print.cl_partition_by_memberships <- function(x, ...) { writeLines(gettextf("A partition of %d objects.", n_of_objects(x))) print(cl_membership(x), ...) invisible(x) } ### * Complex.cl_partition Complex.cl_partition <- function(z) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ### * Math.cl_partition Math.cl_partition <- function(x, ...) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ### * Ops.cl_partition Ops.cl_partition <- function(e1, e2) { if(nargs() == 1L) stop(gettextf("Unary '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ## Only comparisons are supprorted. if(!(as.character(.Generic) %in% c("<", "<=", ">", ">=", "==", "!="))) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) ci1 <- cl_class_ids(e1) ci2 <- cl_class_ids(e2) if(length(ci1) != length(ci2)) stop("Partitions must have the same number of objects.") z <- table(ci1, ci2) > 0 switch(.Generic, "<=" = all(rowSums(z) == 1), "<" = all(rowSums(z) == 1) && any(colSums(z) > 1), ">=" = all(colSums(z) == 1), ">" = all(colSums(z) == 1) && any(rowSums(z) > 1), "==" = all(rowSums(z) == 1) && all(colSums(z) == 1), "!=" = any(rowSums(z) > 1) || any(colSums(z) > 1)) } ### * Summary.cl_partition Summary.cl_partition <- function(..., na.rm = FALSE) { ok <- switch(.Generic, max = , min = , range = TRUE, FALSE) if(!ok) stop(gettextf("Generic '%s' not defined for \"%s\" objects.", .Generic, .Class), domain = NA) args <- list(...) switch(.Generic, "min" = cl_meet(cl_ensemble(list = args)), "max" = cl_join(cl_ensemble(list = args)), "range" = { cl_ensemble(min = cl_meet(cl_ensemble(list = args)), max = cl_join(cl_ensemble(list = args))) }) } ### * cl_partition_by_class_ids cl_partition_by_class_ids <- function(x, labels = NULL) { if(!is.atomic(x)) stop("Class ids must be atomic.") if(is.null(names(x))) names(x) <- labels ## ## Perhaps give the raw class ids more structure? ## E.g, class "cl_class_ids"? ## Problem is that we used to say about extensibility that all there ## is to do for a hard partitioner is to add a cl_class_ids() method ## and two predicates, but *not* to have the former give a suitably ## classed object. On the other hand, the recipe would need to be ## extended for soft partitioners, for which it would be necessary ## to provide a cl_membership() method which really returns an ## object of class cl_membership. Note that we can do this using ## as.cl_membership(m), where m is the raw membership matrix. So ## maybe we should ask for using as.cl_class_ids() to coerce raw ## class ids ... .make_container(as.cl_class_ids(x), c("cl_partition_by_class_ids", .cl_hard_partition_classes), list(n_of_objects = length(x), n_of_classes = length(unique(x)))) ## } ### * cl_partition_by_memberships cl_partition_by_memberships <- function(x, labels = NULL) { if(!is.matrix(x) || any(x < 0, na.rm = TRUE) || any(x > 1, na.rm = TRUE)) stop("Not a valid membership matrix.") ## Be nice. x <- x / rowSums(x, na.rm = TRUE) ## (Note that this does not imply all(rowSums(x) == 1). If we ## wanted to test for this, something like ## .is_stochastic_matrix <- function(x) ## identical(all.equal(rowSums(x), rep(1, nrow(x))), TRUE)) ## should do.) if(is.null(rownames(x))) rownames(x) <- labels .make_container(as.cl_membership(x), c("cl_partition_by_memberships", .cl_partition_classes), list(n_of_objects = nrow(x))) } ### * is.cl_hard_partition ## Determine whether an object is a hard partition. is.cl_hard_partition <- function(x) UseMethod("is.cl_hard_partition") ## Default method. is.cl_hard_partition.default <- .false ## Package stats: kmeans() (R 2.1.0 or better). is.cl_hard_partition.kmeans <- .true ## Package cluster: clara(), fanny(), and pam() give objects of the ## respective class inheriting from class "partition". ## ## Of course, fuzzy clustering can also give a hard partition ... is.cl_hard_partition.fanny <- function(x) { all(rowSums(cl_membership(x) == 1, na.rm = TRUE) > 0) } ## is.cl_hard_partition.partition <- .true ## Package RWeka: clusterers return objects inheriting from ## "Weka_clusterer". is.cl_hard_partition.Weka_clusterer <- .true ## Package cba: ccfkms(). is.cl_hard_partition.ccfkms <- .true ## Package cba: rockCluster() returns objects of class "rock". is.cl_hard_partition.rock <- .true ## Package cclust: cclust(). is.cl_hard_partition.cclust <- .true ## Package e1071: cmeans() gives objects of class "fclust". is.cl_hard_partition.fclust <- is.cl_hard_partition.fanny ## Package e1071: cshell(). is.cl_hard_partition.cshell <- is.cl_hard_partition.fanny ## Package e1071: bclust(). is.cl_hard_partition.bclust <- .true ## Package flexclust: kcca() returns objects of S4 class "kcca" which ## extends S4 class "flexclust". is.cl_hard_partition.kcca <- .true ## Package flexmix: class "flexmix". is.cl_hard_partition.flexmix <- is.cl_hard_partition.fanny ## Package kernlab: specc() and kkmeans() return objects of S4 class ## "specc". is.cl_hard_partition.specc <- .true ## Package mclust: Mclust(). is.cl_hard_partition.Mclust <- is.cl_hard_partition.fanny ## Package clue: (virtual) class "cl_hard_partition". is.cl_hard_partition.cl_hard_partition <- .true ## Package clue: (virtual) class "cl_partition". ## Note that "raw" cl_membership objects are *not* partitions, as they ## are meant for numeric computations. ## Rather than providing is.cl_hard_partition.cl_membership() we thus ## prefer explicit handling of cl_partition objects with a cl_membership ## representation. is.cl_hard_partition.cl_partition <- function(x) { ## If the object has a cl_membership representation ... y <- .get_representation(x) if(inherits(y, "cl_membership")) attr(y, "is_cl_hard_partition") ## Other representations, e.g. for "definitely" hard partitions via ## vectors of class ids or class labels, or a list of classes, may ## be added in future versions. ## In any case, this must be kept in sync with what is handled by ## as.cl_partition() [which currently runs as.cl_membership() in ## case is.cl_partition() gives false]. else is.cl_hard_partition(y) } ## Package clue: kmedoids(). is.cl_hard_partition.kmedoids <- .true ## Package clue: pclust(). is.cl_hard_partition.pclust <- is.cl_hard_partition.fanny ## Package movMF: class "movMF". is.cl_hard_partition.movMF <- is.cl_hard_partition.fanny ### * as.cl_hard_partition .cl_hard_partition_classes <- c("cl_hard_partition", "cl_partition") as.cl_hard_partition <- function(x) { if(is.cl_hard_partition(x)) { if(!inherits(x, "cl_partition")) .make_container(x, .cl_hard_partition_classes) else x } else if(is.cl_partition(x)) { ## A soft cl_partition ... ids <- cl_class_ids(x) cl_partition_by_class_ids(ids, names(ids)) } else if(is.matrix(x)) { ## A matrix of raw memberships, hopefully ... cl_partition_by_class_ids(max.col(x), rownames(x)) } else if(is.atomic(x)) { ## A vector of raw class ids, hopefully ... cl_partition_by_class_ids(x, names(x)) } else stop("Cannot coerce to 'cl_hard_partition'.") } ### * is.cl_soft_partition ## Determine whether an object is a soft partition. is.cl_soft_partition <- function(x) is.cl_partition(x) && ! is.cl_hard_partition(x) ### * .maybe_is_proper_soft_partition ## Determine whether an object might be a proper soft partition (in the ## sense that it is a cl_partition but not a cl_hard_partition). ## This is mostly useful when computing fuzziness measures. .maybe_is_proper_soft_partition <- function(x) UseMethod(".maybe_is_proper_soft_partition") .maybe_is_proper_soft_partition.default <- .false .maybe_is_proper_soft_partition.fanny <- .true .maybe_is_proper_soft_partition.fclust <- .true .maybe_is_proper_soft_partition.cshell <- .true .maybe_is_proper_soft_partition.flexmix <- .true .maybe_is_proper_soft_partition.Mclust <- .true ## See above for why we prefer not to have ## .maybe_is_proper_soft_partition.cl_membership(). ## (Although this is an internal generic really only used for making ## cl_fuzziness() computations more efficient, so we could be more ## generous here [perhaps using a slightly different name such as ## .maybe_represents_a_proper_soft_partition()]. .maybe_is_proper_soft_partition.cl_partition <- function(x) { y <- .get_representation(x) if(inherits(y, "cl_membership")) !attr(y, "is_cl_hard_partition") else .maybe_is_proper_soft_partition(y) } .maybe_is_proper_soft_partition.pclust <- function(x) x$m > 1 ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/lattice.R0000644000175100001440000001356713036514161013205 0ustar hornikuserscl_meet <- function(x, y) { ## General case. ## x either an ensemble, or x and y two clusterings with the same ## number of objects. if(!inherits(x, "cl_ensemble")) { ## Be nice about error messages. if(n_of_objects(x) != n_of_objects(y)) stop("Arguments 'x' and 'y' must have the same number of objects.") x <- cl_ensemble(x, y) } if(inherits(x, "cl_partition_ensemble")) .cl_meet_partition(x) else if(inherits(x, "cl_dendrogram_ensemble")) .cl_meet_dendrogram(x) else if(inherits(x, "cl_hierarchy_ensemble")) .cl_meet_hierarchy(x) else stop("Cannot compute meet of given clusterings.") } .cl_meet_partition <- function(x) { x <- unique(x) if(length(x) == 1L) return(cl_partition_by_class_ids(cl_class_ids(x[[1L]]))) ids <- seq_len(n_of_objects(x[[1L]])) ## Cross-classify the objects. z <- split(ids, lapply(x, cl_class_ids)) ## Subscript on the non-empty cells to get adjacent class ids. lens <- lengths(z) pos <- which(lens > 0) ids[unlist(z, use.names = FALSE)] <- rep.int(seq_along(z[pos]), lens[pos]) cl_partition_by_class_ids(ids) } .cl_meet_dendrogram <- function(x) { ## Meet of an ensemble of dendrograms. ## We need the maximal ultrametric dominated by the given ones, ## which can be obtained by hierarchical clustering with single ## linkage on the pointwise minima of the ultrametrics. as.cl_dendrogram(hclust(as.dist(do.call(pmin, lapply(x, cl_ultrametric))), "single")) } .cl_meet_hierarchy <- function(x) { ## Meet of an ensemble of n-trees. ## Need to find the classes in *all* n-trees. ## Equivalent to computing a strict majority tree. .cl_consensus_hierarchy_majority(x, rep.int(1, length(x)), list(p = 1)) } cl_join <- function(x, y) { ## General case. ## x either an ensemble, or x and y two clusterings with the same ## number of objects. if(!inherits(x, "cl_ensemble")) { ## Be nice about error messages. if(n_of_objects(x) != n_of_objects(y)) stop("Arguments 'x' and 'y' must have the same number of objects.") x <- cl_ensemble(x, y) } if(inherits(x, "cl_partition_ensemble")) .cl_join_partition(x) else if(inherits(x, "cl_dendrogram_ensemble")) .cl_join_dendrogram(x) else if(inherits(x, "cl_hierarchy_ensemble")) .cl_join_hierarchy(x) else stop("Cannot compute join of given clusterings.") } .cl_join_partition <- function(x) { x <- unique(x) if(length(x) == 1) return(cl_partition_by_class_ids(cl_class_ids(x[[1L]]))) ## Canonicalize: ensure that class ids are always the integers from ## one to the number of classes. n <- sapply(x, n_of_classes) ids <- mapply(function(p, ncp) match(cl_class_ids(p), seq_len(ncp)), x, n, SIMPLIFY = FALSE) ## Order according to the number of classes. ids <- ids[order(n)] ## And now incrementally build the join. jcids <- ids[[1L]] # Class ids of the current join. jnc <- length(unique(jcids)) # Number of classes of this. for(b in seq.int(from = 2, to = length(x))) { z <- table(jcids, ids[[b]]) ## It is faster to work on the smaller partition, but this ## should be ensured by the reordering ... ## We need to "join all elements in the same class in at least ## one of the partitions". In the matrix ## C <- (tcrossprod(z) > 0) ## entry i,j is true/one iff z_{ik} z_{jk} > 0 for classes ## i and j in the current join (ids jcids) and some class k in ## the partition with ids[[b]], so that i and j must be joined. ## I.e., C indicates which classes need to be joined directly. ## We need to determine the transitive closure of this relation, ## which can be performed by repeating ## C_{t+1} <- ((C_t %*% C) > 0) ## with C_1 = C until C_t does not change. C_new <- C_old <- C <- (tcrossprod(z) > 0) repeat { C_new <- (C_old %*% C) > 0 if(all(C_new == C_old)) break C_old <- C_new } C <- C_new ## This should now have the connected components. ## Next, compute the map of the join class ids to the ids of ## these components. cnt <- 0 map <- remaining_ids <- seq_len(jnc) while(length(remaining_ids)) { cnt <- cnt + 1 pos <- which(C[remaining_ids[1L], remaining_ids] > 0) map[remaining_ids[pos]] <- cnt remaining_ids <- remaining_ids[-pos] } ## And update the join: jcids <- map[jcids] jnc <- cnt } cl_partition_by_class_ids(jcids) } .cl_join_dendrogram <- function(x) { ## Join of an ensemble of dendrograms. as.cl_dendrogram(do.call(pmax, lapply(x, cl_ultrametric))) } .cl_join_hierarchy <- function(x) { ## Join of an ensemble of n-trees. ## Only exists if the union of all classes of the n-trees is itself ## an n-tree (see Barthelemy et al). classes <- unique(unlist(lapply(x, cl_classes), recursive = FALSE)) ## Now check if this is an n-tree. ## We must verify that for all classes A and B, their intersection ## is A, B, or empty. check <- function(A, B) { m_AB <- match(A, B) m_BA <- match(B, A) ((all(is.na(m_AB)) && all(is.na(m_BA))) || all(is.finite(m_AB)) || all(is.finite(m_BA))) } for(i in seq_along(classes)) { A <- classes[[i]] for(j in seq_along(classes)) if(!check(A, classes[[j]])) stop("Join of given n-trees does not exist.") } as.cl_hierarchy(.cl_ultrametric_from_classes(classes)) } clue/R/proximity.R0000644000175100001440000001244311304023136013605 0ustar hornikusers### * cl_proximity cl_proximity <- function(x, description, class = NULL, labels = NULL, self = NULL, size = NULL) { ## Similar to as.dist(), in a way. ## Currently, as.dist() is not generic, so we cannot provide a ## cl_proximity method for it. Hence, we have our dissimilarities ## and ultrametrics extend dist, and we use capitalized names for ## the attributes provided for compatibility with dist (Size and ## Labels). if(inherits(x, "dist")) { ## Explicitly deal with dist objects. ## Useful in particular because cophenetic() returns them. out <- x if(is.null(size)) size <- attr(x, "Size") if(is.null(labels)) labels <- attr(x, "Labels") } else if(inherits(x, "cl_proximity") || !(is.matrix(x) && (nrow(x) == ncol(x)))) out <- x else { ## Actually, x should really be a square symmetric matrix. ## The "self-proximities" in the main diagonal must be stored ## provided there is one non-zero entry. self <- diag(x) if(all(self == 0)) self <- NULL out <- x[row(x) > col(x)] if(is.null(labels)) { if(!is.null(rownames(x))) labels <- rownames(x) else if(!is.null(colnames(x))) labels <- colnames(x) } } if(is.null(size)) size <- as.integer((sqrt(1 + 8 * length(out)) + 1) / 2) attributes(out) <- list(Size = size, Labels = labels, description = description, self = self) class(out) <- unique(c(class, "cl_proximity")) out } ### * names.cl_proximity names.cl_proximity <- function(x) NULL ### * print.cl_proximity print.cl_proximity <- function(x, ...) { description <- attr(x, "description") if(length(description) > 0L) { ## Could make this generic ... kind <- if(inherits(x, "cl_dissimilarity")) "Dissimilarities" else if(inherits(x, "cl_agreement")) "Agreements" else "Proximities" cat(sprintf("%s using %s", kind, description), ":\n", sep = "") } m <- format(as.matrix(x)) if(is.null(self <- attr(x, "self"))) m[row(m) <= col(m)] <- "" else m[row(m) < col(m)] <- "" print(if(is.null(self)) m[-1, -attr(x, "Size")] else m, quote = FALSE, right = TRUE, ...) invisible(x) } ### * as.matrix.cl_proximity as.matrix.cl_proximity <- function(x, ...) { size <- attr(x, "Size") m <- matrix(0, size, size) m[row(m) > col(m)] <- x m <- m + t(m) if(!is.null(self <- attr(x, "self"))) { diag(m) <- self } ## ## stats:::as.matrix.dist() provides default dimnames ## (seq_len(size)) if no labels are available. ## We used to do this too, but ... if(!is.null(labels <- attr(x, "Labels"))) dimnames(m) <- list(labels, labels) ## m } ### * [.cl_proximity "[.cl_proximity" <- function(x, i, j) { ## Subscripting proximity objects. ## Basically matrix-like, but proximity objects are always ## "matrices", hence no 'drop' argument. ## For double-index subscripting, if i and j are identical, ## structure and class are preserved. Otherwise, a cross-proximity ## object is returned (and methods for classes inheriting from ## proximity need to readjust the class info as needed). ## For single-index subscripting, no attempty is currently made at ## preserving structure and class where possible. (We might also ## change this to select objects, i.e., the same rows and columns.) size <- attr(x, "Size") if(missing(j)) { if(missing(i)) return(x) else j <- seq_len(size) } if(missing(i)) i <- seq_len(size) description <- attr(x, "description") ## RG's graph:::[.dist avoids as.matrix() in noting that for dist ## objects, entry (i,j) is at n(i-1) - i(i-1)/2 + j - i (in the ## veclh dist representation). We could do something similar, but ## note that not all proximities have zero diagonals (i.e., NULL ## "self" attributes). y <- as.matrix(x)[i, j, drop = FALSE] if(identical(i, j)) { ## Testing using identical() is rather defensive ... return(cl_proximity(y, description = description, class = class(x))) } cl_cross_proximity(y, description = description) } ### * cl_cross_proximity cl_cross_proximity <- function(x, description = NULL, class = NULL) { attr(x, "description") <- description class(x) <- c(class, "cl_cross_proximity") x } ### * print.cl_cross_proximity print.cl_cross_proximity <- function(x, ...) { description <- attr(x, "description") if(length(description) > 0L) { ## Could make this generic ... kind <- if(inherits(x, "cl_cross_dissimilarity")) "Cross-dissimilarities" else if(inherits(x, "cl_cross_agreement")) "Cross-agreements" else "Cross-proximities" cat(sprintf("%s using %s", kind, description), ":\n", sep = "") } print(matrix(as.vector(x), nrow = nrow(x), dimnames = dimnames(x)), ...) invisible(x) } ### ** print_description_prefix ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "### [*]+" *** ### End: *** clue/R/pava.R0000644000175100001440000000507013036461767012512 0ustar hornikusers## A Pool Adjacent Violators Algorithm framework for minimizing problems ## like ## ## \sum_i \sum_{J_i} w_{ij} f(y_{ij}, m_i) ## ## under the constraint m_1 <= ... <= m_n with f a convex function in m. ## Note that this formulation allows for repeated data in each block, ## and hence is more general than the usual pava/isoreg ones. A solver ## for the unconstrained \sum_k w_k f(y_k, m) => min! is needed. ## Typical cases are f(y, m) = |y - m|^p for p = 2 (solved by weighted ## mean) and p = 1 (solved by weighted median), respectively. ## A general design issue is whether weights should be supported or not, ## because in the latter case the solver could be a function of a single ## (data) argument only. Let's assume the former for the time being. pava <- function(x, w = NULL, solver = weighted.mean, merger = c) { n <- length(x) if(is.null(w)) { w <- if(is.list(x)) lapply(lengths(x), function(u) rep.int(1, u)) else rep.int(1, n) } else if(is.list(x)) w <- as.list(w) inds <- as.list(seq_len(n)) vals <- mapply(solver, x, w) ## Combine blocks i and i + 1. combine <- if(is.list(x)) { ## In the repeated data case, we explicitly merge the data (and ## weight) lists. function(i) { ## Merge the data and indices, solve, and put things back ## into position i, dropping position i + 1. j <- i + 1L x[[i]] <<- merger(x[[i]], x[[j]]) w[[i]] <<- c(w[[i]], w[[j]]) vals[i] <<- solver(x[[i]], w[[i]]) inds[[i]] <<- c(inds[[i]], inds[[j]]) keep <- seq_len(n)[-j] x <<- x[keep] w <<- w[keep] vals <<- vals[keep] inds <<- inds[keep] n <<- n - 1L } } else { function(i) { ## In the "simple" case, merge only indices and values. j <- i + 1L inds[[i]] <<- c(inds[[i]], inds[[j]]) vals[i] <<- solver(x[inds[[i]]], w[inds[[i]]]) keep <- seq_len(n)[-j] vals <<- vals[keep] inds <<- inds[keep] n <<- n - 1L } } i <- 1L repeat { if(i < n) { if((vals[i] > vals[i + 1])) { combine(i) while((i > 1L) && (vals[i - 1L] > vals[i])) { combine(i - 1L) i <- i - 1L } } else i <- i + 1L } else break } rep.int(vals, lengths(inds)) } clue/R/predict.R0000644000175100001440000002453614503541710013210 0ustar hornikusers## ## Maybe add support for "auto" type (class_ids when predicting from a ## hard, memberships when predicting from a soft partition) eventually. ## cl_predict <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) UseMethod("cl_predict") ## Default method. ## Should also work for kcca() from package flexclust. cl_predict.default <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) .as_cl_class_ids_or_membership(predict(object, newdata, ...), type) ## Package stats: kmeans() (R 2.1.0 or better). cl_predict.kmeans <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) d <- .rxdist(newdata, object$centers) .as_cl_class_ids_or_membership(max.col(-d), type) } ## Package cluster: ## * fanny() cannot make "new" predictions. ## * clara() gives medoids, and takes metric data using Euclidean or ## Manhattan dissimilarities (and we can figure out which by looking ## at the call and the default values). ## * pam() gives medoids, but might have been called with dissimilarity ## data, so is tricky. We can always find out which by looking at the ## medoids: as in the dissimilarity input case this is a vector of ## class labels, and a matrix with in each row the coordinates of one ## medoid otherwise. We then still need to figure out whether ## Euclidean or Manhattan distances were used by looking at the call ## and the default values. ## Both pam() and clara() show that the interfaces could be improved to ## accomodate modern needs, e.g., for bagging. cl_predict.fanny <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) stop("Cannot make new predictions.") } cl_predict.clara <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) ## ## Add support eventually ... if(identical(object$call$stand, TRUE)) warning("Standardization is currently not supported.") ## method <- object$call$metric if(is.null(method)) { ## Not given in the call, hence use default value. method <- eval(formals(clara)$metric)[1L] ## (Or hard-wire the default value: "euclidean".) } d <- .rxdist(newdata, object$medoids, method) .as_cl_class_ids_or_membership(max.col(-d), type) } cl_predict.pam <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) prototypes <- object$medoids if(!is.matrix(prototypes)) stop("Cannot make new predictions.") ## ## Add support eventually ... if(identical(object$call$stand, TRUE)) warning("Standardization is currently not supported.") ## method <- object$call$metric if(is.null(method)) { ## Not given in the call, hence use default value. method <- eval(formals(pam)$metric)[1L] ## (Or hard-wire the default value: "euclidean".) } d <- .rxdist(newdata, object$medoids, method) .as_cl_class_ids_or_membership(max.col(-d), type) } ## Package RWeka: clusterers return objects inheriting from ## "Weka_clusterer". cl_predict.Weka_clusterer <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) .as_cl_class_ids_or_membership(predict(object, newdata = newdata, type = type, ...), type) } ## Package cba: ccfkms(). cl_predict.ccfkms <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) .as_cl_class_ids_or_membership(as.vector(predict(object, newdata)$cl), type) } ## Package cba: rockCluster() returns objects of class "rock". ## If x is a Rock object, fitted(x) and predict(x, newdata) can result ## in missing classifications, as ## In the case a 'drop' value greater than zero is specified, all ## clusters with size equal or less than this value are removed from ## the classifier. Especially, 'fitted' uses a threshold of one ## because for singleton clusters the neighborhood is empty. cl_predict.rock <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) newdata <- object$x ids <- as.vector(predict(object, newdata, ...)$cl) .as_cl_class_ids_or_membership(ids, type) } ## Package cclust: cclust(). cl_predict.cclust <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { ## Package cclust provides predict.cclust() which returns (again) an ## object of class "cclust", but does not give the labels of the ## original data in case no new data are given. if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) .as_cl_class_ids_or_membership(predict(object, newdata), type) } ## Package e1071: cmeans() gives objects of class "fclust". cl_predict.fclust <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) ## Note that the 'fclust' objects returned by cmeans() do not always ## directly contain the information on the fuzzification parameter m ## and the distance (Euclidean/Manhattan) employed, so we have to ## engineer this from the matched call and the default arguments. nms <- names(object$call) ## Note that we cannot directly use object$call$m, as this could ## give the 'method' argument if 'm' was not given. m <- if("m" %in% nms) object$call$m else { ## Not given in the call, hence use default value. formals(e1071::cmeans)$m ## (Or hard-wire the default value: 2.) } method <- if("dist" %in% nms) object$call$dist else { ## Not given in the call, hence use default value. formals(e1071::cmeans)$dist ## (Or hard-wire the default value: "euclidean".) } d <- .rxdist(newdata, object$centers, method) power <- c(m, if(method == "euclidean") 2 else 1) M <- .memberships_from_cross_dissimilarities(d, power) .as_cl_class_ids_or_membership(M, type) } ## Package e1071: cshell(). cl_predict.cshell <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) ## Not surprisingly, this is rather similar to what we do for fclust ## objects. Only dissimiliraties (and exponents) need to be ## computed differently ... nms <- names(object$call) m <- if("m" %in% nms) object$call$m else { ## Not given in the call, hence use default value. formals(e1071::cshell)$m ## (Or hard-wire the default value: 2.) } method <- if("dist" %in% nms) object$call$dist else { ## Not given in the call, hence use default value. formals(e1071::cshell)$dist ## (Or hard-wire the default value: "euclidean".) } d <- .rxdist(newdata, object$centers, method) d <- sweep(d, 2, object$radius) ^ 2 M <- .memberships_from_cross_dissimilarities(d, m) .as_cl_class_ids_or_membership(M, type) } ## Package e1071: bclust(). ## ## One might argue that it would be better to use the 'dist.method' ## employed for the hierarchical clustering, but it seems that class ## labels ("clusters") are always assigned using Euclidean distances. cl_predict.bclust <- cl_predict.kmeans ## ## Package flexclust: kcca() returns objects of S4 class "kcca" which ## extends S4 class "flexclust". cl_predict.kcca <- cl_predict.default ## Package flexmix: class "flexmix". cl_predict.flexmix <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) .as_cl_class_ids_or_membership(modeltools::posterior(object, newdata, ...), type) } ## Package mclust: Mclust(). cl_predict.Mclust <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) pred <- predict(object, newdata, ...) type <- match.arg(type) if(type == "class_ids") as.cl_class_ids(pred$classification) else as.cl_membership(pred$z) } ## Package movMF: movMF(). cl_predict.movMF <- cl_predict.Weka_clusterer ## Package clue: pclust(). cl_predict.pclust <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) { if(is.null(newdata)) return(.cl_class_ids_or_membership(object, type)) d <- object$family$D(newdata, object$prototypes) power <- c(object$m, object$family$e) M <- .memberships_from_cross_dissimilarities(d, power) .as_cl_class_ids_or_membership(M, type) } ## Package clue: (virtual) class "cl_partition". cl_predict.cl_partition <- function(object, newdata = NULL, type = c("class_ids", "memberships"), ...) cl_predict(.get_representation(object), newdata = newdata, type, ...) ## Internal helpers: this looks a bit silly, but makes the rest of the ## code look nicer ... .cl_class_ids_or_membership <- function(x, type = c("class_ids", "memberships")) { type <- match.arg(type) if(type == "class_ids") cl_class_ids(x) else cl_membership(x) } .as_cl_class_ids_or_membership <- function(x, type = c("class_ids", "memberships")) { type <- match.arg(type) if(type == "class_ids") { if(is.matrix(x)) { ## Same as for cl_class_ids.cl_membership(). as.cl_class_ids(.structure(max.col(x), names = rownames(x))) } else as.cl_class_ids(x) } else as.cl_membership(x) } clue/R/medoid.R0000644000175100001440000001561514503541657013027 0ustar hornikusers### * cl_medoid cl_medoid <- function(x, method = "euclidean") { ## ## In principle we can get the same using pam(k = 1)$medoids. ## clusterings <- as.cl_ensemble(x) if(!length(clusterings)) stop("Cannot compute medoid of empty ensemble.") dissimilarities <- as.matrix(cl_dissimilarity(clusterings, method = method)) clusterings[[which.min(rowSums(dissimilarities))]] } ### * cl_pam cl_pam <- function(x, k, method = "euclidean", solver = c("pam", "kmedoids")) { clusterings <- as.cl_ensemble(x) if(!length(clusterings)) stop("Cannot compute medoid partition of empty ensemble.") ## Actually, we should have at least k distinct elements in the ## ensemble ... make_cl_pam <- function(class_ids, medoid_ids, medoids, criterion, description) .structure(list(cluster = class_ids, medoid_ids = medoid_ids, prototypes = medoids, criterion = criterion, description = description), class = "cl_pam") if(k == 1L) { ## Simplify matters if a global medoid is sought. dissimilarities <- cl_dissimilarity(clusterings, method = method) description <- attr(dissimilarities, "description") dissimilarities <- as.matrix(dissimilarities) row_sums <- rowSums(dissimilarities) medoid_id <- which.min(row_sums) criterion <- row_sums[medoid_id] return(make_cl_pam(as.cl_class_ids(seq_along(clusterings)), medoid_id, clusterings[medoid_id], criterion, description)) } solver <- match.arg(solver) ## Argh. We really want to run k-medoids for the unique elements of ## the ensemble, but pam() only works for symmetric dissimilarties. ## As computing cluster dissimilarities is typically expensive, use ## the unique elements for doing so in any case. values <- unique(clusterings) ## Positions of ensemble members in the unique values. positions <- match(clusterings, values) ## Dissimilarities between unique values. dissimilarities <- cl_dissimilarity(values, method = method) description <- attr(dissimilarities, "description") dissimilarities <- as.matrix(dissimilarities) ## For pam(), we need the dissimilarities for all objects. if(solver == "pam") { dissimilarities <- dissimilarities[positions, positions] party <- pam(as.dist(dissimilarities), k) class_ids <- cl_class_ids(party) medoid_ids <- cl_medoid_ids(party) medoids <- clusterings[medoid_ids] criterion <- sum(dissimilarities[cbind(seq_along(class_ids), medoid_ids[class_ids])]) } else { ## Counts of unique values. counts <- tabulate(positions) ## Weigh according to the counts. Should be straightforward to ## add "case weights" as well ... dissimilarities <- counts * dissimilarities ## Now partition. party <- kmedoids(dissimilarities, k) ## And build the solution from this ... criterion <- party$criterion ## First, things for the unique values. medoid_ids <- cl_medoid_ids(party) medoids <- values[medoid_ids] class_ids <- cl_class_ids(party) ## Second, things for all objects. class_ids <- class_ids[positions] medoid_ids <- match(medoid_ids, positions) } make_cl_pam(class_ids, medoid_ids, medoids, criterion, description) } print.cl_pam <- function(x, ...) { class_ids <- cl_class_ids(x) fmt <- "A k-medoid partition of a cluster ensemble with %d elements into %d classes (dissimilarity measure: %s)." writeLines(c(strwrap(gettextf(fmt, n_of_objects(x), n_of_classes(x), x$description)))) writeLines(gettext("Class ids:")) print(class_ids, ...) writeLines(gettext("Criterion:")) print(x$criterion, ...) invisible(x) } ### * cl_medoid_ids ## Little helper, internal for the time being ... cl_medoid_ids <- function(x) UseMethod("cl_medoid_ids") cl_medoid_ids.cl_pam <- function(x) x$medoid_ids cl_medoid_ids.kmedoids <- function(x) x$medoid_ids cl_medoid_ids.clara <- function(x) x$i.med cl_medoid_ids.pam <- function(x) x$id.med ### * kmedoids kmedoids <- function(x, k) { ## ## For the time being, 'x' is assumed a dissimilarity object or a ## matrix of dissimilarities. ## Let's worry about the interface later. ## x <- as.matrix(x) n <- nrow(x) ## Use the formulation in Gordon & Vichi (1998), Journal of ## Classification, [P4'], page 279, with variables c(vec(X), z), but ## with rows and cols interchanged (such that x_{ij} is one iff o_i ## has medoid o_j, and z_j is one iff o_j is a medoid). make_constraint_mat <- function(n) { nsq <- n * n rbind(cbind(kronecker(rbind(rep.int(1, n)), diag(1, n)), matrix(0, n, n)), cbind(diag(1, nsq), kronecker(diag(1, n), rep.int(-1, n))), c(double(nsq), rep.int(1, n)), cbind(matrix(0, n, nsq), diag(1, n))) } make_constraint_dir <- function(n) rep.int(c("=", "<=", "=", "<="), c(n, n * n, 1, n)) make_constraint_rhs <- function(n, k) rep.int(c(1, 0, k, 1), c(n, n * n, 1, n)) ## ## We could try a relaxation without integrality constraints first, ## which seems to "typically work" (and should be faster). To test ## for integrality, use something like ## if(identical(all.equal(y$solution, round(y$solution)), TRUE)) ## y <- lpSolve::lp("min", c(c(x), double(n)), make_constraint_mat(n), make_constraint_dir(n), make_constraint_rhs(n, k), int.vec = seq_len(n * (n + 1))) ## Now get the class ids and medoids. ind <- which(matrix(y$solution[seq_len(n * n)], n) > 0, arr.ind = TRUE) medoid_ids <- unique(ind[, 2L]) class_ids <- seq_len(n) class_ids[ind[, 1L]] <- match(ind[, 2L], medoid_ids) .structure(list(cluster = class_ids, medoid_ids = medoid_ids, criterion = y$objval), class = "kmedoids") } print.kmedoids <- function(x, ...) { fmt <- "A k-medoids clustering of %d objects into %d clusters." writeLines(gettextf(fmt, n_of_objects(x), n_of_classes(x))) writeLines(gettext("Medoid ids:")) print(cl_medoid_ids(x), ...) writeLines(gettext("Class ids:")) print(unclass(cl_class_ids(x)), ...) writeLines(gettext("Criterion:")) print(x$criterion, ...) invisible(x) } clue/R/prototypes.R0000644000175100001440000000346711304023136013777 0ustar hornikuserscl_prototypes <- function(x) UseMethod("cl_prototypes") ## No default method. ## Package stats: kmeans() (R 2.1.0 or better). cl_prototypes.kmeans <- function(x) x$centers ## Package cluster: clara() always gives prototypes. cl_prototypes.clara <- function(x) x$medoids ## Package cluster: fanny() never gives prototypes. ## Package cluster: pam() does not give prototypes if given a ## dissimilarity matrix. cl_prototypes.pam <- function(x) { p <- x$medoids if(!is.matrix(p)) stop("Cannot determine prototypes.") p } ## Package cba: ccfkms(). cl_prototypes.ccfkms <- cl_prototypes.kmeans ## Package cclust: cclust(). cl_prototypes.cclust <- cl_prototypes.kmeans ## Package e1071: cmeans() gives objects of class "fclust". cl_prototypes.fclust <- cl_prototypes.kmeans ## Package e1071: cshell(). cl_prototypes.cshell <- cl_prototypes.kmeans ## Package e1071: bclust(). cl_prototypes.bclust <- cl_prototypes.kmeans ## Package flexclust: kcca() returns objects of S4 class "kcca" which ## extends S4 class "flexclust". cl_prototypes.kcca <- function(x) methods::slot(x, "centers") ## Package kernlab: specc() and kkmeans() return objects of S4 class ## "specc". cl_prototypes.specc <- function(x) kernlab::centers(x) ## Package mclust: Mclust(). cl_prototypes.Mclust <- function(x) { p <- x$mu ## For multidimensional models, we get a matrix whose columns are ## the means of each group in the best model, and hence needs to be ## transposed. if(is.matrix(p)) p <- t(p) p } ## Package clue: cl_pam(). cl_prototypes.cl_pam <- function(x) x$prototypes ## Package clue: (virtual) class "cl_partition". cl_prototypes.cl_partition <- function(x) cl_prototypes(.get_representation(x)) ## Package clue: pclust(). cl_prototypes.pclust <- function(x) x$prototypes clue/vignettes/0000755000175100001440000000000015145302607013233 5ustar hornikusersclue/vignettes/clue.Rnw0000644000175100001440000016521615145302547014671 0ustar hornikusers\documentclass[fleqn]{article} \usepackage[round,longnamesfirst]{natbib} \usepackage{graphicx,keyval,hyperref,doi} \newcommand\argmin{\mathop{\mathrm{arg min}}} \newcommand\trace{\mathop{\mathrm{tr}}} \newcommand\R{{\mathbb{R}}} \newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}} \newcommand{\sQuote}[1]{`{#1}'} \newcommand{\dQuote}[1]{``{#1}''} \let\code=\texttt \newcommand{\file}[1]{\sQuote{\textsf{#1}}} \newcommand{\class}[1]{\code{"#1"}} \SweaveOpts{strip.white=true} \AtBeginDocument{\setkeys{Gin}{width=0.6\textwidth}} \date{2007-06-28} \title{A CLUE for CLUster Ensembles} \author{Kurt Hornik} %% \VignetteIndexEntry{CLUster Ensembles} \sloppy{} \begin{document} \maketitle \begin{abstract} Cluster ensembles are collections of individual solutions to a given clustering problem which are useful or necessary to consider in a wide range of applications. The R package~\pkg{clue} provides an extensible computational environment for creating and analyzing cluster ensembles, with basic data structures for representing partitions and hierarchies, and facilities for computing on these, including methods for measuring proximity and obtaining consensus and ``secondary'' clusterings. \end{abstract} <>= options(width = 60) library("clue") @ % \section{Introduction} \label{sec:introduction} \emph{Cluster ensembles} are collections of clusterings, which are all of the same ``kind'' (e.g., collections of partitions, or collections of hierarchies), of a set of objects. Such ensembles can be obtained, for example, by varying the (hyper)parameters of a ``base'' clustering algorithm, by resampling or reweighting the set of objects, or by employing several different base clusterers. Questions of ``agreement'' in cluster ensembles, and obtaining ``consensus'' clusterings from it, have been studied in several scientific communities for quite some time now. A special issue of the Journal of Classification was devoted to ``Comparison and Consensus of Classifications'' \citep{cluster:Day:1986} almost two decades ago. The recent popularization of ensemble methods such as Bayesian model averaging \citep{cluster:Hoeting+Madigan+Raftery:1999}, bagging \citep{cluster:Breiman:1996} and boosting \citep{cluster:Friedman+Hastie+Tibshirani:2000}, typically in a supervised leaning context, has also furthered the research interest in using ensemble methods to improve the quality and robustness of cluster solutions. Cluster ensembles can also be utilized to aggregate base results over conditioning or grouping variables in multi-way data, to reuse existing knowledge, and to accommodate the needs of distributed computing, see e.g.\ \cite{cluster:Hornik:2005a} and \cite{cluster:Strehl+Ghosh:2003a} for more information. Package~\pkg{clue} is an extension package for R~\citep{cluster:R:2005} providing a computational environment for creating and analyzing cluster ensembles. In Section~\ref{sec:structures+algorithms}, we describe the underlying data structures, and the functionality for measuring proximity, obtaining consensus clusterings, and ``secondary'' clusterings. Four examples are discussed in Section~\ref{sec:examples}. Section~\ref{sec:outlook} concludes the paper. A previous version of this manuscript was published in the \emph{Journal of Statistical Software} \citep{cluster:Hornik:2005b}. \section{Data structures and algorithms} \label{sec:structures+algorithms} \subsection{Partitions and hierarchies} Representations of clusterings of objects greatly vary across the multitude of methods available in R packages. For example, the class ids (``cluster labels'') for the results of \code{kmeans()} in base package~\pkg{stats}, \code{pam()} in recommended package~\pkg{cluster}~\citep{cluster:Rousseeuw+Struyf+Hubert:2005, cluster:Struyf+Hubert+Rousseeuw:1996}, and \code{Mclust()} in package~\pkg{mclust}~\citep{cluster:Fraley+Raftery+Wehrens:2005, cluster:Fraley+Raftery:2003}, are available as components named \code{cluster}, \code{clustering}, and \code{classification}, respectively, of the R objects returned by these functions. In many cases, the representations inherit from suitable classes. (We note that for versions of R prior to 2.1.0, \code{kmeans()} only returned a ``raw'' (unclassed) result, which was changed alongside the development of \pkg{clue}.) We deal with this heterogeneity of representations by providing getters for the key underlying data, such as the number of objects from which a clustering was obtained, and predicates, e.g.\ for determining whether an R object represents a partition of objects or not. These getters, such as \code{n\_of\_objects()}, and predicates are implemented as S3 generics, so that there is a \emph{conceptual}, but no formal class system underlying the predicates. Support for classed representations can easily be added by providing S3 methods. \subsubsection{Partitions} The partitions considered in \pkg{clue} are possibly soft (``fuzzy'') partitions, where for each object~$i$ and class~$j$ there is a non-negative number~$\mu_{ij}$ quantifying the ``belongingness'' or \emph{membership} of object~$i$ to class~$j$, with $\sum_j \mu_{ij} = 1$. For hard (``crisp'') partitions, all $\mu_{ij}$ are in $\{0, 1\}$. We can gather the $\mu_{ij}$ into the \emph{membership matrix} $M = [\mu_{ij}]$, where rows correspond to objects and columns to classes. The \emph{number of classes} of a partition, computed by function \code{n\_of\_classes()}, is the number of $j$ for which $\mu_{ij} > 0$ for at least one object~$i$. This may be less than the number of ``available'' classes, corresponding to the number of columns in a membership matrix representing the partition. The predicate functions \code{is.cl\_partition()}, \code{is.cl\_hard\_partition()}, and \code{is.cl\_soft\_partition()} are used to indicate whether R objects represent partitions of objects of the respective kind, with hard partitions as characterized above (all memberships in $\{0, 1\}$). (Hence, ``fuzzy clustering'' algorithms can in principle also give a hard partition.) \code{is.cl\_partition()} and \code{is.cl\_hard\_partition()} are generic functions; \code{is.cl\_soft\_partition()} gives true iff \code{is.cl\_partition()} is true and \code{is.cl\_hard\_partition()} is false. For R objects representing partitions, function \code{cl\_membership()} computes an R object with the membership values, currently always as a dense membership matrix with additional attributes. This is obviously rather inefficient for computations on hard partitions; we are planning to add ``canned'' sparse representations (using the vector of class ids) in future versions. Function \code{as.cl\_membership()} can be used for coercing \dQuote{raw} class ids (given as atomic vectors) or membership values (given as numeric matrices) to membership objects. Function \code{cl\_class\_ids()} determines the class ids of a partition. For soft partitions, the class ids returned are those of the \dQuote{nearest} hard partition obtained by taking the class ids of the (first) maximal membership values. Note that the cardinality of the set of the class ids may be less than the number of classes in the (soft) partition. Many partitioning methods are based on \emph{prototypes} (``centers''). In typical cases, these are points~$p_j$ in the same feature space the measurements~$x_i$ on the objects~$i$ to be partitioned are in, so that one can measure distance between objects and prototypes, and e.g.\ classify objects to their closest prototype. Such partitioning methods can also induce partitions of the entire feature space (rather than ``just'' the set of objects to be partitioned). Currently, package \pkg{clue} has only minimal support for this ``additional'' structure, providing a \code{cl\_prototypes()} generic for extracting the prototypes, and is mostly focused on computations on partitions which are based on their memberships. Many algorithms resulting in partitions of a given set of objects can be taken to induce a partition of the underlying feature space for the measurements on the objects, so that class memberships for ``new'' objects can be obtained from the induced partition. Examples include partitions based on assigning objects to their ``closest'' prototypes, or providing mixture models for the distribution of objects in feature space. Package~\pkg{clue} provides a \code{cl\_predict()} generic for predicting the class memberships of new objects (if possible). Function \code{cl\_fuzziness()} computes softness (fuzziness) measures for (ensembles) of partitions. Built-in measures are the partition coefficient \label{PC} and partition entropy \citep[e.g.,][]{cluster:Bezdek:1981}, with an option to normalize in a way that hard partitions and the ``fuzziest'' possible partition (where all memberships are the same) get fuzziness values of zero and one, respectively. Note that this normalization differs from ``standard'' ones in the literature. In the sequel, we shall also use the concept of the \emph{co-membership matrix} $C(M) = M M'$, where $'$ denotes matrix transposition, of a partition. For hard partitions, an entry $c_{ij}$ of $C(M)$ is 1 iff the corresponding objects $i$ and $j$ are in the same class, and 0 otherwise. \subsubsection{Hierarchies} The hierarchies considered in \pkg{clue} are \emph{total indexed hierarchies}, also known as \emph{$n$-valued trees}, and hence correspond in a one-to-one manner to \emph{ultrametrics} (distances $u_{ij}$ between pairs of objects $i$ and $j$ which satisfy the ultrametric constraint $u_{ij} = \max(u_{ik}, u_{jk})$ for all triples $i$, $j$, and $k$). See e.g.~\citet[Page~69--71]{cluster:Gordon:1999}. Function \code{cl\_ultrametric(x)} computes the associated ultrametric from an R object \code{x} representing a hierarchy of objects. If \code{x} is not an ultrametric, function \code{cophenetic()} in base package~\pkg{stats} is used to obtain the ultrametric (also known as cophenetic) distances from the hierarchy, which in turn by default calls the S3 generic \code{as.hclust()} (also in \pkg{stats}) on the hierarchy. Support for classes which represent hierarchies can thus be added by providing \code{as.hclust()} methods for this class. In R~2.1.0 or better (again as part of the work on \pkg{clue}), \code{cophenetic} is an S3 generic as well, and one can also more directly provide methods for this if necessary. In addition, there is a generic function \code{as.cl\_ultrametric()} which can be used for coercing \emph{raw} (non-classed) ultrametrics, represented as numeric vectors (of the lower-half entries) or numeric matrices, to ultrametric objects. Finally, the generic predicate function \code{is.cl\_hierarchy()} is used to determine whether an R object represents a hierarchy or not. Ultrametric objects can also be coerced to classes~\class{dendrogram} and \class{hclust} (from base package~\pkg{stats}), and hence in particular use the \code{plot()} methods for these classes. By default, plotting an ultrametric object uses the plot method for dendrograms. Obtaining a hierarchy on a given set of objects can be thought of as transforming the pairwise dissimilarities between the objects (which typically do not yet satisfy the ultrametric constraints) into an ultrametric. Ideally, this ultrametric should be as close as possible to the dissimilarities. In some important cases, explicit solutions are possible (e.g., ``standard'' hierarchical clustering with single or complete linkage gives the optimal ultrametric dominated by or dominating the dissimilarities, respectively). On the other hand, the problem of finding the closest ultrametric in the least squares sense is known to be NP-hard \citep{cluster:Krivanek+Moravek:1986,cluster:Krivanek:1986}. One important class of heuristics for finding least squares fits is based on iterative projection on convex sets of constraints \citep{cluster:Hubert+Arabie:1995}. \label{SUMT} Function \code{ls\_fit\_ultrametric()} follows \cite{cluster:DeSoete:1986} to use an SUMT \citep[Sequential Unconstrained Minimization Technique;][]{cluster:Fiacco+McCormick:1968} approach in turn simplifying the suggestions in \cite{cluster:Carroll+Pruzansky:1980}. Let $L(u)$ be the function to be minimized over all $u$ in some constrained set $\mathcal{U}$---in our case, $L(u) = \sum (d_{ij}-u_{ij})^2$ is the least squares criterion, and $\mathcal{U}$ is the set of all ultrametrics $u$. One iteratively minimizes $L(u) + \rho_k P(u)$, where $P(u)$ is a non-negative function penalizing violations of the constraints such that $P(u)$ is zero iff $u \in \mathcal{U}$. The $\rho$ values are increased according to the rule $\rho_{k+1} = q \rho_k$ for some constant $q > 1$, until convergence is obtained in the sense that e.g.\ the Euclidean distance between successive solutions $u_k$ and $u_{k+1}$ is small enough. Optionally, the final $u_k$ is then suitably projected onto $\mathcal{U}$. For \code{ls\_fit\_ultrametric()}, we obtain the starting value $u_0$ by \dQuote{random shaking} of the given dissimilarity object, and use the penalty function $P(u) = \sum_{\Omega} (u_{ij} - u_{jk}) ^ 2$, were $\Omega$ contains all triples $i, j, k$ for which $u_{ij} \le \min(u_{ik}, u_{jk})$ and $u_{ik} \ne u_{jk}$, i.e., for which $u$ violates the ultrametric constraints. The unconstrained minimizations are carried out using either \code{optim()} or \code{nlm()} in base package~\pkg{stats}, with analytic gradients given in \cite{cluster:Carroll+Pruzansky:1980}. This ``works'', even though we note however that $P$ is not even a continuous function, which seems to have gone unnoticed in the literature! (Consider an ultrametric $u$ for which $u_{ij} = u_{ik} < u_{jk}$ for some $i, j, k$ and define $u(\delta)$ by changing the $u_{ij}$ to $u_{ij} + \delta$. For $u$, both $(i,j,k)$ and $(j,i,k)$ are in the violation set $\Omega$, whereas for all $\delta$ sufficiently small, only $(j,i,k)$ is the violation set for $u(\delta)$. Hence, $\lim_{\delta\to 0} P(u(\delta)) = P(u) + (u_{ij} - u_{ik})^2$. This shows that $P$ is discontinuous at all non-constant $u$ with duplicated entries. On the other hand, it is continuously differentiable at all $u$ with unique entries.) Hence, we need to turn off checking analytical gradients when using \code{nlm()} for minimization. The default optimization using conjugate gradients should work reasonably well for medium to large size problems. For \dQuote{small} ones, using \code{nlm()} is usually faster. Note that the number of ultrametric constraints is of the order $n^3$, suggesting to use the SUMT approach in favor of \code{constrOptim()} in \pkg{stats}. It should be noted that the SUMT approach is a heuristic which can not be guaranteed to find the global minimum. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the base algorithm. \subsubsection{Extensibility} The methods provided in package~\pkg{clue} handle the partitions and hierarchies obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}~\citep{cluster:Hornik+Hothorn+Karatzoglou:2006}, \pkg{cba}~\citep{cluster:Buchta+Hahsler:2005}, \pkg{cclust}~\citep{cluster:Dimitriadou:2005}, \pkg{cluster}, \pkg{e1071}~\citep{cluster:Dimitriadou+Hornik+Leisch:2005}, \pkg{flexclust}~\citep{cluster:Leisch:2006a}, \pkg{flexmix}~\citep{cluster:Leisch:2004}, \pkg{kernlab}~\citep{cluster:Karatzoglou+Smola+Hornik:2004}, and \pkg{mclust} (and of course, \pkg{clue} itself). Extending support to other packages is straightforward, provided that clusterings are instances of classes. Suppose e.g.\ that a package has a function \code{glvq()} for ``generalized'' (i.e., non-Euclidean) Learning Vector Quantization which returns an object of class~\class{glvq}, in turn being a list with component \code{class\_ids} containing the class ids. To integrate this into the \pkg{clue} framework, all that is necessary is to provide the following methods. <<>>= cl_class_ids.glvq <- function(x) as.cl_class_ids(x$class_ids) is.cl_partition.glvq <- function(x) TRUE is.cl_hard_partition.glvq <- function(x) TRUE @ % $ \subsection{Cluster ensembles} Cluster ensembles are realized as lists of clusterings with additional class information. All clusterings in an ensemble must be of the same ``kind'' (i.e., either all partitions as known to \code{is.cl\_partition()}, or all hierarchies as known to \code{is.cl\_hierarchy()}, respectively), and have the same number of objects. If all clusterings are partitions, the list realizing the ensemble has class~\class{cl\_partition\_ensemble} and inherits from \class{cl\_ensemble}; if all clusterings are hierarchies, it has class~\class{cl\_hierarchy\_ensemble} and inherits from \class{cl\_ensemble}. Empty ensembles cannot be categorized according to the kind of clusterings they contain, and hence only have class~\class{cl\_ensemble}. Function \code{cl\_ensemble()} creates a cluster ensemble object from clusterings given either one-by-one, or as a list passed to the \code{list} argument. As unclassed lists could be used to represent single clusterings (in particular for results from \code{kmeans()} in versions of R prior to 2.1.0), we prefer not to assume that an unnamed given list is a list of clusterings. \code{cl\_ensemble()} verifies that all given clusterings are of the same kind, and all have the same number of objects. (By the notion of cluster ensembles, we should in principle verify that the clusterings come from the \emph{same} objects, which of course is not always possible.) The list representation makes it possible to use \code{lapply()} for computations on the individual clusterings in (i.e., the components of) a cluster ensemble. Available methods for cluster ensembles include those for subscripting, \code{c()}, \code{rep()}, \code{print()}, and \code{unique()}, where the last is based on a \code{unique()} method for lists added in R~2.1.1, and makes it possible to find unique and duplicated elements in cluster ensembles. The elements of the ensemble can be tabulated using \code{cl\_tabulate()}. Function \code{cl\_boot()} generates cluster ensembles with bootstrap replicates of the results of applying a \dQuote{base} clustering algorithm to a given data set. Currently, this is a rather simple-minded function with limited applicability, and mostly useful for studying the effect of (uncontrolled) random initializations of fixed-point partitioning algorithms such as \code{kmeans()} or \code{cmeans()} in package~\pkg{e1071}. To study the effect of varying control parameters or explicitly providing random starting values, the respective cluster ensemble has to be generated explicitly (most conveniently by using \code{replicate()} to create a list \code{lst} of suitable instances of clusterings obtained by the base algorithm, and using \code{cl\_ensemble(list = lst)} to create the ensemble). Resampling the training data is possible for base algorithms which can predict the class memberships of new data using \code{cl\_predict} (e.g., by classifying the out-of-bag data to their closest prototype). In fact, we believe that for unsupervised learning methods such as clustering, \emph{reweighting} is conceptually superior to resampling, and have therefore recently enhanced package~\pkg{e1071} to provide an implementation of weighted fuzzy $c$-means, and package~\pkg{flexclust} contains an implementation of weighted $k$-means. We are currently experimenting with interfaces for providing ``direct'' support for reweighting via \code{cl\_boot()}. \subsection{Cluster proximities} \subsubsection{Principles} Computing dissimilarities and similarities (``agreements'') between clusterings of the same objects is a key ingredient in the analysis of cluster ensembles. The ``standard'' data structures available for such proximity data (measures of similarity or dissimilarity) are classes~\class{dist} and \class{dissimilarity} in package~\pkg{cluster} (which basically, but not strictly, extends \class{dist}), and are both not entirely suited to our needs. First, they are confined to \emph{symmetric} dissimilarity data. Second, they do not provide enough reflectance. We also note that the Bioconductor package~\pkg{graph}~\citep{cluster:Gentleman+Whalen:2005} contains an efficient subscript method for objects of class~\class{dist}, but returns a ``raw'' matrix for row/column subscripting. For package~\pkg{clue}, we use the following approach. There are classes for symmetric and (possibly) non-symmetric proximity data (\class{cl\_proximity} and \class{cl\_cross\_proximity}), which, in addition to holding the numeric data, also contain a description ``slot'' (attribute), currently a character string, as a first approximation to providing more reflectance. Internally, symmetric proximity data are store the lower diagonal proximity values in a numeric vector (in row-major order), i.e., the same way as objects of class~\class{dist}; a \code{self} attribute can be used for diagonal values (in case some of these are non-zero). Symmetric proximity objects can be coerced to dense matrices using \code{as.matrix()}. It is possible to use 2-index matrix-style subscripting for symmetric proximity objects; unless this uses identical row and column indices, it results in a non-symmetric proximity object. This approach ``propagates'' to classes for symmetric and (possibly) non-symmetric cluster dissimilarity and agreement data (e.g., \class{cl\_dissimilarity} and \class{cl\_cross\_dissimilarity} for dissimilarity data), which extend the respective proximity classes. Ultrametric objects are implemented as symmetric proximity objects with a dissimilarity interpretation so that self-proximities are zero, and inherit from classes~\class{cl\_dissimilarity} and \class{cl\_proximity}. Providing reflectance is far from optimal. For example, if \code{s} is a similarity object (with cluster agreements), \code{1 - s} is a dissimilarity one, but the description is preserved unchanged. This issue could be addressed by providing high-level functions for transforming proximities. \label{synopsis} Cluster dissimilarities are computed via \code{cl\_dissimilarity()} with synopsis \code{cl\_dissimilarity(x, y = NULL, method = "euclidean")}, where \code{x} and \code{y} are cluster ensemble objects or coercible to such, or \code{NULL} (\code{y} only). If \code{y} is \code{NULL}, the return value is an object of class~\class{cl\_dissimilarity} which contains the dissimilarities between all pairs of clusterings in \code{x}. Otherwise, it is an object of class~\class{cl\_cross\_dissimilarity} with the dissimilarities between the clusterings in \code{x} and the clusterings in \code{y}. Formal argument \code{method} is either a character string specifying one of the built-in methods for computing dissimilarity, or a function to be taken as a user-defined method, making it reasonably straightforward to add methods. Function \code{cl\_agreement()} has the same interface as \code{cl\_dissimilarity()}, returning cluster similarity objects with respective classes~\class{cl\_agreement} and \class{cl\_cross\_agreement}. Built-in methods for computing dissimilarities may coincide (in which case they are transforms of each other), but do not necessarily do so, as there typically are no canonical transformations. E.g., according to needs and scientific community, agreements might be transformed to dissimilarities via $d = - \log(s)$ or the square root thereof \citep[e.g.,][]{cluster:Strehl+Ghosh:2003b}, or via $d = 1 - s$. \subsubsection{Partition proximities} When assessing agreement or dissimilarity of partitions, one needs to consider that the class ids may be permuted arbitrarily without changing the underlying partitions. For membership matrices~$M$, permuting class ids amounts to replacing $M$ by $M \Pi$, where $\Pi$ is a suitable permutation matrix. We note that the co-membership matrix $C(M) = MM'$ is unchanged by these transformations; hence, proximity measures based on co-occurrences, such as the Katz-Powell \citep{cluster:Katz+Powell:1953} or Rand \citep{cluster:Rand:1971} indices, do not explicitly need to adjust for possible re-labeling. The same is true for measures based on the ``confusion matrix'' $M' \tilde{M}$ of two membership matrices $M$ and $\tilde{M}$ which are invariant under permutations of rows and columns, such as the Normalized Mutual Information (NMI) measure introduced in \cite{cluster:Strehl+Ghosh:2003a}. Other proximity measures need to find permutations so that the classes are optimally matched, which of course in general requires exhaustive search through all $k!$ possible permutations, where $k$ is the (common) number of classes in the partitions, and thus will typically be prohibitively expensive. Fortunately, in some important cases, optimal matchings can be determined very efficiently. We explain this in detail for ``Euclidean'' partition dissimilarity and agreement (which in fact is the default measure used by \code{cl\_dissimilarity()} and \code{cl\_agreement()}). Euclidean partition dissimilarity \citep{cluster:Dimitriadou+Weingessel+Hornik:2002} is defined as \begin{displaymath} d(M, \tilde{M}) = \min\nolimits_\Pi \| M - \tilde{M} \Pi \| \end{displaymath} where the minimum is taken over all permutation matrices~$\Pi$, $\|\cdot\|$ is the Frobenius norm (so that $\|Y\|^2 = \trace(Y'Y)$), and $n$ is the (common) number of objects in the partitions. As $\| M - \tilde{M} \Pi \|^2 = \trace(M'M) - 2 \trace(M'\tilde{M}\Pi) + \trace(\Pi'\tilde{M}'\tilde{M}\Pi) = \trace(M'M) - 2 \trace(M'\tilde{M}\Pi) + \trace(\tilde{M}'\tilde{M})$, we see that minimizing $\| M - \tilde{M} \Pi \|^2$ is equivalent to maximizing $\trace(M'\tilde{M}\Pi) = \sum_{i,k}{\mu_{ik}\tilde{\mu}}_{i,\pi(k)}$, which for hard partitions is the number of objects with the same label in the partitions given by $M$ and $\tilde{M}\Pi$. Finding the optimal $\Pi$ is thus recognized as an instance of the \emph{linear sum assignment problem} (LSAP, also known as the weighted bipartite graph matching problem). The LSAP can be solved by linear programming, e.g., using Simplex-style primal algorithms as done by function~\code{lp.assign()} in package~\pkg{lpSolve}~\citep{cluster:Buttrey:2005}, but primal-dual algorithms such as the so-called Hungarian method can be shown to find the optimum in time $O(k^3)$ \citep[e.g.,][]{cluster:Papadimitriou+Steiglitz:1982}. Available published implementations include TOMS 548 \citep{cluster:Carpaneto+Toth:1980}, which however is restricted to integer weights and $k < 131$. One can also transform the LSAP into a network flow problem, and use e.g.~RELAX-IV \citep{cluster:Bertsekas+Tseng:1994} for solving this, as is done in package~\pkg{optmatch}~\citep{cluster:Hansen:2005}. In package~\pkg{clue}, we use an efficient C implementation of the Hungarian algorithm kindly provided to us by Walter B\"ohm, which has been found to perform very well across a wide range of problem sizes. \cite{cluster:Gordon+Vichi:2001} use a variant of Euclidean dissimilarity (``GV1 dissimilarity'') which is based on the sum of the squared difference of the memberships of matched (non-empty) classes only, discarding the unmatched ones (see their Example~2). This results in a measure which is discontinuous over the space of soft partitions with arbitrary numbers of classes. The partition agreement measures ``angle'' and ``diag'' (maximal cosine of angle between the memberships, and maximal co-classification rate, where both maxima are taken over all column permutations of the membership matrices) are based on solving the same LSAP as for Euclidean dissimilarity. Finally, Manhattan partition dissimilarity is defined as the minimal sum of the absolute differences of $M$ and all column permutations of $\tilde{M}$, and can again be computed efficiently by solving an LSAP. For hard partitions, both Manhattan and squared Euclidean dissimilarity give twice the \emph{transfer distance} \citep{cluster:Charon+Denoeud+Guenoche:2006}, which is the minimum number of objects that must be removed so that the implied partitions (restrictions to the remaining objects) are identical. This is also known as the \emph{$R$-metric} in \cite{cluster:Day:1981}, i.e., the number of augmentations and removals of single objects needed to transform one partition into the other, and the \emph{partition-distance} in \cite{cluster:Gusfield:2002}. Note when assessing proximity that agreements for soft partitions are always (and quite often considerably) lower than the agreements for the corresponding nearest hard partitions, unless the agreement measures are based on the latter anyways (as currently done for Rand, Katz-Powell, and NMI). Package~\pkg{clue} provides additional agreement measures, such as the Jaccard and Fowles-Mallows \citep[quite often incorrectly attributed to \cite{cluster:Wallace:1983}]{cluster:Fowlkes+Mallows:1983a} indices, and dissimilarity measures such as the ``symdiff'' and Rand distances (the latter is proportional to the metric of \cite{cluster:Mirkin:1996}) and the metrics discussed in \cite{cluster:Boorman+Arabie:1972}. One could easily add more proximity measures, such as the ``Variation of Information'' \citep{cluster:Meila:2003}. However, all these measures are rigorously defined for hard partitions only. To see why extensions to soft partitions are far from straightforward, consider e.g.\ measures based on the confusion matrix. Its entries count the cardinality of certain intersections of sets. \label{fuzzy} In a fuzzy context for soft partitions, a natural generalization would be using fuzzy cardinalities (i.e., sums of memberships values) of fuzzy intersections instead. There are many possible choices for the latter, with the product of the membership values (corresponding to employing the confusion matrix also in the fuzzy case) one of them, but the minimum instead of the product being the ``usual'' choice. A similar point can be made for co-occurrences of soft memberships. We are not aware of systematic investigations of these extension issues. \subsubsection{Hierarchy proximities} Available built-in dissimilarity measures for hierarchies include \emph{Euclidean} (again, the default measure used by \code{cl\_dissimilarity()}) and Manhattan dissimilarity, which are simply the Euclidean (square root of the sum of squared differences) and Manhattan (sum of the absolute differences) dissimilarities between the associated ultrametrics. Cophenetic dissimilarity is defined as $1 - c^2$, where $c$ is the cophenetic correlation coefficient \citep{cluster:Sokal+Rohlf:1962}, i.e., the Pearson product-moment correlation between the ultrametrics. Gamma dissimilarity is the rate of inversions between the associated ultrametrics $u$ and $v$ (i.e., the rate of pairs $(i,j)$ and $(k,l)$ for which $u_{ij} < u_{kl}$ and $v_{ij} > v_{kl}$). This measure is a linear transformation of Kruskal's~$\gamma$. Finally, symdiff dissimilarity is the cardinality of the symmetric set difference of the sets of classes (hierarchies in the strict sense) induced by the dendrograms. Associated agreement measures are obtained by suitable transformations of the dissimilarities~$d$; for Euclidean proximities, we prefer to use $1 / (1 + d)$ rather than e.g.\ $\exp(-d)$. One should note that whereas cophenetic and gamma dissimilarities are invariant to linear transformations, Euclidean and Manhattan ones are not. Hence, if only the relative ``structure'' of the dendrograms is of interest, these dissimilarities should only be used after transforming the ultrametrics to a common range of values (e.g., to $[0,1]$). \subsection{Consensus clusterings} Consensus clusterings ``synthesize'' the information in the elements of a cluster ensemble into a single clustering. There are three main approaches to obtaining consensus clusterings \citep{cluster:Hornik:2005a,cluster:Gordon+Vichi:2001}: in the \emph{constructive} approach, one specifies a way to construct a consensus clustering. In the \emph{axiomatic} approach, emphasis is on the investigation of existence and uniqueness of consensus clusterings characterized axiomatically. The \emph{optimization} approach formalizes the natural idea of describing consensus clusterings as the ones which ``optimally represent the ensemble'' by providing a criterion to be optimized over a suitable set $\mathcal{C}$ of possible consensus clusterings. If $d$ is a dissimilarity measure and $C_1, \ldots, C_B$ are the elements of the ensemble, one can e.g.\ look for solutions of the problem \begin{displaymath} \sum\nolimits_{b=1}^B w_b d(C, C_b) ^ p \Rightarrow \min\nolimits_{C \in \mathcal{C}}, \end{displaymath} for some $p \ge 0$, i.e., as clusterings~$C^*$ minimizing weighted average dissimilarity powers of order~$p$. Analogously, if a similarity measure is given, one can look for clusterings maximizing weighted average similarity powers. Following \cite{cluster:Gordon+Vichi:1998}, an above $C^*$ is referred to as (weighted) \emph{median} or \emph{medoid} clustering if $p = 1$ and the optimum is sought over the set of all possible base clusterings, or the set $\{ C_1, \ldots, C_B \}$ of the base clusterings, respectively. For $p = 2$, we have \emph{least squares} consensus clusterings (generalized means). For computing consensus clusterings, package~\pkg{clue} provides function \code{cl\_consensus()} with synopsis \code{cl\_consensus(x, method = NULL, weights = 1, control = list())}. This allows (similar to the functions for computing cluster proximities, see Section~\ref{synopsis} on Page~\pageref{synopsis}) argument \code{method} to be a character string specifying one of the built-in methods discussed below, or a function to be taken as a user-defined method (taking an ensemble, the case weights, and a list of control parameters as its arguments), again making it reasonably straightforward to add methods. In addition, function~\code{cl\_medoid()} can be used for obtaining medoid partitions (using, in principle, arbitrary dissimilarities). Modulo possible differences in the case of ties, this gives the same results as (the medoid obtained by) \code{pam()} in package~\pkg{cluster}. If all elements of the ensemble are partitions, package~\pkg{clue} provides algorithms for computing soft least squares consensus partitions for weighted Euclidean, GV1 and co-membership dissimilarities. Let $M_1, \ldots, M_B$ and $M$ denote the membership matrices of the elements of the ensemble and their sought least squares consensus partition, respectively. For Euclidean dissimilarity, we need to find \begin{displaymath} \sum_b w_b \min\nolimits_{\Pi_b} \| M - M_b \Pi_b \|^2 \Rightarrow \min\nolimits_M \end{displaymath} over all membership matrices (i.e., stochastic matrices) $M$, or equivalently, \begin{displaymath} \sum_b w_b \| M - M_b \Pi_b \|^2 \Rightarrow \min\nolimits_{M, \Pi_1, \ldots, \Pi_B} \end{displaymath} over all $M$ and permutation matrices $\Pi_1, \ldots, \Pi_B$. Now fix the $\Pi_b$ and let $\bar{M} = s^{-1} \sum_b w_b M_b \Pi_b$ be the weighted average of the $M_b \Pi_b$, where $s = \sum_b w_b$. Then \begin{eqnarray*} \lefteqn{\sum_b w_b \| M - M_b \Pi_b \|^2} \\ &=& \sum_b w_b (\|M\|^2 - 2 \trace(M' M_b \Pi_b) + \|M_b\Pi_b\|^2) \\ &=& s \|M\|^2 - 2 s \trace(M' \bar{M}) + \sum_b w_b \|M_b\|^2 \\ &=& s (\|M - \bar{M}\|^2) + \sum_b w_b \|M_b\|^2 - s \|\bar{M}\|^2 \end{eqnarray*} Thus, as already observed in \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} and \cite{cluster:Gordon+Vichi:2001}, for fixed permutations $\Pi_b$ the optimal soft $M$ is given by $\bar{M}$. The optimal permutations can be found by minimizing $- s \|\bar{M}\|^2$, or equivalently, by maximizing \begin{displaymath} s^2 \|\bar{M}\|^2 = \sum_{\beta, b} w_\beta w_b \trace(\Pi_\beta'M_\beta'M_b\Pi_b). \end{displaymath} With $U_{\beta,b} = w_\beta w_b M_\beta' M_b$ we can rewrite the above as \begin{displaymath} \sum_{\beta, b} w_\beta w_b \trace(\Pi_\beta'M_\beta'M_b\Pi_b) = \sum_{\beta,b} \sum_{j=1}^k [U_{\beta,b}]_{\pi_\beta(j), \pi_b(j)} =: \sum_{j=1}^k c_{\pi_1(j), \ldots, \pi_B(j)} \end{displaymath} This is an instance of the \emph{multi-dimensional assignment problem} (MAP), which, contrary to the LSAP, is known to be NP-hard \citep[e.g., via reduction to 3-DIMENSIONAL MATCHING,][]{cluster:Garey+Johnson:1979}, and can e.g.\ be approached using randomized parallel algorithms \citep{cluster:Oliveira+Pardalos:2004}. Branch-and-bound approaches suggested in the literature \citep[e.g.,][]{cluster:Grundel+Oliveira+Pardalos:2005} are unfortunately computationally infeasible for ``typical'' sizes of cluster ensembles ($B \ge 20$, maybe even in the hundreds). Package~\pkg{clue} provides two heuristics for (approximately) finding the soft least squares consensus partition for Euclidean dissimilarity. Method \code{"DWH"} of function \code{cl\_consensus()} is an extension of the greedy algorithm in \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} which is based on a single forward pass through the ensemble which in each step chooses the ``locally'' optimal $\Pi$. Starting with $\tilde{M}_1 = M_1$, $\tilde{M}_b$ is obtained from $\tilde{M}_{b-1}$ by optimally matching $M_b \Pi_b$ to this, and taking a weighted average of $\tilde{M}_{b-1}$ and $M_b \Pi_b$ in a way that $\tilde{M}_b$ is the weighted average of the first~$b$ $M_\beta \Pi_\beta$. This simple approach could be further enhanced via back-fitting or several passes, in essence resulting in an ``on-line'' version of method \code{"SE"}. This, in turn, is a fixed-point algorithm, which iterates between updating $M$ as the weighted average of the current $M_b \Pi_b$, and determining the $\Pi_b$ by optimally matching the current $M$ to the individual $M_b$. Finally, method \code{"GV1"} implements the fixed-point algorithm for the ``first model'' in \cite{cluster:Gordon+Vichi:2001}, which gives least squares consensus partitions for GV1 dissimilarity. In the above, we implicitly assumed that all partitions in the ensemble as well as the sought consensus partition have the same number of classes. The more general case can be dealt with through suitable ``projection'' devices. When using co-membership dissimilarity, the least squares consensus partition is determined by minimizing \begin{eqnarray*} \lefteqn{\sum_b w_b \|MM' - M_bM_b'\|^2} \\ &=& s \|MM' - \bar{C}\|^2 + \sum_b w_b \|M_bM_b'\|^2 - s \|\bar{C}\|^2 \end{eqnarray*} over all membership matrices~$M$, where now $\bar{C} = s^{-1} \sum_b C(M_b) = s^{-1} \sum_b M_bM_b'$ is the weighted average co-membership matrix of the ensemble. This corresponds to the ``third model'' in \cite{cluster:Gordon+Vichi:2001}. Method \code{"GV3"} of function \code{cl\_consensus()} provides a SUMT approach (see Section~\ref{SUMT} on Page~\pageref{SUMT}) for finding the minimum. We note that this strategy could more generally be applied to consensus problems of the form \begin{displaymath} \sum_b w_b \|\Phi(M) - \Phi(M_b)\|^2 \Rightarrow \min\nolimits_M, \end{displaymath} which are equivalent to minimizing $\|\Phi(B) - \bar{\Phi}\|^2$, with $\bar{\Phi}$ the weighted average of the $\Phi(M_b)$. This includes e.g.\ the case where generalized co-memberships are defined by taking the ``standard'' fuzzy intersection of co-incidences, as discussed in Section~\ref{fuzzy} on Page~\pageref{fuzzy}. Package~\pkg{clue} currently does not provide algorithms for obtaining \emph{hard} consensus partitions, as e.g.\ done in \cite{cluster:Krieger+Green:1999} using Rand proximity. It seems ``natural'' to extend the methods discussed above to include a constraint on softness, e.g., on the partition coefficient PC (see Section~\ref{PC} on Page~\pageref{PC}). For Euclidean dissimilarity, straightforward Lagrangian computations show that the constrained minima are of the form $\bar{M}(\alpha) = \alpha \bar{M} + (1 - \alpha) E$, where $E$ is the ``maximally soft'' membership with all entries equal to $1/k$, $\bar{M}$ is again the weighted average of the $M_b\Pi_b$ with the $\Pi_b$ solving the underlying MAP, and $\alpha$ is chosen such that $PC(\bar{M}(\alpha))$ equals a prescribed value. As $\alpha$ increases (even beyond one), softness of the $\bar{M}(\alpha)$ decreases. However, for $\alpha^* > 1 / (1 - k\mu^*)$, where $\mu^*$ is the minimum of the entries of $\bar{M}$, the $\bar{M}(\alpha)$ have negative entries, and are no longer feasible membership matrices. Obviously, the non-negativity constraints for the $\bar{M}(\alpha)$ eventually put restrictions on the admissible $\Pi_b$ in the underlying MAP. Thus, such a simple relaxation approach to obtaining optimal hard partitions is not feasible. For ensembles of hierarchies, \code{cl\_consensus()} provides a built-in method (\code{"cophenetic"}) for approximately minimizing average weighted squared Euclidean dissimilarity \begin{displaymath} \sum_b w_b \| U - U_b \|^2 \Rightarrow \min\nolimits_U \end{displaymath} over all ultrametrics~$U$, where $U_1, \ldots, U_B$ are the ultrametrics corresponding to the elements of the ensemble. This is of course equivalent to minimizing $\| U - \bar{U} \|^2$, where $\bar{U} = s^{-1} \sum_b w_b U_b$ is the weighted average of the $U_b$. The SUMT approach provided by function \code{ls\_fit\_ultrametric()} (see Section~\ref{SUMT} on Page~\pageref{SUMT}) is employed for finding the sought weighted least squares consensus hierarchy. In addition, method \code{"majority"} obtains a consensus hierarchy from an extension of the majority consensus tree of \cite{cluster:Margush+McMorris:1981}, which minimizes $L(U) = \sum_b w_b d(U_b, U)$ over all ultrametrics~$U$, where $d$ is the symmetric difference dissimilarity. Clearly, the available methods use heuristics for solving hard optimization problems, and cannot be guaranteed to find a global optimum. Standard practice would recommend to use the best solution found in ``sufficiently many'' replications of the methods. Alternative recent approaches to obtaining consensus partitions include ``Bagged Clustering'' \citep[provided by \code{bclust()} in package~\pkg{e1071}]{cluster:Leisch:1999}, the ``evidence accumulation'' framework of \cite{cluster:Fred+Jain:2002}, the NMI optimization and graph-partitioning methods in \cite{cluster:Strehl+Ghosh:2003a}, ``Bagged Clustering'' as in \cite{cluster:Dudoit+Fridlyand:2003}, and the hybrid bipartite graph formulation of \cite{cluster:Fern+Brodley:2004}. Typically, these approaches are constructive, and can easily be implemented based on the infrastructure provided by package~\pkg{clue}. Evidence accumulation amounts to standard hierarchical clustering of the average co-membership matrix. Procedure~BagClust1 of \cite{cluster:Dudoit+Fridlyand:2003} amounts to computing $B^{-1} \sum_b M_b\Pi_b$, where each $\Pi_b$ is determined by optimal Euclidean matching of $M_b$ to a fixed reference membership $M_0$. In the corresponding ``Bagged Clustering'' framework, $M_0$ and the $M_b$ are obtained by applying the base clusterer to the original data set and bootstrap samples from it, respectively. This is implemented as method \code{"DFBC1"} of \code{cl\_bag()} in package~\pkg{clue}. Finally, the approach of \cite{cluster:Fern+Brodley:2004} solves an LSAP for an asymmetric cost matrix based on object-by-all-classes incidences. \subsection{Cluster partitions} To investigate the ``structure'' in a cluster ensemble, an obvious idea is to start clustering the clusterings in the ensemble, resulting in ``secondary'' clusterings \citep{cluster:Gordon+Vichi:1998, cluster:Gordon:1999}. This can e.g.\ be performed by using \code{cl\_dissimilarity()} (or \code{cl\_agreement()}) to compute a dissimilarity matrix for the ensemble, and feed this into a dissimilarity-based clustering algorithm (such as \code{pam()} in package~\pkg{cluster} or \code{hclust()} in package~\pkg{stats}). (One can even use \code{cutree()} to obtain hard partitions from hierarchies thus obtained.) If prototypes (``typical clusterings'') are desired for partitions of clusterings, they can be determined post-hoc by finding suitable consensus clusterings in the classes of the partition, e.g., using \code{cl\_consensus()} or \code{cl\_medoid()}. Package~\pkg{clue} additionally provides \code{cl\_pclust()} for direct prototype-based partitioning based on minimizing criterion functions of the form $\sum w_b u_{bj}^m d(x_b, p_j)^e$, the sum of the case-weighted membership-weighted $e$-th powers of the dissimilarities between the elements~$x_b$ of the ensemble and the prototypes~$p_j$, for suitable dissimilarities~$d$ and exponents~$e$. (The underlying feature spaces are that of membership matrices and ultrametrics, respectively, for partitions and hierarchies.) Parameter~$m$ must not be less than one and controls the softness of the obtained partitions, corresponding to the \dQuote{fuzzification parameter} of the fuzzy $c$-means algorithm. For $m = 1$, a generalization of the Lloyd-Forgy variant \citep{cluster:Lloyd:1957, cluster:Forgy:1965, cluster:Lloyd:1982} of the $k$-means algorithm is used, which iterates between reclassifying objects to their closest prototypes, and computing new prototypes as consensus clusterings for the classes. \citet{cluster:Gaul+Schader:1988} introduced this procedure for \dQuote{Clusterwise Aggregation of Relations} (with the same domains), containing equivalence relations, i.e., hard partitions, as a special case. For $m > 1$, a generalization of the fuzzy $c$-means recipe \citep[e.g.,][]{cluster:Bezdek:1981} is used, which alternates between computing optimal memberships for fixed prototypes, and computing new prototypes as the suitably weighted consensus clusterings for the classes. This procedure is repeated until convergence occurs, or the maximal number of iterations is reached. Consensus clusterings are computed using (one of the methods provided by) \code{cl\_consensus}, with dissimilarities~$d$ and exponent~$e$ implied by method employed, and obtained via a registration mechanism. The default methods compute Least Squares Euclidean consensus clusterings, i.e., use Euclidean dissimilarity~$d$ and $e = 2$. \section{Examples} \label{sec:examples} \subsection{Cassini data} \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} and \cite{cluster:Leisch:1999} use Cassini data sets to illustrate how e.g.\ suitable aggregation of base $k$-means results can reveal underlying non-convex structure which cannot be found by the base algorithm. Such data sets contain points in 2-dimensional space drawn from the uniform distribution on 3 structures, with the two ``outer'' ones banana-shaped and the ``middle'' one a circle, and can be obtained by function~\code{mlbench.cassini()} in package~\pkg{mlbench}~\citep{cluster:Leisch+Dimitriadou:2005}. Package~\pkg{clue} contains the data sets \code{Cassini} and \code{CKME}, which are an instance of a 1000-point Cassini data set, and a cluster ensemble of 50 $k$-means partitions of the data set into three classes, respectively. The data set is shown in Figure~\ref{fig:Cassini}. <>= data("Cassini") plot(Cassini$x, col = as.integer(Cassini$classes), xlab = "", ylab = "") @ % $ \begin{figure} \centering <>= <> @ % \caption{The Cassini data set.} \label{fig:Cassini} \end{figure} Figure~\ref{fig:CKME} gives a dendrogram of the Euclidean dissimilarities of the elements of the $k$-means ensemble. <>= data("CKME") plot(hclust(cl_dissimilarity(CKME)), labels = FALSE) @ % \begin{figure} \centering <>= <> @ % \caption{A dendrogram of the Euclidean dissimilarities of 50 $k$-means partitions of the Cassini data into 3 classes.} \label{fig:CKME} \end{figure} We can see that there are large groups of essentially identical $k$-means solutions. We can gain more insight by inspecting representatives of these three groups, or by computing the medoid of the ensemble <<>>= m1 <- cl_medoid(CKME) table(Medoid = cl_class_ids(m1), "True Classes" = Cassini$classes) @ % $ and inspecting it (Figure~\ref{fig:Cassini-medoid}): <>= plot(Cassini$x, col = cl_class_ids(m1), xlab = "", ylab = "") @ % $ \begin{figure} \centering <>= <> @ % \caption{Medoid of the Cassini $k$-means ensemble.} \label{fig:Cassini-medoid} \end{figure} Flipping this solution top-down gives a second ``typical'' partition. We see that the $k$-means base clusterers cannot resolve the underlying non-convex structure. For the least squares consensus of the ensemble, we obtain <<>>= set.seed(1234) m2 <- cl_consensus(CKME) @ % where here and below we set the random seed for reproducibility, noting that one should really use several replicates of the consensus heuristic. This consensus partition has confusion matrix <<>>= table(Consensus = cl_class_ids(m2), "True Classes" = Cassini$classes) @ % $ and class details as displayed in Figure~\ref{fig:Cassini-mean}: <>= plot(Cassini$x, col = cl_class_ids(m2), xlab = "", ylab = "") @ % $ \begin{figure} \centering <>= <> @ % \caption{Least Squares Consensus of the Cassini $k$-means ensemble.} \label{fig:Cassini-mean} \end{figure} This has drastically improved performance, and almost perfect recovery of the two outer shapes. In fact, \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} show that almost perfect classification can be obtained by suitable combinations of different base clusterers ($k$-means, fuzzy $c$-means, and unsupervised fuzzy competitive learning). \subsection{Gordon-Vichi macroeconomic data} \citet[Table~1]{cluster:Gordon+Vichi:2001} provide soft partitions of 21 countries based on macroeconomic data for the years 1975, 1980, 1985, 1990, and 1995. These partitions were obtained using fuzzy $c$-means on measurements of the following variables: the annual per capita gross domestic product (GDP) in USD (converted to 1987 prices); the percentage of GDP provided by agriculture; the percentage of employees who worked in agriculture; and gross domestic investment, expressed as a percentage of the GDP. Table~5 in \cite{cluster:Gordon+Vichi:2001} gives 3-class consensus partitions obtained by applying their models 1, 2, and 3 and the approach in \cite{cluster:Sato+Sato:1994}. The partitions and consensus partitions are available in data sets \code{GVME} and \code{GVME\_Consensus}, respectively. We compare the results of \cite{cluster:Gordon+Vichi:2001} using GV1 dissimilarities (model 1) to ours as obtained by \code{cl\_consensus()} with method \code{"GV1"}. <<>>= data("GVME") GVME set.seed(1) m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 3, verbose = TRUE)) @ % This results in a soft partition with average squared GV1 dissimilarity (the criterion function to be optimized by the consensus partition) of <<>>= mean(cl_dissimilarity(GVME, m1, "GV1") ^ 2) @ % We compare this to the consensus solution given in \cite{cluster:Gordon+Vichi:2001}: <<>>= data("GVME_Consensus") m2 <- GVME_Consensus[["MF1/3"]] mean(cl_dissimilarity(GVME, m2, "GV1") ^ 2) table(CLUE = cl_class_ids(m1), GV2001 = cl_class_ids(m2)) @ % Interestingly, we are able to obtain a ``better'' solution, which however agrees with the one reported on the literature with respect to their nearest hard partitions. For the 2-class consensus partition, we obtain <<>>= set.seed(1) m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) @ which is slightly better than the solution reported in \cite{cluster:Gordon+Vichi:2001} <<>>= mean(cl_dissimilarity(GVME, m1, "GV1") ^ 2) m2 <- GVME_Consensus[["MF1/2"]] mean(cl_dissimilarity(GVME, m2, "GV1") ^ 2) @ but in fact agrees with it apart from rounding errors: <<>>= max(abs(cl_membership(m1) - cl_membership(m2))) @ It is interesting to compare these solutions to the Euclidean 2-class consensus partition for the GVME ensemble: <<>>= m3 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) @ This is markedly different from the GV1 consensus partition <<>>= table(GV1 = cl_class_ids(m1), Euclidean = cl_class_ids(m3)) @ with countries <<>>= rownames(m1)[cl_class_ids(m1) != cl_class_ids(m3)] @ % classified differently, being with the ``richer'' class for the GV1 and the ``poorer'' for the Euclidean consensus partition. (In fact, all these countries end up in the ``middle'' class for the 3-class GV1 consensus partition.) \subsection{Rosenberg-Kim kinship terms data} \cite{cluster:Rosenberg+Kim:1975} describe an experiment where perceived similarities of the kinship terms were obtained from six different ``sorting'' experiments. In one of these, 85 female undergraduates at Rutgers University were asked to sort 15 English terms into classes ``on the basis of some aspect of meaning''. These partitions were printed in \citet[Table~7.1]{cluster:Rosenberg:1982}. Comparison with the original data indicates that the partition data have the ``nephew'' and ``niece'' columns interchanged, which is corrected in data set \code{Kinship82}. \citet[Table~6]{cluster:Gordon+Vichi:2001} provide consensus partitions for these data based on their models 1--3 (available in data set \code{Kinship82\_Consensus}). We compare their results using co-membership dissimilarities (model 3) to ours as obtained by \code{cl\_consensus()} with method \code{"GV3"}. <<>>= data("Kinship82") Kinship82 set.seed(1) m1 <- cl_consensus(Kinship82, method = "GV3", control = list(k = 3, verbose = TRUE)) @ % This results in a soft partition with average co-membership dissimilarity (the criterion function to be optimized by the consensus partition) of <<>>= mean(cl_dissimilarity(Kinship82, m1, "comem") ^ 2) @ % Again, we compare this to the corresponding consensus solution given in \cite{cluster:Gordon+Vichi:2001}: <<>>= data("Kinship82_Consensus") m2 <- Kinship82_Consensus[["JMF"]] mean(cl_dissimilarity(Kinship82, m2, "comem") ^ 2) @ % Interestingly, again we obtain a (this time only ``slightly'') better solution, with <<>>= cl_dissimilarity(m1, m2, "comem") table(CLUE = cl_class_ids(m1), GV2001 = cl_class_ids(m2)) @ % indicating that the two solutions are reasonably close, even though <<>>= cl_fuzziness(cl_ensemble(m1, m2)) @ % shows that the solution found by \pkg{clue} is ``softer''. \subsection{Miller-Nicely consonant phoneme confusion data} \cite{cluster:Miller+Nicely:1955} obtained the data on the auditory confusions of 16 English consonant phonemes by exposing female subjects to a series of syllables consisting of one of the consonants followed by the vowel `a' under 17 different experimental conditions. Data set \code{Phonemes} provides consonant misclassification probabilities (i.e., similarities) obtained from aggregating the six so-called flat-noise conditions in which only the speech-to-noise ratio was varied into a single matrix of misclassification frequencies. These data are used in \cite{cluster:DeSoete:1986} as an illustration of the SUMT approach for finding least squares optimal fits to dissimilarities by ultrametrics. We can reproduce this analysis as follows. <<>>= data("Phonemes") d <- as.dist(1 - Phonemes) @ % (Note that the data set has the consonant misclassification probabilities, i.e., the similarities between the phonemes.) <<>>= u <- ls_fit_ultrametric(d, control = list(verbose = TRUE)) @ % This gives an ultrametric~$u$ for which Figure~\ref{fig:Phonemes} plots the corresponding dendrogram, ``basically'' reproducing Figure~1 in \cite{cluster:DeSoete:1986}. <>= plot(u) @ % \begin{figure} \centering <>= <> @ % \caption{Dendrogram for least squares fit to the Miller-Nicely consonant phoneme confusion data.} \label{fig:Phonemes} \end{figure} We can also compare the least squares fit obtained to that of other hierarchical clusterings of $d$, e.g.\ those obtained by \code{hclust()}. The ``optimal''~$u$ has Euclidean dissimilarity <<>>= round(cl_dissimilarity(d, u), 4) @ % to $d$. For the \code{hclust()} results, we get <<>>= hclust_methods <- c("ward", "single", "complete", "average", "mcquitty") hens <- cl_ensemble(list = lapply(hclust_methods, function(m) hclust(d, m))) names(hens) <- hclust_methods round(sapply(hens, cl_dissimilarity, d), 4) @ % which all exhibit greater Euclidean dissimilarity to $d$ than $u$. (We exclude methods \code{"median"} and \code{"centroid"} as these do not yield valid hierarchies.) We can also compare the ``structure'' of the different hierarchies, e.g.\ by looking at the rate of inversions between them: <<>>= ahens <- c(L2opt = cl_ensemble(u), hens) round(cl_dissimilarity(ahens, method = "gamma"), 2) @ % \section{Outlook} \label{sec:outlook} Package~\pkg{clue} was designed as an \emph{extensible} environment for computing on cluster ensembles. It currently provides basic data structures for representing partitions and hierarchies, and facilities for computing on these, including methods for measuring proximity and obtaining consensus and ``secondary'' clusterings. Many extensions to the available functionality are possible and in fact planned (some of these enhancements were already discussed in more detail in the course of this paper). \begin{itemize} \item Provide mechanisms to generate cluster ensembles based on reweighting (assuming base clusterers allowing for case weights) the data set. \item Explore recent advances (e.g., parallelized random search) in heuristics for solving the multi-dimensional assignment problem. \item Add support for \emph{additive trees} \citep[e.g.,][]{cluster:Barthelemy+Guenoche:1991}. \item Add heuristics for finding least squares fits based on iterative projection on convex sets of constraints, see e.g.\ \cite{cluster:Hubert+Arabie+Meulman:2006} and the accompanying MATLAB code available at \url{http://cda.psych.uiuc.edu/srpm_mfiles/} for using these methods (instead of SUMT approaches) to fit ultrametrics and additive trees to proximity data. \item Add an ``$L_1$ View''. Emphasis in \pkg{clue}, in particular for obtaining consensus clusterings, is on using Euclidean dissimilarities (based on suitable least squares distances); arguably, more ``robust'' consensus solutions should result from using Manhattan dissimilarities (based on absolute distances). Adding such functionality necessitates developing the corresponding structure theory for soft Manhattan median partitions. Minimizing average Manhattan dissimilarity between co-memberships and ultrametrics results in constrained $L_1$ approximation problems for the weighted medians of the co-memberships and ultrametrics, respectively, and could be approached by employing SUMTs analogous to the ones used for the $L_2$ approximations. \item Provide heuristics for obtaining \emph{hard} consensus partitions. \item Add facilities for tuning hyper-parameters (most prominently, the number of classes employed) and ``cluster validation'' of partitioning algorithms, as recently proposed by \cite{cluster:Roth+Lange+Braun:2002}, \cite{cluster:Lange+Roth+Braun:2004}, \cite{cluster:Dudoit+Fridlyand:2002}, and \cite{cluster:Tibshirani+Walther:2005}. \end{itemize} We are hoping to be able to provide many of these extensions in the near future. \subsubsection*{Acknowledgments} We are grateful to Walter B\"ohm for providing efficient C code for solving assignment problems. {\small \bibliographystyle{abbrvnat} \bibliography{cluster} } \end{document} clue/vignettes/cluster.bib0000644000175100001440000012332615144361352015402 0ustar hornikusers@Book{cluster:Arabie+Carroll+Desarbo:1987, author = {Arabie, Phipps and Carroll, J. Douglas and DeSarbo, Wayne}, title = {Three-way Scaling and Clustering}, year = 1987, pages = 92, publisher = {Sage Publications Inc}, } @Book{cluster:Arabie+Hubert+DeSoete:1996, author = {Phipps Arabie and Lawrence J. Hubert and Geert de Soete}, title = {Clustering and Classification}, year = 1996, pages = 490, publisher = {World Scientific Publications}, } @Book{cluster:Barthelemy+Guenoche:1991, author = {Jean-Pierry Barth\'el\'emy and Alain Gu\'enoche}, title = {Trees and Proximity Representations}, publisher = {John Wiley \& Sons}, year = 1991, series = {Wiley-Interscience Series in Discrete Mathematics and Optimization}, address = {Chichester}, note = {{ISBN 0-471-92263-3}}, } @Article{cluster:Barthelemy+Leclerc+Monjardet:1986, author = {Jean-Pierre Barth\'el\'emy and Bruno Leclerc and Bernard Monjardet}, title = {On the Use of Ordered Sets in Problems of Comparison and Consensus of Classifications}, journal = {Journal of Classification}, year = 1986, volume = 3, number = 2, pages = {187--224}, doi = {10.1007/BF01894188}, } @Article{cluster:Barthelemy+Mcmorris:1986, author = {Jean-Pierre Barth\'el\'emy and F. R. McMorris}, title = {The Median Procedure for $n$-trees}, year = 1986, journal = {Journal of Classification}, volume = 3, pages = {329--334}, doi = {10.1007/BF01894194}, } @Article{cluster:Barthelemy+Monjardet:1981, author = {Jean-Pierre Barth\'el\'emy and Bernard Monjardet}, title = {The Median Procedure in Cluster Analysis and Social Choice Theory}, journal = {Mathematical Social Sciences}, year = 1981, volume = 1, pages = {235--267}, doi = {10.1016/0165-4896(81)90041-X}, } @TechReport{cluster:Bertsekas+Tseng:1994, author = {Dimitri P. Bertsekas and P. Tseng}, title = {{RELAX-IV}: A Faster Version of the {RELAX} Code for Solving Minimum Cost Flow Problems}, institution = {Massachusetts Institute of Technology}, year = 1994, number = {P-2276}, url = {https://dspace.mit.edu/handle/1721.1/3392}, } @Book{cluster:Bezdek:1981, author = {James C. Bezdek}, title = {Pattern Recognition with Fuzzy Objective Function Algorithms}, publisher = {Plenum}, address = {New York}, year = 1981, } @InCollection{cluster:Boorman+Arabie:1972, author = {Scott A. Boorman and Phipps Arabie}, title = {Structural Measures and the Method of Sorting}, booktitle = {Multidimensional Scaling: Theory and Applications in the Behavioral Sciences, 1: Theory}, pages = {225--249}, publisher = {Seminar Press}, year = 1972, editor = {Roger N. Shepard and A. Kimball Romney and Sara Beth Nerlove}, address = {New York}, } @Article{cluster:Boorman+Olivier:1973, author = {Scott A. Boorman and Donald C. Olivier}, title = {Metrics on Spaces of Finite Trees}, journal = {Journal of Mathematical Psychology}, year = 1973, volume = 10, number = 1, pages = {26--59}, doi = {10.1016/0022-2496(73)90003-5}, } @Article{cluster:Breiman:1996, author = {Leo Breiman}, title = {Bagging Predictors}, journal = {Machine Learning}, year = 1996, volume = 24, number = 2, pages = {123--140}, doi = {10.1023/A:1018054314350}, } @Manual{cluster:Buchta+Hahsler:2005, title = {cba: Clustering for Business Analytics}, author = {Christian Buchta and Michael Hahsler}, year = 2005, note = {R package version 0.1-6}, url = {https://CRAN.R-project.org/package=cba}, } @Article{cluster:Buttrey:2005, author = {Samuel E. Buttrey}, title = {Calling the \texttt{lp\_solve} Linear Program Software from {R}, {S-PLUS} and {Excel}}, journal = {Journal of Statistical Software}, year = 2005, volume = 14, number = 4, doi = {10.18637/jss.v014.i04}, } @article{cluster:Carpaneto+Toth:1980, author = {Giorgio Carpaneto and Paolo Toth}, title = {Algorithm 548: Solution of the Assignment Problem}, journal = {ACM Transactions on Mathematical Software}, volume = 6, number = 1, year = 1980, issn = {0098-3500}, pages = {104--111}, doi = {10.1145/355873.355883}, publisher = {ACM Press}, } @Article{cluster:Carroll+Clark+Desarbo:1984, author = {Carroll, J. Douglas and Clark, Linda A. and DeSarbo, Wayne S.}, title = {The Representation of Three-way Proximity Data by Single and Multiple Tree Structure Models}, year = 1984, journal = {Journal of Classification}, volume = 1, pages = {25--74}, keywords = {Clustering analysis; Alternating least squares; Discrete optimization}, doi = {10.1007/BF01890116}, } @InCollection{cluster:Carroll+Pruzansky:1980, author = {J. D. Carroll and S. Pruzansky}, title = {Discrete and Hybrid Scaling Models}, booktitle = {Similarity and Choice}, address = {Bern, Switzerland}, publisher = {Huber}, year = 1980, editor = {E. D. Lantermann and H. Feger}, } @Article{cluster:Carroll:1976, author = {Carroll, J. Douglas}, title = {Spatial, Non-spatial and Hybrid Models for Scaling}, year = 1976, journal = {Psychometrika}, volume = 41, pages = {439--464}, keywords = {Multidimensional scaling; Hierarchical tree structure; Clustering; Geometric model; Multivariate data}, doi = {10.1007/BF02296969}, } @TechReport{cluster:Charon+Denoeud+Guenoche:2005, author = {Ir{\`e}ne Charon and Lucile Denoeud and Alain Gu{\'e}noche and Olivier Hudry}, title = {Maximum Transfer Distance Between Partitions}, institution = {Ecole Nationale Sup{\'e}rieure des T{\'e}l{\'e}communications --- Paris}, year = 2005, number = {2005D003}, month = {May}, note = {ISSN 0751-1345 ENST D}, } @Article{cluster:Charon+Denoeud+Guenoche:2006, author = {Ir{\`e}ne Charon and Lucile Denoeud and Alain Gu{\'e}noche and Olivier Hudry}, title = {Maximum Transfer Distance Between Partitions}, journal = {Journal of Classification}, year = 2006, volume = 23, number = 1, pages = {103-121}, month = {June}, doi = {10.1007/s00357-006-0006-2}, } @Article{cluster:Day:1981, author = {William H. E. Day}, title = {The Complexity of Computing Metric Distances Between Partitions}, journal = {Mathematical Social Sciences}, year = 1981, volume = 1, pages = {269--287}, doi = {10.1016/0165-4896(81)90042-1}, } @Article{cluster:Day:1986, author = {William H. E. Day}, title = {Foreword: Comparison and Consensus of Classifications}, journal = {Journal of Classification}, year = 1986, volume = 3, pages = {183--185}, doi = {10.1007/BF01894187}, } @Article{cluster:Day:1987, author = {Day, William H. E.}, title = {Computational Complexity of Inferring Phylogenies from Dissimilarity Matrices}, year = 1987, journal = {Bulletin of Mathematical Biology}, volume = 49, pages = {461--467}, doi = {10.1007/BF02458863}, } @Article{cluster:DeSoete+Carroll+Desarbo:1987, author = {De Soete, Geert and Carroll, J. Douglas and DeSarbo, Wayne S.}, title = {Least Squares Algorithms for Constructing Constrained Ultrametric and Additive Tree Representations of Symmetric Proximity Data}, year = 1987, journal = {Journal of Classification}, volume = 4, pages = {155--173}, keywords = {Hierarchical clustering; Classification}, doi = {10.1007/BF01896984}, } @Article{cluster:DeSoete+Desarbo+Furnas:1984, author = {De Soete, Geert and DeSarbo, Wayne S. and Furnas, George W. and Carroll, J. Douglas}, title = {The Estimation of Ultrametric and Path Length Trees from Rectangular Proximity Data}, year = 1984, journal = {Psychometrika}, volume = 49, pages = {289--310}, keywords = {Cluster analysis}, doi = {10.1007/BF02306021}, } @Article{cluster:DeSoete:1983, author = {De Soete, Geert}, title = {A Least Squares Algorithm for Fitting Additive Trees to Proximity Data}, year = 1983, journal = {Psychometrika}, volume = 48, pages = {621--626}, keywords = {Clustering}, doi = {10.1007/BF02293884}, } @Article{cluster:DeSoete:1984, author = {Geert de Soete}, title = {Ultrametric Tree Representations of Incomplete Dissimilarity Data}, journal = {Journal of Classification}, year = 1984, volume = 1, pages = {235--242}, doi = {10.1007/BF01890124}, } @Article{cluster:DeSoete:1986, author = {Geert de Soete}, title = {A Least Squares Algorithm for Fitting an Ultrametric Tree to a Dissimilarity Matrix}, journal = {Pattern Recognition Letters}, year = 1986, volume = 2, pages = {133--137}, doi = {10.1016/0167-8655(84)90036-9}, } @Manual{cluster:Dimitriadou+Hornik+Leisch:2005, title = {e1071: Misc Functions of the Department of Statistics (e1071), TU Wien}, author = {Evgenia Dimitriadou and Kurt Hornik and Friedrich Leisch and David Meyer and Andreas Weingessel}, year = 2005, note = {R package version 1.5-7}, url = {https://CRAN.R-project.org/package=e1071}, } @Article{cluster:Dimitriadou+Weingessel+Hornik:2002, author = {Evgenia Dimitriadou and Andreas Weingessel and Kurt Hornik}, title = {A Combination Scheme for Fuzzy Clustering}, journal = {International Journal of Pattern Recognition and Artificial Intelligence}, year = 2002, volume = 16, number = 7, pages = {901--912}, doi = {10.1142/S0218001402002052}, } @Manual{cluster:Dimitriadou:2005, title = {cclust: Convex Clustering Methods and Clustering Indexes}, author = {Evgenia Dimitriadou}, year = 2005, note = {R package version 0.6-12}, url = {https://CRAN.R-project.org/package=cclust}, } @Article{cluster:Dudoit+Fridlyand:2002, author = {Sandrine Dudoit and Jane Fridlyand}, title = {A Prediction-based Resampling Method for Estimating the Number of Clusters in a Dataset}, journal = {Genome Biology}, year = 2002, volume = 3, number = 7, pages = {1--21}, doi = {10.1186/gb-2002-3-7-research0036}, } @Article{cluster:Dudoit+Fridlyand:2003, author = {Sandrine Dudoit and Jane Fridlyand}, title = {Bagging to Improve the Accuracy of a Clustering Procedure}, journal = {Bioinformatics}, year = 2003, volume = 19, number = 9, pages = {1090--1099}, doi = {10.1093/bioinformatics/btg038}, } @InProceedings{cluster:Fern+Brodley:2004, author = {Xiaoli Zhang Fern and Carla E. Brodley}, title = {Solving Cluster Ensemble Problems by Bipartite Graph Partitioning}, booktitle = {ICML '04: Twenty-first International Conference on Machine Learning}, year = 2004, isbn = {1-58113-828-5}, location = {Banff, Alberta, Canada}, doi = {10.1145/1015330.1015414}, publisher = {ACM Press}, } @comment address = {New York, NY, USA}, @Book{cluster:Fiacco+McCormick:1968, author = {Anthony V. Fiacco and Garth P. McCormick}, title = {Nonlinear Programming: Sequential Unconstrained Minimization Techniques}, publisher = {John Willey \& Sons}, year = 1968, address = {New York}, } @Article{cluster:Forgy:1965, author = {Forgy, E. W.}, title = {Cluster Analysis of Multivariate Data: Efficiency vs Interpretability of Classifications}, journal = {Biometrics}, year = 1965, volume = 21, pages = {768--769}, } @Article{cluster:Fowlkes+Mallows:1983a, author = {Fowlkes, E. B. and Mallows, C. L.}, title = {A Method for Comparing Two Hierarchical Clusterings}, year = 1983, journal = {Journal of the American Statistical Association}, volume = 78, pages = {553--569}, keywords = {Similarity; Graphics}, doi = {10.1080/01621459.1983.10478008}, } @Article{cluster:Fowlkes+Mallows:1983b, author = {Fowlkes, E. B. and Mallows, C. L.}, title = {Reply to Comments on ``{A} Method for Comparing Two Hierarchical Clusterings''}, year = 1983, journal = {Journal of the American Statistical Association}, volume = 78, pages = {584--584}, } @Manual{cluster:Fraley+Raftery+Wehrens:2005, title = {mclust: Model-based Cluster Analysis}, author = {Chris Fraley and Adrian E. Raftery and Ron Wehrens}, year = 2005, note = {R package version 2.1-11}, url = {https://CRAN.R-project.org/package=mclust}, } @TechReport{cluster:Fraley+Raftery:2002, author = {Chris Fraley and Adrian E. Raftery}, title = {{MCLUST}: Software for Model-based Clustering, Discriminant Analysis, and Density Estimation}, institution = {Department of Statistics, University of Washington}, year = 2002, number = 415, month = {October}, url = {ftp://ftp.u.washington.edu/public/mclust/tr415.pdf}, } @Article{cluster:Fraley+Raftery:2003, author = {Chris Fraley and Adrian E. Raftery}, title = {Enhanced Model-based Clustering, Density Estimation, and Discriminant Analysis Software: {MCLUST}}, year = 2003, journal = {Journal of Classification}, volume = 20, number = 2, pages = {263--286}, keywords = {clustering software; Mixture models; Cluster analysis; supervised classification; unsupervised classification; software abstract}, doi = {10.1007/s00357-003-0015-3}, } @InProceedings{cluster:Fred+Jain:2002, author = {Ana L. N. Fred and Anil K. Jain}, title = {Data Clustering Using Evidence Accumulation}, booktitle = {Proceedings of the 16th International Conference on Pattern Recognition (ICPR 2002)}, pages = {276--280}, year = 2002, doi = {10.1109/ICPR.2002.1047450}, } @Article{cluster:Friedman+Hastie+Tibshirani:2000, author = {Jerome Friedman and Travor Hastie and Robert Tibshirani}, title = {Additive Logistic Regression: A Statistical View of Boosting}, journal = {The Annals of Statistics}, year = 2000, volume = 28, number = 2, pages = {337--407}, doi = {10.1214/aos/1016218223}, } @Book{cluster:Garey+Johnson:1979, author = {M. R. Garey and D. S. Johnson}, title = {Computers and Intractability: A Guide to the Theory of {NP}-Completeness}, address = {San Francisco}, publisher = {W. H. Freeman}, year = 1979, } @Article{cluster:Gaul+Schader:1988, author = {Wolfgang Gaul and Manfred Schader}, title = {Clusterwise Aggregation of Relations}, journal = {Applied Stochastic Models and Data Analysis}, year = 1988, volume = 4, pages = {273--282}, doi = {10.1002/asm.3150040406}, } @Manual{cluster:Gentleman+Whalen:2005, author = {Robert Gentleman and Elizabeth Whalen}, title = {graph: A Package to Handle Graph Data Structures}, year = 2005, note = {R package version 1.5.9}, url = {https://www.bioconductor.org/}, } @Article{cluster:Gordon+Vichi:1998, author = {Gordon, A. D. and Vichi, M.}, title = {Partitions of Partitions}, year = 1998, journal = {Journal of Classification}, volume = 15, pages = {265--285}, keywords = {Classification}, doi = {10.1007/s003579900034}, } @Article{cluster:Gordon+Vichi:2001, author = {Gordon, A. D. and Vichi, M.}, title = {Fuzzy Partition Models for Fitting a Set of Partitions}, year = 2001, journal = {Psychometrika}, volume = 66, number = 2, pages = {229--248}, keywords = {Classification; Cluster analysis; consensus fuzzy partition; membership function; three-way data}, doi = {10.1007/BF02294837}, } @Article{cluster:Gordon:1996, author = {Gordon, A. D.}, title = {A Survey of Constrained Classification}, year = 1996, journal = {Computational Statistics \& Data Analysis}, volume = 21, pages = {17--29}, keywords = {Model selection}, doi = {10.1016/0167-9473(95)00005-4}, } @Book{cluster:Gordon:1999, author = {A. D. Gordon}, title = {Classification}, address = {Boca Raton, Florida}, publisher = {Chapman \& Hall/CRC}, year = 1999, pages = 256, edition = {2nd}, } @Article{cluster:Grundel+Oliveira+Pardalos:2005, author = {Don Grundel and Carlos A.S. Oliveira and Panos M. Pardalos and Eduardo Pasiliao}, title = {Asymptotic Results for Random Multidimensional Assignment Problems}, journal = {Computational Optimization and Applications}, year = 2005, volume = 31, number = 3, pages = {275--293}, doi = {10.1007/s10589-005-3227-0}, } @Article{cluster:Guha+Rastogi+Shim:2000, author = {Sudipto Guha and Rajeev Rastogi and Kyuseok Shim}, title = {{ROCK}: A Robust Clustering Algorithm for Categorical Attributes}, journal = {Information Systems}, year = 2000, volume = 25, number = 5, pages = {345--366}, doi = {10.1016/S0306-4379(00)00022-3}, } @Article{cluster:Gusfield:2002, author = {Dan Gusfield}, title = {Partition-Distance: A Problem and Class of Perfect Graphs Arising in Clustering}, journal = {Information Processing Letters}, year = 2002, volume = 82, pages = {159--164}, doi = {10.1016/S0020-0190(01)00263-0}, } @Manual{cluster:Hansen:2005, title = {optmatch: Functions for Optimal Matching}, author = {Ben B. Hansen}, year = 2005, note = {R package version 0.1-3}, url = {https://CRAN.R-project.org/package=optmatch}, } @Article{cluster:Hartigan+Wong:1979, author = {Hartigan, J. A. and Wong, M. A.}, title = {A $K$-Means Clustering Algorithm}, journal = {Applied Statistics}, year = 1979, volume = 28, pages = {100--108}, doi = {10.2307/2346830}, } @Article{cluster:Hoeting+Madigan+Raftery:1999, author = {Jennifer Hoeting and David Madigan and Adrian Raftery and Chris Volinsky}, title = {Bayesian Model Averaging: A Tutorial}, journal = {Statistical Science}, year = 1999, volume = 14, pages = {382--401}, doi = {10.1214/ss/1009212519}, } @Manual{cluster:Hornik+Hothorn+Karatzoglou:2006, title = {RWeka: {R/Weka} Interface}, author = {Kurt Hornik and Torsten Hothorn and Alexandros Karatzoglou}, year = 2006, note = {R package version 0.2-0}, url = {https://CRAN.R-project.org/package=RWeka}, } @InProceedings{cluster:Hornik:2005a, author = {Kurt Hornik}, title = {Cluster Ensembles}, booktitle = {Classification -- The Ubiquitous Challenge}, pages = {65--72}, year = 2005, editor = {Claus Weihs and Wolfgang Gaul}, publisher = {Springer-Verlag}, note = {Proceedings of the 28th Annual Conference of the Gesellschaft f{\"u}r Klassifikation e.V., University of Dortmund, March 9--11, 2004}, } @comment address = {Heidelberg}, @Article{cluster:Hornik:2005b, author = {Kurt Hornik}, title = {A {CLUE} for {CLUster Ensembles}}, year = 2005, journal = {Journal of Statistical Software}, volume = 14, number = 12, month = {September}, doi = {10.18637/jss.v014.i12}, } @Misc{cluster:Hubert+Arabie+Meulman:2004, author = {Lawrence Hubert and Phipps Arabie and Jacqueline Meulman}, title = {The Structural Representation of Proximity Matrices With {MATLAB}}, year = 2004, url = {http://cda.psych.uiuc.edu/srpm_mfiles/}, } @Book{cluster:Hubert+Arabie+Meulman:2006, author = {Lawrence Hubert and Phipps Arabie and Jacqueline Meulman}, title = {The Structural Representation of Proximity Matrices With {MATLAB}}, publisher = {SIAM}, address = {Philadelphia}, year = 2006, doi = {10.1137/1.9780898718355}, } @Article{cluster:Hubert+Arabie:1985, author = {Hubert, Lawrence and Arabie, Phipps}, title = {Comparing Partitions}, year = 1985, journal = {Journal of Classification}, volume = 2, pages = {193--218}, keywords = {Agreement; Association measure; Consensus index}, doi = {10.1007/bf01908075}, } @Article{cluster:Hubert+Arabie:1994, author = {Hubert, Lawrence and Arabie, Phipps}, title = {The Analysis of Proximity Matrices through Sums of Matrices Having (anti-) {R}obinson Forms}, year = 1994, journal = {British Journal of Mathematical and Statistical Psychology}, volume = 47, pages = {1--40}, doi = {10.1111/j.2044-8317.1994.tb01023.x}, } @Article{cluster:Hubert+Arabie:1995, author = {Hubert, Lawrence and Arabie, Phipps}, title = {Iterative Projection Strategies for the Least Squares Fitting of Tree Structures to Proximity Data}, year = 1995, journal = {British Journal of Mathematical and Statistical Psychology}, volume = 48, pages = {281--317}, keywords = {Graph theory}, doi = {10.1111/j.2044-8317.1995.tb01065.x}, } @Article{cluster:Hubert+Baker:1978, author = {Hubert, Lawrence J. and Baker, Frank B.}, title = {Evaluating the Conformity of Sociometric Measurements}, year = 1978, journal = {Psychometrika}, volume = 43, pages = {31--42}, keywords = {Permutation test; Nonparametric test}, doi = {10.1007/BF02294087}, } @Article{cluster:Hutchinson:1989, author = {Hutchinson, J. Wesley}, title = {{NETSCAL}: {A} Network Scaling Algorithm for Nonsymmetric Proximity Data}, year = 1989, journal = {Psychometrika}, volume = 54, pages = {25--51}, keywords = {Similarity; Graph theory}, doi = {10.1007/BF02294447}, } @Article{cluster:Karatzoglou+Smola+Hornik:2004, title = {kernlab -- An {S4} Package for Kernel Methods in {R}}, author = {Alexandros Karatzoglou and Alex Smola and Kurt Hornik and Achim Zeileis}, journal = {Journal of Statistical Software}, year = 2004, volume = 11, number = 9, pages = {1--20}, doi = {10.18637/jss.v011.i09}, } @Article{cluster:Katz+Powell:1953, author = {L. Katz and J. H. Powell}, title = {A Proposed Index of the Conformity of one Sociometric Measurement to Another}, journal = {Psychometrika}, year = 1953, volume = 18, pages = {249--256}, doi = {10.1007/BF02289063}, } @Book{cluster:Kaufman+Rousseeuw:1990, author = {Kaufman, Leonard and Rousseeuw, Peter J.}, title = {Finding Groups in Data: An Introduction to Cluster Analysis}, year = 1990, pages = 342, publisher = {John Wiley \& Sons}, } @Article{cluster:Klauer+Carroll:1989, author = {Klauer, K. C. and Carroll, J. D.}, title = {A Mathematical Programming Approach to Fitting General Graphs}, year = 1989, journal = {Journal of Classification}, volume = 6, pages = {247--270}, keywords = {Multivariate analysis; Proximity data}, doi = {10.1007/BF01908602}, } @Article{cluster:Klauer+Carroll:1991, author = {Klauer, K. C. and Carroll, J. O.}, title = {A Comparison of Two Approaches to Fitting Directed Graphs to Nonsymmetric Proximity Measures}, year = 1991, journal = {Journal of Classification}, volume = 8, pages = {251--268}, keywords = {Clustering}, doi = {10.1007/BF02616242}, } @Article{cluster:Krieger+Green:1999, author = {Abba M. Krieger and Paul E. Green}, title = {A Generalized {Rand}-index Method for Consensus Clustering of Separate Partitions of the Same Data Base}, journal = {Journal of Classification}, year = 1999, volume = 16, pages = {63--89}, doi = {10.1007/s003579900043}, } @Article{cluster:Krivanek+Moravek:1986, author = {M. Krivanek and J. Moravek}, title = {{NP}-hard Problems in Hierarchical Tree Clustering}, journal = {Acta Informatica}, year = 1986, volume = 23, pages = {311--323}, doi = {10.1007/BF00289116}, } @InProceedings{cluster:Krivanek:1986, author = {Krivanek, Mirko}, title = {On the Computational Complexity of Clustering}, year = 1986, booktitle = {Data Analysis and Informatics 4}, editor = {Diday, E. and Escoufier, Y. and Lebart, L. and Pages, J. and Schektman, Y. and Tomassone, R.}, publisher = {Elsevier/North-Holland}, pages = {89--96}, } @comment address = {Amsterdam}, @Article{cluster:Lange+Roth+Braun:2004, author = {Tilman Lange and Volker Roth and Mikio L. Braun and Joachim M. Buhmann}, title = {Stability-Based Validation of Clustering Solutions}, journal = {Neural Computation}, year = 2004, volume = 16, number = 6, pages = {1299--1323}, doi = {10.1162/089976604773717621}, } @Manual{cluster:Leisch+Dimitriadou:2005, title = {mlbench: Machine Learning Benchmark Problems}, author = {Friedrich Leisch and Evgenia Dimitriadou}, year = 2005, note = {R package version 1.0-1}, url = {https://CRAN.R-project.org/package=mlbench}, } @TechReport{cluster:Leisch:1999, author = {Friedrich Leisch}, title = {Bagged Clustering}, institution = {SFB Adaptive Information Systems and Modelling in Economics and Management Science, WU Vienna University of Economics and Business}, year = 1999, type = {Working Paper}, number = 51, month = {August}, doi = {10.57938/9b129f95-b53b-44ce-a129-5b7a1168d832}, } @Article{cluster:Leisch:2004, title = {{FlexMix}: A General Framework for Finite Mixture Models and Latent Class Regression in {R}}, author = {Friedrich Leisch}, journal = {Journal of Statistical Software}, year = 2004, volume = 11, number = 8, doi = {10.18637/jss.v011.i08}, } @Manual{cluster:Leisch:2005, author = {Friedrich Leisch}, title = {flexclust: Flexible Cluster Algorithms}, note = {R package 0.7-0}, year = 2005, url = {https://CRAN.R-project.org/package=flexclust}, } @Article{cluster:Leisch:2006a, author = {Friedrich Leisch}, title = {A Toolbox for $K$-Centroids Cluster Analysis}, journal = {Computational Statistics and Data Analysis}, year = 2006, volume = 51, number = 2, pages = {526--544}, doi = {10.1016/j.csda.2005.10.006}, } @Unpublished{cluster:Lloyd:1957, author = {Lloyd, S. P.}, title = {Least Squares Quantization in {PCM}}, note = {Technical Note, Bell Laboratories}, year = 1957, } @Article{cluster:Lloyd:1982, author = {Lloyd, S. P.}, title = {Least Squares Quantization in {PCM}}, journal = {IEEE Transactions on Information Theory}, year = 1982, volume = 28, pages = {128--137}, doi = {10.1109/TIT.1982.1056489}, } @Article{cluster:Margush+Mcmorris:1981, author = {T. Margush and F. R. McMorris}, title = {Consensus $n$-Trees}, journal = {Bulletin of Mathematical Biology}, year = 1981, volume = 43, number = 2, pages = {239--244}, doi = {10.1007/BF02459446}, } @InProceedings{cluster:Meila:2003, author = {Marina Meila}, title = {Comparing Clusterings by the Variation of Information}, booktitle = {Learning Theory and Kernel Machines}, editor = {Bernhard Sch{\"o}lkopf and Manfred K. Warmuth}, series = {Lecture Notes in Computer Science}, publisher = {Springer-Verlag}, volume = 2777, year = 2003, pages = {173--187}, ee = {http://springerlink.metapress.com/openurl.asp?genre=article&issn=0302-9743&volume=2777&spage=173}, bibsource = {DBLP, http://dblp.uni-trier.de}, } @comment address = {Heidelberg}, @Article{cluster:Messatfa:1992, author = {Messatfa, H.}, title = {An Algorithm to Maximize the Agreement Between Partitions}, year = 1992, journal = {Journal of Classification}, volume = 9, pages = {5--15}, keywords = {Association; Contingency table}, doi = {10.1007/BF02618465}, } @Article{cluster:Miller+Nicely:1955, author = {G. A. Miller and P. E. Nicely}, title = {An Analysis of Perceptual Confusions Among some {English} Consonants}, journal = {Journal of the Acoustical Society of America}, year = 1955, volume = 27, pages = {338--352}, doi = {10.1121/1.1907526}, } @Book{cluster:Mirkin:1996, author = {Boris G. Mirkin}, title = {Mathematical Classification and Clustering}, year = 1996, pages = 428, publisher = {Kluwer Academic Publishers Group}, } @Article{cluster:Monti+Tamayo+Mesirov:2003, author = {Stefano Monti and Pablo Tamayo and Jill Mesirov and Todd Golub}, title = {Consensus Clustering: A Resampling-based Method for Class Discovery and Visualization of Gene Expression Microarray Data}, journal = {Machine Learning}, volume = 52, number = {1--2}, year = 2003, issn = {0885-6125}, pages = {91--118}, publisher = {Kluwer Academic Publishers}, address = {Hingham, MA, USA}, doi = {10.1023/A:1023949509487}, } @Article{cluster:Oliveira+Pardalos:2004, author = {Carlos A. S. Oliveira and Panos M. Pardalos}, title = {Randomized Parallel Algorithms for the Multidimensional Assignment Problem}, journal = {Applied Numerical Mathematics}, year = 2004, volume = 49, number = 1, pages = {117--133}, month = {April}, doi = {10.1016/j.apnum.2003.11.014}, } @Book{cluster:Papadimitriou+Steiglitz:1982, author = {Christos Papadimitriou and Kenneth Steiglitz}, title = {Combinatorial Optimization: Algorithms and Complexity}, publisher = {Prentice Hall}, year = 1982, address = {Englewood Cliffs}, } @Manual{cluster:R:2005, title = {R: A Language and Environment for Statistical Computing}, author = {{R Development Core Team}}, organization = {R Foundation for Statistical Computing}, address = {Vienna, Austria}, year = 2005, note = {{ISBN} 3-900051-07-0}, url = {https://www.R-project.org/}, } @article{cluster:Rajski:1961, author = {C. Rajski}, title = {A Metric Space of Discrete Probability Distributions}, journal = {Information and Control}, year = 1961, volume = 4, number = 4, pages = {371--377}, doi = {10.1016/S0019-9958(61)80055-7}, } @Article{cluster:Rand:1971, author = {William M. Rand}, title = {Objective Criteria for the Evaluation of Clustering Methods}, journal = {Journal of the American Statistical Association}, year = 1971, volume = 66, number = 336, pages = {846--850}, keywords = {Pattern recognition}, doi = {10.2307/2284239}, } @Article{cluster:Rosenberg+Kim:1975, author = {S. Rosenberg and M. P. Kim}, title = {The Method of Sorting as a Data-Gathering Procedure in Multivariate Research}, journal = {Multivariate Behavioral Research}, year = 1975, volume = 10, pages = {489--502}, doi = {10.1207/s15327906mbr1004_7}, } @InCollection{cluster:Rosenberg:1982, author = {S. Rosenberg}, title = {The Method of Sorting in Multivariate Research with Applications Selected from Cognitive Psychology and Person Perception}, booktitle = {Multivariate Applications in the Social Sciences}, pages = {117--142}, address = {Hillsdale, New Jersey}, publisher = {Erlbaum}, year = 1982, editor = {N. Hirschberg and L. G. Humphreys}, } @InProceedings{cluster:Roth+Lange+Braun:2002, author = {Volker Roth and Tilman Lange and Mikio Braun and Joachim M. Buhmann}, title = {A Resampling Approach to Cluster Validation}, booktitle = {{COMPSTAT} 2002 -- Proceedings in Computational Statistics}, pages = {123--128}, year = 2002, editor = {Wolfgang H{\"a}rdle and Bernd R{\"o}nz}, publisher = {Physika Verlag}, note = {ISBN 3-7908-1517-9}, } @comment address = {Heidelberg, Germany}, @Manual{cluster:Rousseeuw+Struyf+Hubert:2005, title = {cluster: Functions for Clustering (by Rousseeuw et al.)}, author = {Peter Rousseeuw and Anja Struyf and Mia Hubert and Martin Maechler}, year = 2005, note = {R package version 1.9.8}, url = {https://CRAN.R-project.org/package=cluster}, } @InCollection{cluster:Roux:1988, author = {M. Roux}, title = {Techniques of Approximation for Building Two Tree Structures}, booktitle = {Recent Developments in Clustering and Data Analysis}, pages = {151--170}, publisher = {Academic Press}, year = 1988, editor = {C. Hayashi and E. Diday and M. Jambu and N. Ohsumi}, address = {New York}, } @article{cluster:Rubin:1967, author = {Jerrold Rubin}, title = {Optimal Classification into Groups: An Approach for Solving the Taxonomy Problem}, journal = {Journal of Theoretical Biology}, year = 1967, volume = 15, number = 1, pages = {103--144}, doi = {10.1016/0022-5193(67)90046-X}, } @Article{cluster:Sato+Sato:1994, author = {M. Sato and Y. Sato}, title = {On a Multicriteria Fuzzy Clustering Method for 3-way Data}, journal = {International Journal of Uncertainty, Fuzziness and Knowledge-based Systems}, year = 1994, volume = 2, pages = {127--142}, doi = {10.1142/S0218488594000122}, } @Article{cluster:Smith:2000, author = {Smith, Thomas J.}, title = {${L}_1$ Optimization under Linear Inequality Constraints}, year = 2000, journal = {Journal of Classification}, volume = 17, number = 2, pages = {225--242}, keywords = {$L_1$-norm; Ultrametric; stuctural representation}, doi = {10.1007/s003570000020}, } @Article{cluster:Smith:2001, author = {Smith, Thomas J.}, title = {Constructing Ultrametric and Additive Trees Based on the ${L}_1$ Norm}, year = 2001, journal = {Journal of Classification}, volume = 18, number = 2, pages = {185--207}, keywords = {iteratively re-weighted iterative projection (IRIP); Combinatorial probability; explicit machine computation; Combinatorics; Trees; Graph theory; Linear regression; probabilistic Monte Carlo methods}, doi = {10.1007/s00357-001-0015-0}, } @Article{cluster:Sokal+Rohlf:1962, author = {R. R. Sokal and F. J. Rohlf}, title = {The Comparisons of Dendrograms by Objective Methods}, journal = {Taxon}, year = 1962, volume = 11, pages = {33--40}, doi = {10.2307/1217208}, } @Article{cluster:Strehl+Ghosh:2003a, author = {Alexander Strehl and Joydeep Ghosh}, title = {Cluster Ensembles -- {A} Knowledge Reuse Framework for Combining Multiple Partitions}, journal = {Journal of Machine Learning Research}, volume = 3, year = 2003, issn = {1533-7928}, pages = {583--617}, publisher = {MIT Press}, url = {https://www.jmlr.org/papers/volume3/strehl02a/strehl02a.pdf}, } @Article{cluster:Strehl+Ghosh:2003b, author = {Alexander Strehl and Joydeep Ghosh}, title = {Relationship-based Clustering and Visualization for High-Dimensional Data Mining}, journal = {{INFORMS} Journal on Computing}, year = 2003, volume = 15, issue = 2, pages = {208--230}, ISSN = {1526-5528}, doi = {10.1287/ijoc.15.2.208.14448}, } @Article{cluster:Struyf+Hubert+Rousseeuw:1996, author = {Anja Struyf and Mia Hubert and Peter Rousseeuw}, title = {Clustering in an Object-Oriented Environment}, journal = {Journal of Statistical Software}, year = 1996, volume = 1, number = 4, doi = {10.18637/jss.v001.i04}, } @Article{cluster:Tibshirani+Walther+Hastie:2001, author = {Tibshirani, Robert and Walther, Guenther and Hastie, Trevor}, title = {Estimating the Number of Clusters in a Data Set Via the Gap Statistic}, year = 2001, journal = {Journal of the Royal Statistical Society, Series B: Statistical Methodology}, volume = 63, number = 2, pages = {411--423}, keywords = {Clustering; groups; Hierarchy; $k$-means; Uniform distribution}, doi = {10.1111/1467-9868.00293}, } @Article{cluster:Tibshirani+Walther:2005, author = {Tibshirani, Robert and Walther, Guenther}, title = {Cluster Validation by Prediction Strength}, year = 2005, journal = {Journal of Computational and Graphical Statistics}, volume = 14, number = 3, pages = {511--528}, keywords = {number of clusters; prediction; Unsupervised learning}, doi = {10.1198/106186005X59243}, } @InProceedings{cluster:Topchy+Jain+Punch:2003, author = {A. Topchy and A. Jain and W. Punch}, title = {Combining Multiple Weak Clusterings}, booktitle = {Proceedings of the Third IEEE International Conference on Data Mining (ICDM'03)}, year = 2003, doi = {10.1109/ICDM.2003.1250937}, } @Article{cluster:Vichi:1999, author = {Vichi, Maurizio}, title = {One-mode Classification of a Three-way Data Matrix}, year = 1999, journal = {Journal of Classification}, volume = 16, pages = {27--44}, keywords = {Cluster analysis}, doi = {10.1007/s003579900041}, } @Article{cluster:Wallace:1983, author = {Wallace, David L.}, title = {Comments on ``{A} Method for Comparing Two Hierarchical Clusterings''}, year = 1983, journal = {Journal of the American Statistical Association}, volume = 78, pages = {569--576}, doi = {10.2307/2288118}, } @Inproceedings{cluster:Zhou+Li+Zha:2005, author = {Ding Zhou and Jia Li and Hongyuan Zha}, title = {A New {Mallows} Distance Based Metric for Comparing Clusterings}, booktitle = {ICML '05: Proceedings of the 22nd International Conference on Machine Learning}, year = 2005, isbn = {1-59593-180-5}, pages = {1028--1035}, location = {Bonn, Germany}, doi = {10.1145/1102351.1102481}, publisher = {ACM Press}, address = {New York, NY, USA}, } %%% Local Variables: *** %%% bibtex-maintain-sorted-entries: t *** %%% End: *** clue/data/0000755000175100001440000000000015145302607012134 5ustar hornikusersclue/data/GVME.rda0000644000175100001440000000221315145302610013352 0ustar hornikusersX;LQ~b"T&j0/`T0 cufVZha# Qh ,jfL3{߽}Ι;sdwD[BӴ+l"VŴJOh:ܨi5GWf13wq$!ǚN/CU^Kc>{L>q7Ӥ|Wm%>nwㄘ "=~UbfX/ 1Azև*⾤8bܡ<#dw!+4y_qt6~A[V*ۙc8R^/2it*ۮYK'Ȟiw)C+ݰn3FDf%uWϓ2ib=~tH|դ 5-:*ve3IbyniHn3c@h˹cVTMa()J+RZ:\=a-x9{unK`Zy{C}mv(_-9ݴOj5Xk@dKREʫG -B4XCۊPͷ,eۏ8U̲pnUL6R-cr<0괥BAаI@eHMA5D2M-~շ; $_ /.ɗyyf~E^{@y x)/@ukt<waŅJ38R)}_K?qU@P|v !E!$jl33U/hx+DIdj!`,2!T@>!=@ "/ Q]$ Uܳ'nT5 Uw4~G6#K UPgg|0`-V ( #ԋ-n/^ $rY7(4k^ї-z`}EEBg`P?aUS0kK: . sJb" d gv,2~{#*D`|@~ (x k Y HdI2~#=Bc|%AXs[BP]7iUeXJR}8@~d xclue/data/Kinship82_Consensus.rda0000644000175100001440000000143315145302610016436 0ustar hornikusersV?LQ?z"ք:8 eq!otWz־k¢,,MHLN*+ݻ{JMtr {t264-#b2S :|ϲyLomnڼ5M"P/z\8Úʳ}v(Kbq`4׸JqJklk}pe-}Γ-j=`;rwfay *y9NR~05w (0 y`zQ=eصvݱGZtNlцL'Jj.]1Xe[cO[/]EsHx(YuosV pS*Q v.o8`>֐dlQL>m"H D$}8;>#Y)BefMe5nbبlфEb:%`#1X@c[P7F6Ƹj2FjejuOҙGs W'?&W."SPAh&EUO 5'%<+O=K䩘'$=T{"Wr}ݿn?E+&E-JV7Q)s| {QCgxP9@H:Z/h 驉ϨMM})[ыE,*i*&o5N clue/data/CKME.rda0000644000175100001440000001151415145302610013337 0ustar hornikusers7zXZi"6!XK])TW"nRʟ%!},:McoV-Lqy)^j~ {L- K TR UDTد5&48s]AoAH]͈( tR|4L\urkIm͟3Ok yҜ;-AaxOݵA)BMbQL `kX 0=Pvnxjp#%:(pw<{=о1i<;WeGI/[H)BDz>7hk=A9G#xWnbygr/:$kAf9.C2>P+cG4vC&.:<c*M:G0Hjzl p{,XH.`(R,ɳ,Yʊw8puZtUBṙ;F`d2f?ZV@X xL@YDHk;;ݎT6NG،ɳ#r=D6b$؍s7#fi,.?elW&W>pC &q%>lw~DOӌ(Ƴ_bg}_ &3Ea+${,iG#ZY t>8<9ҌXe1&:+/;eV2T >*s'ސ>o4thx jz!ҁtZΨG@ zS 1b=[?62vEK\'kɔ CJ4A28ߞC<3<ֵ+:w/*\ ]W0 wΉ;%@X$7㟆];M@ /qߑw6`;J(&q_mwdPP:}3K2CbmE+/t's&؛Ux+0?w`ņ.tS)V rvb}vQa:DWqQ~ J_(Ǫ k= cs5oNjvs[${S\kC]g#ͷB Dԁ5>+~44pRlz?ƽ6-*ٳEQt4b \jBX~΅:$!P76 p4S{gM# ħ -F=@ధU@2yn&P N&`W'@3@%C{O(7{ۚcq# P,rsy RE}ȁ t_PDH8h7DGw+,/;C}hm4݀=5-4 [U wһ7\k5Y N؝'B}W 95!r&* ZM=;0YSDDz9ט숴3zu<+=| n+,=xXA*vXE-'ge:r+Fѣ&,vC-b S_K\{#J`xV0U$`6iqa8=_Chqz t-/FA>+o,E8PY*؂Ŧ`mPNn z_v4iWcl}vQ @GI 'wY֌>d)KGb _d"dtNFMY*E+TlBkZm R=hl ( EJo{Qlۨ m5E*,LIhTlq/3DZ W5Z Ѕ7Tvꮦ7V QfXҢ2:tF=Qޓ'Z2̦ЅT؆rj"y RȬWؑA, SAQPq8Ǐ w_ߙb\V ]{6kfb_=qDt9.݋NvpƮ[6qhEOFq6 ف:CM1.I H-xS |%> V%7Pqr[u'y4gϦ_;DA~ZI5w=,KW CnihV}ڈUs;.:,ɡ?j 88R@#+} LI.3,МL1s߭BL8ݓ;\Q\a,' If½F=PFhIIU~c7bT$ ĄC{o*wDD5 D6 EL^~7(9Sԇs"KLBj8tFw.=^=BuIGXul!b]7J"5o5}-S|ғ$]Z~rˏ9[NfNG!u2`^V,2 }{' ҟ (>G3mT̵8U2a#6YkeəY(FW aQE7VP7/贄#eDsuwJ B3RUg&"IYVs*7FIZ;۲CwlW{;7RI!|5O!M{XrV\h;:Pq,cN՟JM{ޏZTL;ƴLP%^w[)\11@JN2x[X( Ub- ⬕g(.bøq Ie a^(ы!,{Lo+QGx?ixX"YRg\gNj?M9ÅcF#3/h)fe}XF\|Wk tV}ׁS.!S 9򖝓gXP)SSG%ahCovn¦jq2Ğ*JCZzSkMlVA~ȩME(FWADrw ٠{\Z7X8ǫ3a1SV`v狀6Ab0#Gt)^fQ" , Lhb@9=msg[Q?k=>GTiNo;J ;BNς.?c{#v~^ 5̒6`ćYn x.D~j%|T?pt`/v 䓬b׌fBU6x8@(<FItl)0 e'R>|GFwջFzL؝na: >e˸M vaࡊ[H!R$S<&;Blb=3ýEDB zYnohKc߇%0n"YMȖYrEz|y6[~jJ78]gàqK IM Bsg5X v*u2Z <6\oA#cez=/Bg#JF*H ҰqFqk!zmsdm& L>0 YZclue/data/Kinship82.rda0000644000175100001440000000240315145302610014374 0ustar hornikusersBZh91AY&SY2BRJTT@PHT2h14@`M4a54dɣi0LM48ɓF!`i&@ FLz2h)Ii4 fChTEɁ/2%R뤸_K. $)4Ik$%ւ-K83ډKQl&ɲm[FѴm$ض-btJmI3 ObL$;nvI7RtZD).{ Zo&&*2Cʰuk c,ʲ2a`_0~gϹѝ& kf8ff:TOoa)ORM*dI>m>MԛdiA}Ywݧ %Q; 9\vsv9$@*&E39Socltp٘Y憱Lk YY|1fbkmZ0d`kÇ{I,@^$% $fJqVafwzO>\>KT&k\3~t- WF2f#͵XVغ뭿5roqo[.[YEg. ·^w]moݮmi߮߻كzc]1U> 7x 5_*e$X\O ~.ŵxclp]3 c <  %̄s2O&ְohc:Vt;beÑ%Zb3ߥ?|{b$%%IyQXIiy)~$rI>9'9GdzObL$}^T%It"\)8R\I-RK%!Ғ˝%߉|Kod( $$%%դT9'$؞ԓIJ]ܤ2Kq%.auzII)3݊M<);O[,II>xI9NRMNNI'zIOI$lRqjII$]MMy'I$I;$OIu:ddRMy'9&I5$$$'AIR{MO$$^RLy)&hJq'Iȓ|D$'"(H!~dclue/data/GVME_Consensus.rda0000644000175100001440000000415415145302610015420 0ustar hornikusersZilTUQ*""j,)1,UB1)C7uf%P0PQRJHKtvf:of}߽;È21~ͽsw|9j)Y6-ŖaM5βzc'.sM{lsL͖55i}4@=7SzON#߰ή??B v?0zT7OQ~÷]s|6yQlᬛ%vUtvPPTGS/^Cٻ8EnP[{hߺHM!?7J[5o3^<_y鞪(y>iv2DpL"ohoQ4ĥ'Ty/&~5a}Pއg%ŀ!j꫿ o^Q8h;#?yN٪v^,O|ҤO\9RHs{e =RRL@T;$2&TND=GgjשQfWsyvRz^DʼnۮOGcݚJt2,tͣ*y%0^fm.;?ʼϴk(p{vNW N]] hKi'dI#c|fdMk%v[ ?Nk/kYrҦ{=!ѩǩҳGٽjY-[^du ŝ4@"܎_$>'RL5"L+)&AFb)`K\O5`RմmdVHʢ(E G8T[Ll}q4LaQ) @7(?hk .EW+#h#WB7 &-o} cB]I vo]h:>?'?07JR#h1 /\o󥂕.#1WO;w ^.Bf` X߳Xu>( @+Jb;nm]И IGDB)Jiĸ<FL_ g˖p)edvAuςlXhOc2U +IFI2ndĞB KuDaó2AZ3`@XiH4 ?&_P` rO QqFNȇS/u:Nǟ S) &Pr.r0Q5>pʋɽZBxda-g2 !,e!Yg$$u2:mL'Lΰ]]c C ˍ'7O,iKtu63EfmX(=U*#clue/data/Cassini.rda0000644000175100001440000003170515145302610014215 0ustar hornikusers|y87RF鑩424l"*dH A&"2gNڙgᘏpLY~n{^k}}? E'qqqp.;'/n.^.~N˧iqƅ+ d\ÔCo4RqFZu(U7o˼n׭PSQ|,ZJ>mnvhfb&'e~2ĵq};.1ߙie]phv#KܻI?7Fߣӻ0=pvl!b~8P1O\w톓!S^{N)~)WE_~@/q^ft z3o] >} .{m\|\z:1zŲrBܳjӣ8 \fGwe_S[a(dcpbU t}\7w)(G1EJ)J.2(wړBA#/9^E_ϴ)ᡕ~!GEvE +h~y[poрxuUJT0rAv)U/z,+}w>?xTb۽ET+4b4 -{v eecݩ~ӖN9*_zģz]q?~rN,=Q[5/]AOqwUܸD1#..Qr3 $\`zqp jY4U[bC {MAA4(렰Rg%ļr8PWx(JfAB5Y팈,V8W{DEby-m)Q$H_{~ ֆ ߥq{AcOќ-xs~ۼ1ğJ~c ጓ/r嗠&]ѐmѻfU9't=¹pboN%~>o!!\s :8iq"ZP"_'U~կmw C*T.hj+hɁ;(T@^].'Ma_i[fÍZ8 >-ǞΠ>O>ը2uqֈyQp ³ǑzU׈8|Y%J;L>3$,vD,Yi>8sb<"sӇhT?U)(9.2fqe*W e}^+*0]QӏMcoPPKL\{\(76(Ih!2'wTgpsZQ$2K⾅w<@|kZ桺c@MD54Pقa{KgP޲0O#4*Zt'JÍ.\žt K]|1PaYGEO{χQw|)K;k`W.3n9MQx 6(QS[LY&m1?]k6^wc"gz |mFx׸Ce"G֡%+&}Em.cU2煤qGm73+U׃yÖjuE%b.,TDˤD# 2CP-SшT5^W_]ٵӄ﷡..SWbV0x=[\ ;ͣ/< 705fu3{z3fXj%uv|d<3`Y|a-Ekm~5yiN̉h.\=ݗ~X!LyRFzqdLI}%F)W7]CWP piujѡ 9sގ /i1[biy'vy<>}2v6 󱡢w'd6#Jԅ98n69|*]H[~LPIM++/[俬_)qEpO4dg4&}:),d/)OC'R^0[]>s9kvy@̬`7j> T{ܡA:?}6ى-?aᏻ,8gn5jVE7O0iP@44=С$ɪP̤'H=#yIh梾8Rl0_ӹKiyYSm2Ր,wH)E;sJA#{[SgU{tkS{U8Wڇc,~#=a,|.<}#nPȣS"Q5=cP͢f/*;؀VhԡmӍƿpc֨ZȲ꽨A(vsN(S{BKrqo߃&_ne’QM ~(g?78B QXMzDxS1,SSqOSqOƎ:}1}kmD㝃29/`uTphCJ᫟z]WfPv-1 ,Ka;e*c#Wj>Ġ=eu6ABzN4!5dy A:#ҥd4{ٍ[( hG0+. ]F (Y vA}TK6<$\JɇBtC-h8b_߅(@q#u[%~/wx{l~d67C Mbᡵw\=|h&P7 wyk- :4|eޛJHU<*[̭3+Ŭ'n`<%d׮?; Faz@ aܷd˃_(MWp>QP|{.ٹoD[Y/7y 1=x|{^D+8a_(23.2)c:._noCW\Ɲ{G_Hr˼E0O-n`)O4 SQ@WN1CvJW^ۍ:?Nqr߸ROKp(l+/8Sk<'i8f y;<1`|vzӭ¶,ϑC/K>gaZ54Ľe6^辣-R/zkVvZ8/=6Y{?8j׉b pcɲ]yfnHl#KML?$Ĕ^9!ۺFĊ]ɞ݇`&=@4 ey`5i ywguGQͱу2"zfJ#U}Zfb7-pEoŨx_ݳᒦ1y\=q5jrhZ4zϛ)\UCK12M<bp;!f~ e:nBiw-+۽NflL1f s/:. tusG3ˇFEcb=p1kU_kzuzCuP׿3#ȲIhl}kxDv%\42RN{{}OևꗋcU3W}9%`\{&Kۓڞ\d'l~%璼Hm>x*z?VW>\}Ήg!ax93fUﯻ~3JϠ~9,yzyMK譝V!T_`,#| ͊]p%u'3twcM8@#K%-_o{Yӂ_mBt7s4\ZڿVUrB^Q\a$lŖ@/]4sϺyuO'N>û :l>IY;GG%,`1ͤ̚y쫫4V'QJq-=(OcMf^*x Y֪ƹbZZ!u6BČNlJǃ=29쫲*ZI?5G- pGud [uh[ݛ;#niwJi])Kj1yu]82IU 5 ;=>F=[.?F n*s-d~QY3qkvcuze-h_`iF8%͢g'P4yҋX{b sG*c C*ѼD0GCJ'E0"]aYϨTqn|WO.ݷ*aIn6)TSwhו|~W 7NDD^Ý{{prէp%T PƤQo<_E[6ԙ#ROuT2$83Jt0Bziϝ<ҟԸz9f%5~A3 /tW&EkpJ&Pݫ:G09P{{KVo9PˏDjqϱhV{*?3?LܯI&"-k QtCkPqySϳpO~8Y?ZZ4^ic_=;2m$',7C8>MWrȧs Q5EKuO-Ѻ9/P..@̣2ߤB+r<*a-EAyvUţIL)Fmmqކ{T0Cz+5 JMKD}mjWƿcʩg>sp5Zdol5#7q恳&Tm21 ¼*7sO# 3,MC7\E#Wuoi`&wܿ\V҈,s)a®–E!~=ԻCfsw~7Z K&f%D+P|֝(L6nFM}K✣m@LR-FqE43Ʒl& "nU(} *{SViZՅj)YgNè2j#fw=;Q[c^ =nTYK'wȩo_EKC 9n1K-_pۢ$Eèi~#(maL柎NذժJIAwj©Q6l\a1਼^*(Ki}MuV\7w- ѽdHoĽ]aG:tIl9_n3x~]ێ}_u+b8[هKٱ_s<7Fݛ(I+A|&j =ףּ:;D}2#Hi9Qܨ^qw䜫w>2څ.|#OECG&JNz*dϐԧ%u}J0hP=$s]¾L"e{-Uj9 \pHI{(CsӸuh&nB<q4l#6^>6aw!w(FƞG#;i9;)bl1 6 Bws9RsRo58L@U?=5 OscWM9?.y͑蕖G7:rqqH%%%lㅵy'9}2+rD8󯂄rK9 j"˞_=p-3yzqЗ8 m EA[zgSD?sC+|<U7܄@;; qp=<5+j| +ׂk.! .]qk^ ǎ>JFOaAU$G>jkB_xvcȩ { 4b?`<%Dl1򶈁30_%}Ǐvʤ ?~9|8ae~lj'$/=C! /=Z)T~=@W=skǫF Ǿ4kdLWym{WZ <@Nz.fO}{gRUЛ, jQOp[̪臍>h#f/Ί#Y ?I_m<ٱMⰱ3)Ho@0f"*-=Vwf3ƝJ9zҚ+0<gW%s/PNCC?tWIX;;H& ă[ -h6uXj;q usJN>k|RopQ:KZ)°1ߕ #A_ʗ 7zb"hk,yE;d8DAӴmϘ20a O8E-Ͻ!UCy(OJ(uZQ:pU FIzᯬ~s n@rXQ 8:{!]ANH#Zﺍ-f.3TGѫE5T⯩ܱ hd`='۬A^{iG~*xL&o>AC)3rrGq?eo1yC<[h&ͿG3~_Fxoa+<Xҟ^נ Ԁ]@~} YQ1~, Ӟ=\GdS u3ZzgI)s >b.!,#|[wbA[/'O^$O~fp剹nv0EZZ!'f%}fEGq_{o=ƙ͞L(!3Ssy# nqTD\[0O,#pҲԠl~ъ+` _@Eom&gS :큃;"o;} ]ÝZ0?6" $3gr*;KAXb}zz8#s/g޵s-.YK? Q>7xe_Ť.HGWO&+@͓ٱ,i3lFT=@!u$y3O!t@>gԽ.*xE:WF_eĿ"{I}(x1șvC:pk"q3~cR'<#gr~߱,Ը7@~/5C~0=W!qz%Wyct(.&ٰ,_> -qMD$}l"*Y=ৎ,MQ3ܔQAo~!i*->&\B=-Fl ף:v @ 7J8ȟ2\O^5vc'/nRVo8JPybG:hSqVhhiF f UBiϧ~ y3>o3~ vbcDw Y*TC*IԑEͤnXL8R%WYٯK'xbM}t1.UzqMx4sw ٯgMݨιxz%גlо@5%Jʟ}]%y%O>Lx$uDK pN>؟ vVp9I\]K%!~&/"w~E_1WIcnʹO|H1Ӗd6I!oK&q.+[4&p'#8\h19 4U]O 2@iّd}ΧI4?ɋd yֈW2į5oyoв+Hd8  z&@;w[]'e%Ð5MSv1:;6Gp3_!9G=!|Cn>Os#睑$&5H=z(g d{h;~pj.\ M`.ɫ6×+<\l rf_5˯8k+k$?v_FjwGx`?5XFlMYx:L-;Jo"r*g s g.KCLΛ~7+13 zSߴ9E$W. P#m~EO}?uoЛ#") e}h~4q"|͍vBܢFe -83O1A9k/ŏ1@GzRZZFǜثqOI[m>Y/+"t[6s)=%'l;|+3kFߨvzghBqT 䍕< {L7xq'7T5vpu&3ٷ$I nQ_{̸]d4n ?thCeUX -S8'~J <* BЌ+nZft7aXG^K}UKwRG::`1<_-h.m 7}A/˦ٟ>V :3tԻRg&=IJ\Bp/o-%PbYՓߒLc]'sM[ bǻ$?%d  z z\H= s8G5- wtIgϸh x77H xT; G$/DKAB7ɫ|=A]18#{<8ȯzIV`M)- "wg#V6dSKDbG291!q68f'u!%CTOl-gyc̲poQ)=7zwu^fb9OVI ovEhi+DH*U$JshpF?ג74MJVY_cyMC`|8ІXVVptn#ޘsa_=)<$_j}-L{w~W"WkΖ{}S*ut43@ةmCCՉ{d[ ?rp@?і?uÝhN'Oc\0ԫ5"?FεI gurfӎ>ζ$?]I]LPh#zQr {WJ{ {Oj}"#0M*M{nܫ\_iU@K蚴r=-ʔI3PviLH~KГ_%>ϐ&e˭KrirOy^Dew؟ӂM޶ 0 CJ7*X}pߕJldAO(Ax_kO|O!@sA)܋>1S} )ӻGg{T՝8Jy~CMvod\"*aQ',pC[\jEQm]N 8r 8s F{3i{ rY`,;-^i}Sr?"a#mi4fcR"仈`Gu7/[_n'ߢu -`ꃽmp}x_<'wbH<Ј-/z6߱~ ~|ܳyQ@_D"G{l$θd_5~G_\U^'"sY .F|M)bo6w<*1 tvQjςاM仔mƃ s|߀_}tpWBuE r1a7ba}NXu֫Vܿ=[z|oc5P 8.#QE it[}(C^1# cG`GJ`n OXK'q,+|# So,uTX vt|,n )TڹAȫZ e yx {!Q&W;J.S$|ݳ<~$gn59R'L7k=}A,8)בzu! ޮ(ܦ2I7tC*AiRp='Qs]AX+1W0NP}guQ;i;N%xP!@)z0w!KȽGc^g/s9 ϣrqOx:/%Df?bou4 +ǫ׹W0&?neWQ5;5rdOclue/data/Phonemes.rda0000644000175100001440000000152615145302610014400 0ustar hornikusersV[HTQP"=)"")͈Q6 ⇉E2)"zhKSKS2aFE "Q}D{{aI ̽{9g^Y8 ðpfvyb؍i="%7ߛCOݐaX|,3vXjP| ЕS+m7ݥSA<~>+gOjX!4w_* Vfjof|L&vTVK:U ^5"2ՙ9N/}ŕnj7K#'uo_|Q/ @JDmc΢u[Bsg]S?R rZ.ΨqG\kQju =T Վ+5CWzWxn; p->runs = 0; /* allocate memory */ p->s = calloc(1 + n, sizeof(int)); p->f = calloc(1 + n, sizeof(int)); ri = calloc(1 + n, sizeof(int)); ci = calloc(1 + n, sizeof(int)); if(ri == NULL || ci == NULL || p->s == NULL || p->f == NULL) error("ap_hungarian: could not allocate memory!"); preprocess(p); preassign(p); while(p->na < n){ if(REDUCE == cover(p, ri, ci)) reduce(p, ri, ci); ++p->runs; } end = time(0); p->rtime = end - start; /* check if assignment is a permutation of (1..n) */ for(i = 1; i <= n; i++){ ok = 0; for(j = 1; j <= n; j++) if(p->s[j] == i) ++ok; if(ok != 1) error("ap_hungarian: error in assigment, is not a permutation!"); } /* calculate cost of assignment */ p->cost = 0; for(i = 1; i <= n; i++) p->cost+= p->C[i][p->s[i]]; /* reset result back to base-0 indexing */ for(i = 1; i <= n; i++) p->s[i - 1] = p->s[i] - 1; /* free memory */ free(ri); free(ci); } /* abbreviated interface */ int ap_assignment(AP *p, int *res) { int i; if(p->s == NULL) ap_hungarian(p); for(i = 0; i < p->n; i++) res[i] = p->s[i]; return p->n; } /*******************************************************************/ /* constructors */ /* read data from file */ /*******************************************************************/ AP *ap_read_problem(char *file) { FILE *f; int i,j,c; int m,n; double x; double **t; int nrow,ncol; AP *p; f = fopen(file,"r"); if(f==NULL) return NULL; t = (double **)malloc(sizeof(double*)); m = 0; n = 0; nrow = 0; ncol = 0; while(EOF != (i = fscanf(f, "%lf", &x))){ if(i == 1){ if(n == 0){ t = (double **) realloc(t,(m + 1) * sizeof(double *)); t[m] = (double *) malloc(sizeof(double)); }else t[m] = (double *) realloc(t[m], (n + 1) * sizeof(double)); t[m][n++] = x; ncol = (ncol < n) ? n : ncol; c=fgetc(f); if(c == '\n'){ n = 0; ++m; nrow = (nrow < m) ? m : nrow; } } } fclose(f); /* prepare data */ if(nrow != ncol){ /* fprintf(stderr,"ap_read_problem: problem not quadratic\nrows =%d, cols = %d\n",nrow,ncol); */ warning("ap_read_problem: problem not quadratic\nrows = %d, cols = %d\n", nrow, ncol); return NULL; } p = (AP*) malloc(sizeof(AP)); p->n = ncol; p->C = (double **) malloc((1 + nrow)*sizeof(double *)); p->c = (double **) malloc((1 + nrow)*sizeof(double *)); if(p->C == NULL || p->c == NULL) return NULL; for(i = 1; i <= nrow; i++){ p->C[i] = (double *) calloc(ncol + 1, sizeof(double)); p->c[i] = (double *) calloc(ncol + 1, sizeof(double)); if(p->C[i] == NULL || p->c[i] == NULL) return NULL; } for(i = 1; i <= nrow; i++) for( j = 1; j <= ncol; j++){ p->C[i][j] = t[i-1][j-1]; p->c[i][j] = t[i-1][j-1]; } for(i = 0; i < nrow; i++) free(t[i]); free(t); p->cost = 0; p->s = NULL; p->f = NULL; return p; } AP *ap_create_problem_from_matrix(double **t, int n) { int i,j; AP *p; p = (AP*) malloc(sizeof(AP)); if(p == NULL) return NULL; p->n = n; p->C = (double **) malloc((n + 1) * sizeof(double *)); p->c = (double **) malloc((n + 1) * sizeof(double *)); if(p->C == NULL || p->c == NULL) return NULL; for(i = 1; i <= n; i++){ p->C[i] = (double *) calloc(n + 1, sizeof(double)); p->c[i] = (double *) calloc(n + 1, sizeof(double)); if(p->C[i] == NULL || p->c[i] == NULL) return NULL; } for(i = 1; i <= n; i++) for( j = 1; j <= n; j++){ p->C[i][j] = t[i-1][j-1]; p->c[i][j] = t[i-1][j-1]; } p->cost = 0; p->s = NULL; p->f = NULL; return p; } /* read data from vector */ AP *ap_create_problem(double *t, int n) { int i,j; AP *p; p = (AP*) malloc(sizeof(AP)); if(p == NULL) return NULL; p->n = n; p->C = (double **) malloc((n + 1) * sizeof(double *)); p->c = (double **) malloc((n + 1) * sizeof(double *)); if(p->C == NULL || p->c == NULL) return NULL; for(i = 1; i <= n; i++){ p->C[i] = (double *) calloc(n + 1, sizeof(double)); p->c[i] = (double *) calloc(n + 1, sizeof(double)); if(p->C[i] == NULL || p->c[i] == NULL) return NULL; } for(i = 1; i <= n; i++) for( j = 1; j <= n; j++){ p->C[i][j] = t[n*(j - 1) + i - 1]; p->c[i][j] = t[n*(j - 1) + i - 1]; } p->cost = 0; p->s = NULL; p->f = NULL; return p; } /* destructor */ void ap_free(AP *p) { int i; free(p->s); free(p->f); for(i = 1; i <= p->n; i++){ free(p->C[i]); free(p->c[i]); } free(p->C); free(p->c); free(p); } /* set + get functions */ /* void ap_show_data(AP *p) { int i, j; for(i = 1; i <= p->n; i++){ for(j = 1; j <= p->n; j++) printf("%6.2f ", p->c[i][j]); printf("\n"); } } */ double ap_mincost(AP *p) { if(p->s == NULL) ap_hungarian(p); return p->cost; } int ap_size(AP *p) { return p->n; } int ap_time(AP *p) { return (int) p->rtime; } int ap_iterations(AP *p) { return p->runs; } /* void ap_print_solution(AP *p) { int i; printf("%d itertations, %d secs.\n",p->runs, (int)p->rtime); printf("Min Cost: %10.4f\n",p->cost); for(i = 0; i < p->n; i++) printf("%4d",p->s[i]); printf("\n"); } */ int ap_costmatrix(AP *p, double **m) { int i,j; for(i = 0; i < p->n; i++) for(j = 0; j < p->n; j++) m[i][j] = p->C[i + 1][j + 1]; return p->n; } int ap_datamatrix(AP *p, double **m) { int i,j; for(i = 0; i < p->n; i++) for(j = 0; j < p->n; j++) m[i][j] = p->c[i + 1][j + 1]; return p->n; } /* error reporting */ /* void ap_error(char *message) { fprintf(stderr,"%s\n",message); exit(1); } */ /*************************************************************/ /* these functions are used internally */ /* by ap_hungarian */ /*************************************************************/ int cover(AP *p, int *ri, int *ci) { int *mr, i, r; int n; n = p->n; mr = calloc(1 + p->n, sizeof(int)); /* reset cover indices */ for(i = 1; i <= n; i++){ if(p->s[i] == UNASSIGNED){ ri[i] = UNCOVERED; mr[i] = MARKED; } else ri[i] = COVERED; ci[i] = UNCOVERED; } while(TRUE){ /* find marked row */ r = 0; for(i = 1; i <= n; i++) if(mr[i] == MARKED){ r = i; break; } if(r == 0) break; for(i = 1; i <= n; i++) if(p->c[r][i] == 0 && ci[i] == UNCOVERED){ if(p->f[i]){ ri[p->f[i]] = UNCOVERED; mr[p->f[i]] = MARKED; ci[i] = COVERED; }else{ if(p->s[r] == UNASSIGNED) ++p->na; p->f[p->s[r]] = 0; p->f[i] = r; p->s[r] = i; free(mr); return NOREDUCE; } } mr[r] = UNMARKED; } free(mr); return REDUCE; } void reduce(AP *p, int *ri, int *ci) { int i, j, n; double min; n = p->n; /* find minimum in uncovered c-matrix */ min = DBL_MAX; for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) if(ri[i] == UNCOVERED && ci[j] == UNCOVERED){ if(p->c[i][j] < min) min = p->c[i][j]; } /* subtract min from each uncovered element and add it to each element */ /* which is covered twice */ for(i = 1; i <= n; i++) for(j = 1; j <= n; j++){ if(ri[i] == UNCOVERED && ci[j] == UNCOVERED) p->c[i][j]-= min; if(ri[i] == COVERED && ci[j] == COVERED) p->c[i][j]+= min; } } void preassign(AP *p) { int i, j, min, r, c, n, count; int *ri, *ci, *rz, *cz; n = p->n; p->na = 0; /* row and column markers */ ri = calloc(1 + n, sizeof(int)); ci = calloc(1 + n, sizeof(int)); /* row and column counts of zeroes */ rz = calloc(1 + n, sizeof(int)); cz = calloc(1 + n, sizeof(int)); for(i = 1; i <= n; i++){ count = 0; for(j = 1; j <= n; j++) if(p->c[i][j] == 0) ++count; rz[i] = count; } for(i = 1; i <= n; i++){ count = 0; for(j = 1; j <= n; j++) if(p->c[j][i] == 0) ++count; cz[i] = count; } while(TRUE){ /* find unassigned row with least number of zeroes > 0 */ min = INT_MAX; r = 0; for(i = 1; i <= n; i++) if(rz[i] > 0 && rz[i] < min && ri[i] == UNASSIGNED){ min = rz[i]; r = i; } /* check if we are done */ if(r == 0) break; /* find unassigned column in row r with least number of zeroes */ c = 0; min = INT_MAX; for(i = 1; i <= n; i++) if(p->c[r][i] == 0 && cz[i] < min && ci[i] == UNASSIGNED){ min = cz[i]; c = i; } if(c){ ++p->na; p->s[r] = c; p->f[c] = r; ri[r] = ASSIGNED; ci[c] = ASSIGNED; /* adjust zero counts */ cz[c] = 0; for(i = 1; i <= n; i++) if(p->c[i][c] == 0) --rz[i]; } } /* free memory */ free(ri); free(ci); free(rz); free(cz); } void preprocess(AP *p) { int i, j, n; double min; n = p->n; /* subtract column minima in each row */ for(i = 1; i <= n; i++){ min = p->c[i][1]; for(j = 2; j <= n; j++) if(p->c[i][j] < min) min = p->c[i][j]; for(j = 1; j <= n; j++) p->c[i][j]-= min; } /* subtract row minima in each column */ for(i = 1; i <= n; i++){ min = p->c[1][i]; for(j = 2; j <= n; j++) if(p->c[j][i] < min) min = p->c[j][i]; for(j = 1; j <= n; j++) p->c[j][i]-= min; } } clue/src/assignment.h0000644000175100001440000000334511623271704014340 0ustar hornikusers#include #include #include #include /* INT_MAX */ #include /* DBL_MAX */ #include #include /* constants used for improving readability of code */ #define COVERED 1 #define UNCOVERED 0 #define ASSIGNED 1 #define UNASSIGNED 0 #define TRUE 1 #define FALSE 0 #define MARKED 1 #define UNMARKED 0 #define REDUCE 1 #define NOREDUCE 0 typedef struct{ int n; /* order of problem */ double **C; /* cost matrix */ double **c; /* reduced cost matrix */ int *s; /* assignment */ int *f; /* column i is assigned to f[i] */ int na; /* number of assigned items; */ int runs; /* number of iterations */ double cost; /* minimum cost */ time_t rtime; /* time */ } AP; /* public interface */ /* constructors and destructor */ AP *ap_create_problem(double *t, int n); AP *ap_create_problem_from_matrix(double **t, int n); AP *ap_read_problem(char *file); void ap_free(AP *p); int ap_assignment(AP *p, int *res); int ap_costmatrix(AP *p, double **m); int ap_datamatrix(AP *p, double **m); int ap_iterations(AP *p); void ap_hungarian(AP *p); double ap_mincost(AP *p); void ap_print_solution(AP *p); void ap_show_data(AP *p); int ap_size(AP *p); int ap_time(AP *p); /* error reporting */ void ap_error(char *message); /* private functions */ void preprocess(AP *p); void preassign(AP *p); int cover(AP *p, int *ri, int *ci); void reduce(AP *p, int *ri, int *ci); clue/src/clue.h0000644000175100001440000000210114245105414013102 0ustar hornikusers#ifndef _CLUE_H #define _CLUE_H #include typedef int Sint; void solve_LSAP(double *c, Sint *n, Sint *p); double **clue_vector_to_square_matrix(double *x, Sint n); void clue_dissimilarity_count_inversions(double *x, double *y, Sint *n, double *count); void deviation_from_ultrametricity(double *x, int *n, double *v, int *max); void deviation_from_ultrametricity_gradient(double *x, int *n, double *out); void deviation_from_additivity(double *x, int *n, double *v, int *max); void deviation_from_additivity_gradient(double *x, int *n, double *out); void ls_fit_ultrametric_by_iterative_reduction(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose); void ls_fit_ultrametric_by_iterative_projection(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose); void ls_fit_addtree_by_iterative_reduction(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose); void ls_fit_addtree_by_iterative_projection(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose); #endif clue/src/trees.c0000644000175100001440000002401211304023137013266 0ustar hornikusers#include #include #include "clue.h" static int iwork3[3]; static int iwork4[4]; static void isort3(int *i, int *j, int *k) { iwork3[0] = *i; iwork3[1] = *j; iwork3[2] = *k; R_isort(iwork3, 3); *i = iwork3[0]; *j = iwork3[1]; *k = iwork3[2]; } static void isort4(int *i, int *j, int *k, int *l) { iwork4[0] = *i; iwork4[1] = *j; iwork4[2] = *k; iwork4[3] = *l; R_isort(iwork4, 4); *i = iwork4[0]; *j = iwork4[1]; *k = iwork4[2]; *l = iwork4[3]; } void deviation_from_ultrametricity(double *x, int *n, double *v, int *max) { double **D, p, delta, A, B, C; int i, j, k; D = clue_vector_to_square_matrix(x, *n); p = 0; for(i = 0; i < *n - 2; i++) for(j = i + 1; j < *n - 1; j++) { A = D[i][j]; for(k = j + 1; k < *n; k++) { B = D[i][k]; C = D[j][k]; if((A <= B) && (A <= C)) delta = C - B; else if(B <= C) delta = A - C; else delta = B - A; if(*max) p = fmax2(p, fabs(delta)); else p += delta * delta; } } *v = p; } void deviation_from_ultrametricity_gradient(double *x, int *n, double *out) { double **D, **G, A, B, C, delta; int i, j, k; D = clue_vector_to_square_matrix(x, *n); G = clue_vector_to_square_matrix(out, *n); for(i = 0; i < *n - 2; i++) for(j = i + 1; j < *n - 1; j++) { A = D[i][j]; for(k = j + 1; k < *n; k++) { B = D[i][k]; C = D[j][k]; if((A <= B) && (A <= C)) { delta = 2 * (B - C); G[i][k] += delta; G[j][k] -= delta; } else if(B <= C) { delta = 2 * (C - A); G[j][k] += delta; G[i][j] -= delta; } else { delta = 2 * (A - B); G[i][j] += delta; G[i][k] -= delta; } } } for(i = 0; i < *n; i++) for(j = 0; j < *n; j++) *out++ = G[i][j]; } void deviation_from_additivity(double *x, int *n, double *v, int *max) { double **D, p, delta, A, B, C; int i, j, k, l; D = clue_vector_to_square_matrix(x, *n); p = 0; for(i = 0; i < *n - 3; i++) for(j = i + 1; j < *n - 2; j++) for(k = j + 1; k < *n - 1; k++) for(l = k + 1; l < *n; l++) { A = D[i][j] + D[k][l]; B = D[i][k] + D[j][l]; C = D[i][l] + D[j][k]; if((A <= B) && (A <= C)) delta = (C - B); else if(B <= C) delta = (A - C); else delta = (B - A); if(*max) p = fmax2(p, fabs(delta)); else p += delta * delta; } *v = p; } void deviation_from_additivity_gradient(double *x, int *n, double *out) { double **D, **G, A, B, C, delta; int i, j, k, l; D = clue_vector_to_square_matrix(x, *n); G = clue_vector_to_square_matrix(out, *n); for(i = 0; i < *n - 3; i++) for(j = i + 1; j < *n - 2; j++) for(k = j + 1; k < *n - 1; k++) for(l = k + 1; l < *n; l++) { A = D[i][j] + D[k][l]; B = D[i][k] + D[j][l]; C = D[i][l] + D[j][k]; if((A <= B) && (A <= C)) { delta = 2 * (B - C); G[i][l] -= delta; G[j][k] -= delta; G[i][k] += delta; G[j][l] += delta; } else if(B <= C) { delta = 2 * (C - A); G[i][l] += delta; G[j][k] += delta; G[i][j] -= delta; G[k][l] -= delta; } else { delta = 2 * (A - B); G[i][k] -= delta; G[j][l] -= delta; G[i][j] += delta; G[k][l] += delta; } } for(i = 0; i < *n; i++) for(j = 0; j < *n; j++) *out++ = G[i][j]; } void ls_fit_ultrametric_by_iterative_reduction(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose) { double A, B, C, **D, DQ, delta, tmp; int i, i1, j, j1, k, k1, N3; D = clue_vector_to_square_matrix(d, *n); /* And initialize the upper half of D ("work array") to 0. (Yes, this could be done more efficiently by just propagating the veclh dist representation.) */ for(i = 0; i < *n - 1; i++) for(j = i + 1; j < *n; j++) D[i][j] = 0; N3 = (*n - 2); for(*iter = 0; *iter < *maxiter; (*iter)++) { if(*verbose) Rprintf("Iteration: %d, ", *iter); for(i1 = 0; i1 < *n - 2; i1++) for(j1 = i1 + 1; j1 < *n - 1; j1++) for(k1 = j1 + 1; k1 < *n; k1++) { i = order[i1]; j = order[j1]; k = order[k1]; isort3(&i, &j, &k); A = D[j][i]; B = D[k][i]; C = D[k][j]; /* B & G have a divisor of 2 for case 1 and 4 for cases 2 and 3 ... clearly, we should use the same in all cases, but should it be 2 or 4? */ if((A <= B) && (A <= C)) { /* Case 1: 5080 */ DQ = (C - B) / 2; D[i][k] += DQ; D[j][k] -= DQ; } else if(B <= C) { /* Case 2: 5100 */ DQ = (C - A) / 2; D[i][j] += DQ; D[j][k] -= DQ; } else { /* Case 3: 5120 */ DQ = (B - A) / 2; D[i][j] += DQ; D[i][k] -= DQ; } } delta = 0; for(i = 0; i < *n - 1; i++) for(j = i + 1; j < *n; j++) { tmp = D[i][j] / N3; D[j][i] += tmp; D[i][j] = 0; delta += fabs(tmp); } if(*verbose) Rprintf("change: %f\n", delta); if(delta < *tol) break; } /* And now write results back. Could make this more efficient, of course ... */ for(j = 0; j < *n; j++) for(i = 0; i < *n; i++) *d++ = D[i][j]; } void ls_fit_ultrametric_by_iterative_projection(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose) { double A, B, C, **D, delta; int i, i1, j, j1, k, k1; D = clue_vector_to_square_matrix(d, *n); for(*iter = 0; *iter < *maxiter; (*iter)++) { if(*verbose) Rprintf("Iteration: %d, ", *iter); delta = 0; for(i1 = 0; i1 < *n - 2; i1++) for(j1 = i1 + 1; j1 < *n - 1; j1++) for(k1 = j1 + 1; k1 < *n; k1++) { i = order[i1]; j = order[j1]; k = order[k1]; isort3(&i, &j, &k); A = D[i][j]; B = D[i][k]; C = D[j][k]; if((A <= B) && (A <= C)) { D[i][k] = D[j][k] = (B + C) / 2; delta += fabs(B - C); } else if(B <= C) { D[i][j] = D[j][k] = (C + A) / 2; delta += fabs(C - A); } else { D[i][j] = D[i][k] = (A + B) / 2; delta += fabs(A - B); } } if(*verbose) Rprintf("change: %f\n", delta); if(delta < *tol) break; } for(i = 0; i < *n - 1; i++) for(j = i + 1; j < *n; j++) D[j][i] = D[i][j]; /* And now write results back. Could make this more efficient, of course ... */ for(j = 0; j < *n; j++) for(i = 0; i < *n; i++) *d++ = D[i][j]; } void ls_fit_addtree_by_iterative_reduction(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose) { /* Once we have ls_fit_ultrametric_by_iterative_reduction() we can always do this as well ... See page 67f in Barthelemy and Guenoche. */ double A, B, C, **D, DQ, delta, tmp, N3; int i, i1, j, j1, k, k1, l, l1; D = clue_vector_to_square_matrix(d, *n); /* And initialize the upper half of D ("work array") to 0. (Yes, this could be done more efficiently by just propagating the veclh dist representation.) */ for(i = 0; i < *n - 1; i++) for(j = i + 1; j < *n; j++) D[i][j] = 0; N3 = (*n - 2) * (*n - 3) / 2; for(*iter = 0; *iter < *maxiter; (*iter)++) { if(*verbose) Rprintf("Iteration: %d, ", *iter); for(i1 = 0; i1 < *n - 3; i1++) for(j1 = i1 + 1; j1 < *n - 2; j1++) for(k1 = j1 + 1; k1 < *n - 1; k1++) for(l1 = k1 + 1; l1 < *n; l1++) { i = order[i1]; j = order[j1]; k = order[k1]; l = order[l1]; isort4(&i, &j, &k, &l); A = D[j][i] + D[l][k]; B = D[k][i] + D[l][j]; C = D[l][i] + D[k][j]; if((A <= B) && (A <= C)) { /* Case 1: 5090 */ DQ = (C - B) / 4; D[i][l] -= DQ; D[j][k] -= DQ; D[i][k] += DQ; D[j][l] += DQ; } else if(B <= C) { /* Case 2: 5120 */ DQ = (A - C) / 4; D[i][l] += DQ; D[j][k] += DQ; D[i][j] -= DQ; D[k][l] -= DQ; } else { /* Case 3: 5150 */ DQ = (B - A) / 4; D[i][k] -= DQ; D[j][l] -= DQ; D[i][j] += DQ; D[k][l] += DQ; } } delta = 0; for(i = 0; i < *n - 1; i++) for(j = i + 1; j < *n; j++) { tmp = D[i][j] / N3; D[j][i] += tmp; D[i][j] = 0; delta += fabs(tmp); } if(*verbose) Rprintf("change: %f\n", delta); if(delta < *tol) break; } /* And now write results back. Could make this more efficient, of course ... */ for(j = 0; j < *n; j++) for(i = 0; i < *n; i++) *d++ = D[i][j]; } void ls_fit_addtree_by_iterative_projection(double *d, int *n, int *order, int *maxiter, int *iter, double *tol, int *verbose) { double A, B, C, **D, DQ, delta; int i, i1, j, j1, k, k1, l, l1; D = clue_vector_to_square_matrix(d, *n); for(*iter = 0; *iter < *maxiter; (*iter)++) { delta = 0; if(*verbose) Rprintf("Iteration: %d, ", *iter); for(i1 = 0; i1 < *n - 3; i1++) for(j1 = i1 + 1; j1 < *n - 2; j1++) for(k1 = j1 + 1; k1 < *n - 1; k1++) for(l1 = k1 + 1; l1 < *n; l1++) { i = order[i1]; j = order[j1]; k = order[k1]; l = order[l1]; isort4(&i, &j, &k, &l); A = D[i][j] + D[k][l]; B = D[i][k] + D[j][l]; C = D[i][l] + D[j][k]; if((A <= B) && (A <= C)) { DQ = (C - B) / 4; D[i][l] -= DQ; D[j][k] -= DQ; D[i][k] += DQ; D[j][l] += DQ; delta += fabs(C - B); } else if(B <= C) { DQ = (A - C) / 4; D[i][l] += DQ; D[j][k] += DQ; D[i][j] -= DQ; D[k][l] -= DQ; delta += fabs(A - C); } else { DQ = (B - A) / 4; D[i][k] -= DQ; D[j][l] -= DQ; D[i][j] += DQ; D[k][l] += DQ; delta += fabs(B - A); } } if(*verbose) Rprintf("change: %f\n", delta); if(delta < *tol) break; } for(i = 0; i < *n - 1; i++) for(j = i + 1; j < *n; j++) D[j][i] = D[i][j]; /* And now write results back. Could make this more efficient, of course ... */ for(j = 0; j < *n; j++) for(i = 0; i < *n; i++) *d++ = D[i][j]; } clue/src/init.c0000644000175100001440000000373613340544526013135 0ustar hornikusers#include #include #include #include "clue.h" static R_NativePrimitiveArgType solve_LSAP_t[3] = { REALSXP, INTSXP, INTSXP }; static R_NativePrimitiveArgType clue_dissimilarity_count_inversions_t[4] = { REALSXP, REALSXP, INTSXP, REALSXP }; static R_NativePrimitiveArgType deviation_from_ultrametricity_t[4] = { REALSXP, INTSXP, REALSXP, LGLSXP }; static R_NativePrimitiveArgType deviation_from_ultrametricity_gradient_t[3] = { REALSXP, INTSXP, REALSXP }; static R_NativePrimitiveArgType deviation_from_additivity_t[4] = { REALSXP, INTSXP, REALSXP, LGLSXP }; static R_NativePrimitiveArgType deviation_from_additivity_gradient_t[3] = { REALSXP, INTSXP, REALSXP }; static R_NativePrimitiveArgType ls_fit_ultrametric_by_iterative_reduction_t[7] = { REALSXP, INTSXP, INTSXP, INTSXP, INTSXP, REALSXP, LGLSXP }; static R_NativePrimitiveArgType ls_fit_ultrametric_by_iterative_projection_t[7] = { REALSXP, INTSXP, INTSXP, INTSXP, INTSXP, REALSXP, LGLSXP }; static R_NativePrimitiveArgType ls_fit_addtree_by_iterative_reduction_t[7] = { REALSXP, INTSXP, INTSXP, INTSXP, INTSXP, REALSXP, LGLSXP }; static R_NativePrimitiveArgType ls_fit_addtree_by_iterative_projection_t[7] = { REALSXP, INTSXP, INTSXP, INTSXP, INTSXP, REALSXP, LGLSXP }; #define CDEF(name) {#name, (DL_FUNC) &name, sizeof(name ## _t)/sizeof(name ## _t[0]), name ##_t} static const R_CMethodDef cMethods[] = { CDEF(solve_LSAP), CDEF(clue_dissimilarity_count_inversions), CDEF(deviation_from_ultrametricity), CDEF(deviation_from_ultrametricity_gradient), CDEF(deviation_from_additivity), CDEF(deviation_from_additivity_gradient), CDEF(ls_fit_ultrametric_by_iterative_reduction), CDEF(ls_fit_ultrametric_by_iterative_projection), CDEF(ls_fit_addtree_by_iterative_reduction), CDEF(ls_fit_addtree_by_iterative_projection), {NULL, NULL, 0} }; void R_init_clue(DllInfo *dll) { R_registerRoutines(dll, cMethods, NULL, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } clue/src/lsap.c0000644000175100001440000000033311304023137013103 0ustar hornikusers#include #include "assignment.h" #include "clue.h" void solve_LSAP(double *c, Sint *n, Sint *p) { AP *ap; ap = ap_create_problem(c, *n); ap_hungarian(ap); ap_assignment(ap, p); ap_free(ap); } clue/src/clue.c0000644000175100001440000000135211304023137013076 0ustar hornikusers#include #include #include "clue.h" double **clue_vector_to_square_matrix(double *x, Sint n) { double **data, *val; Sint i, j; data = (double **) R_alloc(n, sizeof(double)); for(i = 0; i < n; i++) { data[i] = (double *) R_alloc(n, sizeof(double)); val = x + i; for(j = 0; j < n; j++, val += n) data[i][j] = *val; } return(data); } static int clue_sign(double x) { if(x == 0) return(0); return((x > 0) ? 1 : -1); } void clue_dissimilarity_count_inversions(double *x, double *y, Sint *n, double *count) { Sint i, j; for(i = 0; i < *n; i++) for(j = 0; j < *n; j++) if((clue_sign(x[i] - x[j]) * clue_sign(y[i] - y[j])) < 0) (*count)++; } clue/NAMESPACE0000644000175100001440000002447214503541633012454 0ustar hornikusersuseDynLib("clue", .registration = TRUE, .fixes = "C_") import("stats") importFrom("graphics", "par", "plot") importFrom("cluster", "clara", "pam", "silhouette") export("cl_agreement", "cl_bag", "cl_boot", "cl_consensus", "cl_dissimilarity", "cl_ensemble", "as.cl_ensemble", "is.cl_ensemble", "cl_fuzziness", "cl_join", "cl_margin", "cl_meet", "cl_medoid", "cl_membership", "as.cl_membership", "cl_object_names", "cl_pam", "cl_pclust", "cl_predict", "cl_prototypes", "cl_tabulate", "cl_ultrametric", "as.cl_ultrametric", "ls_fit_addtree", "ls_fit_centroid", "ls_fit_ultrametric", "ls_fit_sum_of_ultrametrics", "ls_fit_ultrametric_target", "l1_fit_ultrametric", "l1_fit_ultrametric_target", "cl_validity", "n_of_objects", "n_of_classes", "cl_class_ids", "as.cl_class_ids", "cl_classes", "is.cl_partition", "as.cl_partition", "is.cl_hard_partition", "as.cl_hard_partition", "is.cl_soft_partition", "is.cl_dendrogram", "as.cl_dendrogram", "is.cl_hierarchy", "as.cl_hierarchy", "as.cl_addtree", "kmedoids", "pclust", "pclust_family", "pclust_object", "solve_LSAP", "sumt") ## S3 methods, sorted alphabetically. S3method("[", "cl_agreement") S3method("[", "cl_dissimilarity") S3method("[", "cl_ensemble") S3method("[", "cl_proximity") S3method("Complex", "cl_hierarchy") S3method("Complex", "cl_partition") S3method("Math", "cl_hierarchy") S3method("Math", "cl_partition") S3method("Ops", "cl_dendrogram") S3method("Ops", "cl_hierarchy") S3method("Ops", "cl_partition") S3method("Summary", "cl_hierarchy") S3method("Summary", "cl_partition") S3method("Summary", "cl_partition_ensemble") S3method("as.cl_addtree", "default") S3method("as.cl_addtree", "phylo") S3method("as.cl_membership", "default") S3method("as.cl_membership", "matrix") S3method("as.cl_ultrametric", "default") S3method("as.cl_ultrametric", "matrix") S3method("as.dendrogram", "cl_ultrametric") S3method("as.dist", "cl_dissimilarity") S3method("as.hclust", "cl_hierarchy") S3method("as.hclust", "cl_ultrametric") S3method("as.hclust", "mona") S3method("as.matrix", "cl_proximity") S3method("as.matrix", "hclust") S3method("c", "cl_ensemble") S3method("cl_class_ids", "Mclust") S3method("cl_class_ids", "Weka_clusterer") S3method("cl_class_ids", "bclust") S3method("cl_class_ids", "ccfkms") S3method("cl_class_ids", "cclust") S3method("cl_class_ids", "cl_class_ids") S3method("cl_class_ids", "cl_membership") S3method("cl_class_ids", "cl_pam") S3method("cl_class_ids", "cl_partition") S3method("cl_class_ids", "cl_partition_by_class_ids") S3method("cl_class_ids", "cshell") S3method("cl_class_ids", "default") S3method("cl_class_ids", "fclust") S3method("cl_class_ids", "flexmix") S3method("cl_class_ids", "kcca") S3method("cl_class_ids", "kmeans") S3method("cl_class_ids", "kmedoids") S3method("cl_class_ids", "movMF") S3method("cl_class_ids", "partition") S3method("cl_class_ids", "pclust") S3method("cl_class_ids", "relation") S3method("cl_class_ids", "rock") S3method("cl_class_ids", "specc") S3method("cl_classes", "cl_hierarchy") S3method("cl_classes", "cl_partition") S3method("cl_classes", "cl_ultrametric") S3method("cl_classes", "default") S3method("cl_membership", "Mclust") S3method("cl_membership", "bclust") S3method("cl_membership", "cclust") S3method("cl_membership", "cl_membership") S3method("cl_membership", "cl_partition") S3method("cl_membership", "cshell") S3method("cl_membership", "default") S3method("cl_membership", "fanny") S3method("cl_membership", "fclust") S3method("cl_membership", "flexmix") S3method("cl_membership", "kmeans") S3method("cl_membership", "movMF") S3method("cl_membership", "partition") S3method("cl_membership", "pclust") S3method("cl_object_names", "cl_ensemble") S3method("cl_object_names", "cl_hierarchy") S3method("cl_object_names", "cl_membership") S3method("cl_object_names", "cl_partition") S3method("cl_object_names", "cl_ultrametric") S3method("cl_object_names", "default") S3method("cl_object_names", "dist") S3method("cl_object_names", "hclust") S3method("cl_object_names", "mona") S3method("cl_object_names", "phylo") S3method("cl_object_names", "twins") S3method("cl_predict", "Mclust") S3method("cl_predict", "Weka_clusterer") S3method("cl_predict", "bclust") S3method("cl_predict", "cclust") S3method("cl_predict", "cl_partition") S3method("cl_predict", "ccfkms") S3method("cl_predict", "clara") S3method("cl_predict", "cshell") S3method("cl_predict", "default") S3method("cl_predict", "fanny") S3method("cl_predict", "fclust") S3method("cl_predict", "flexmix") S3method("cl_predict", "kcca") S3method("cl_predict", "kmeans") S3method("cl_predict", "movMF") S3method("cl_predict", "pam") S3method("cl_predict", "pclust") S3method("cl_predict", "rock") S3method("cl_prototypes", "Mclust") S3method("cl_prototypes", "bclust") S3method("cl_prototypes", "ccfkms") S3method("cl_prototypes", "cclust") S3method("cl_prototypes", "cl_pam") S3method("cl_prototypes", "cl_partition") S3method("cl_prototypes", "clara") S3method("cl_prototypes", "cshell") S3method("cl_prototypes", "fclust") S3method("cl_prototypes", "kcca") S3method("cl_prototypes", "kmeans") S3method("cl_prototypes", "pam") S3method("cl_prototypes", "pclust") S3method("cl_prototypes", "specc") S3method("cl_validity", "agnes") S3method("cl_validity", "default") S3method("cl_validity", "diana") S3method("cl_validity", "cl_partition") S3method("cl_validity", "pclust") S3method("cophenetic", "cl_ultrametric") S3method("cophenetic", "mona") S3method("cut", "cl_dendrogram") S3method("is.cl_dendrogram", "cl_dendrogram") S3method("is.cl_dendrogram", "default") S3method("is.cl_dendrogram", "hclust") S3method("is.cl_dendrogram", "mona") S3method("is.cl_dendrogram", "phylo") S3method("is.cl_dendrogram", "twins") S3method("is.cl_hard_partition", "Mclust") S3method("is.cl_hard_partition", "Weka_clusterer") S3method("is.cl_hard_partition", "bclust") S3method("is.cl_hard_partition", "ccfkms") S3method("is.cl_hard_partition", "cclust") S3method("is.cl_hard_partition", "cl_hard_partition") S3method("is.cl_hard_partition", "cl_partition") S3method("is.cl_hard_partition", "cshell") S3method("is.cl_hard_partition", "default") S3method("is.cl_hard_partition", "fanny") S3method("is.cl_hard_partition", "fclust") S3method("is.cl_hard_partition", "flexmix") S3method("is.cl_hard_partition", "kcca") S3method("is.cl_hard_partition", "kmeans") S3method("is.cl_hard_partition", "kmedoids") S3method("is.cl_hard_partition", "movMF") S3method("is.cl_hard_partition", "partition") S3method("is.cl_hard_partition", "pclust") S3method("is.cl_hard_partition", "rock") S3method("is.cl_hard_partition", "specc") S3method("is.cl_hierarchy", "cl_hierarchy") S3method("is.cl_hierarchy", "default") S3method("is.cl_hierarchy", "hclust") S3method("is.cl_hierarchy", "mona") S3method("is.cl_hierarchy", "phylo") S3method("is.cl_hierarchy", "twins") S3method("is.cl_partition", "Mclust") S3method("is.cl_partition", "Weka_clusterer") S3method("is.cl_partition", "bclust") S3method("is.cl_partition", "ccfkms") S3method("is.cl_partition", "cclust") S3method("is.cl_partition", "cl_partition") S3method("is.cl_partition", "cshell") S3method("is.cl_partition", "default") S3method("is.cl_partition", "fclust") S3method("is.cl_partition", "flexmix") S3method("is.cl_partition", "kcca") S3method("is.cl_partition", "kmeans") S3method("is.cl_partition", "kmedoids") S3method("is.cl_partition", "movMF") S3method("is.cl_partition", "partition") S3method("is.cl_partition", "pclust") S3method("is.cl_partition", "rock") S3method("is.cl_partition", "specc") S3method("n_of_classes", "Mclust") S3method("n_of_classes", "bclust") S3method("n_of_classes", "cclust") S3method("n_of_classes", "cl_membership") S3method("n_of_classes", "cl_partition") S3method("n_of_classes", "cshell") S3method("n_of_classes", "default") S3method("n_of_classes", "fanny") S3method("n_of_classes", "fclust") S3method("n_of_classes", "kmeans") S3method("n_of_classes", "partition") S3method("n_of_classes", "pclust") S3method("n_of_objects", "Mclust") S3method("n_of_objects", "bclust") S3method("n_of_objects", "cclust") S3method("n_of_objects", "cl_ensemble") S3method("n_of_objects", "cl_hierarchy") S3method("n_of_objects", "cl_membership") S3method("n_of_objects", "cl_partition") S3method("n_of_objects", "cl_ultrametric") S3method("n_of_objects", "cshell") S3method("n_of_objects", "default") S3method("n_of_objects", "dist") S3method("n_of_objects", "fclust") S3method("n_of_objects", "hclust") S3method("n_of_objects", "kmeans") S3method("n_of_objects", "mona") S3method("n_of_objects", "partition") S3method("n_of_objects", "pclust") S3method("n_of_objects", "phylo") S3method("n_of_objects", "twins") S3method("names", "cl_proximity") S3method("plot", "cl_addtree") S3method("plot", "cl_ensemble") S3method("plot", "cl_dendrogram") S3method("plot", "cl_ultrametric") S3method("print", "cl_class_ids") S3method("print", "cl_classes_of_partition_of_objects") S3method("print", "cl_classes_of_hierarchy_of_objects") S3method("print", "cl_cross_proximity") S3method("print", "cl_dendrogram") S3method("print", "cl_dendrogram_ensemble") S3method("print", "cl_ensemble") S3method("print", "cl_fuzziness") S3method("print", "cl_hierarchy") S3method("print", "cl_hierarchy_ensemble") S3method("print", "cl_membership") S3method("print", "cl_pam") S3method("print", "cl_partition") S3method("print", "cl_partition_by_class_ids") S3method("print", "cl_partition_by_memberships") S3method("print", "cl_partition_ensemble") S3method("print", "cl_proximity") S3method("print", "cl_pclust") S3method("print", "cl_validity") S3method("print", "kmedoids") S3method("print", "pclust") S3method("print", "solve_LSAP") S3method("rep", "cl_ensemble") S3method("silhouette", "cl_partition") S3method("silhouette", "cl_pclust") S3method("unique", "cl_ensemble") S3method(".maybe_is_proper_soft_partition", "Mclust") S3method(".maybe_is_proper_soft_partition", "cl_partition") S3method(".maybe_is_proper_soft_partition", "cshell") S3method(".maybe_is_proper_soft_partition", "default") S3method(".maybe_is_proper_soft_partition", "fanny") S3method(".maybe_is_proper_soft_partition", "fclust") S3method(".maybe_is_proper_soft_partition", "flexmix") S3method(".maybe_is_proper_soft_partition", "pclust") clue/inst/0000755000175100001440000000000015145302607012200 5ustar hornikusersclue/inst/CITATION0000644000175100001440000000103314366206623013337 0ustar hornikusersbibentry("Manual", other = unlist(citation(auto = meta), recursive = FALSE)) bibentry("Article", title = "A {CLUE} for {CLUster Ensembles}", author = person("Kurt", "Hornik", email = "Kurt.Hornik@R-project.org", comment = c(ORCID = "0000-0003-4198-9911")), year = 2005, journal = "Journal of Statistical Software", volume = 14, number = 12, month = "September", doi = "10.18637/jss.v014.i12" ) clue/inst/po/0000755000175100001440000000000012213262407012612 5ustar hornikusersclue/inst/po/en@quot/0000755000175100001440000000000012213262407014225 5ustar hornikusersclue/inst/po/en@quot/LC_MESSAGES/0000755000175100001440000000000012213262407016012 5ustar hornikusersclue/inst/po/en@quot/LC_MESSAGES/R-clue.mo0000644000175100001440000002064013143661614017506 0ustar hornikusersSqL/AHa?X ^ j5v2,X We V : 5O ) , / ! ;. 8j  % + )5 (_ 2 ) # * *4 _ %| )  & / I T1_*+19"X&{& *Eb%~  94.+Zy"07% ]+j(> 1,F's6/H"k?X  +572m,XW&V~:9-J0x7%C8K ")+)(H2q)#**H%e)&  2 =1H.z+1""*&M&t& #>[%w  94.$Sr&0 7" Z +g  , B !1!,K!'x!4I%R52'M+ D0.N O3,L"AS1)$=E*B#! J;-? > KQ8 PC<G(9:F&7@/H6A hard partition of %d objects into %d classes.A hard partition of %d objects.A hard partition of a cluster ensemble with %d elements into %d classes.A partition of %d objects.A soft partition (degree m = %f) of %d objects into %d classes.A soft partition (degree m = %f) of a cluster ensemble with %d elements into %d classes.AOG run: %dAOS run: %dAll clusterings must have the same number of objects.All elements must have the same number of objects.All given orders must be valid permutations.An ensemble of %d dendrogram of %d objects.An ensemble of %d dendrograms of %d objects.An ensemble of %d hierarchy of %d objects.An ensemble of %d hierarchies of %d objects.An ensemble of %d partition of %d objects.An ensemble of %d partitions of %d objects.An ensemble with %d element.An ensemble with %d elements.An object of virtual class '%s', with representation:Argument 'weights' has negative elements.Argument 'weights' has no positive elements.Argument 'weights' must be compatible with 'x'.Argument 'x' must be a partition.Arguments 'x' and 'y' must have the same number of objects.Can only determine classes of partitions or hierarchies.Can only handle hard partitions.Cannot coerce to 'cl_addtree'.Cannot coerce to 'cl_hard_partition'.Cannot compute consensus of empty ensemble.Cannot compute join of given clusterings.Cannot compute medoid of empty ensemble.Cannot compute medoid partition of empty ensemble.Cannot compute meet of given clusterings.Cannot compute prototype distances.Cannot determine how to modify prototypes.Cannot determine how to subset prototypes.Cannot determine prototypes.Cannot extract object dissimilaritiesCannot infer class ids from given object.Cannot make new predictions.Cannot mix partitions and hierarchies.Change: %gChange: u: %g L: %gClass ids must be atomic.Class ids:Criterion:Dendrograms must have the same number of objects.Generic '%s' not defined for "%s" objects.Given ensemble contains no dissimilarities.Hierarchies must have the same number of objects.Invalid agreement method '%s'.Invalid consensus method '%s'.Invalid dissimilarity method '%s'.Invalid function to modify prototypes.Invalid function to subset prototypes.Iteration: %dIteration: %d *** value: %gIteration: %d Rho: %g P: %gIteration: 0 *** value: %gIteration: 0 Rho: %g P: %gIterative projection run: %dIterative reduction run: %dJoin of given n-trees does not exist.Medoid ids:Minimum: %gNo information on dissimilarity in consensus method used.No information on exponent in consensus method used.Non-identical weights currently not supported.Not a valid membership matrix.Not a valid ultrametric.Outer iteration: %dOverall change: u: %g L: %gParameter 'p' must be in [1/2, 1].Partitions must have the same number of objects.Pclust run: %dPlotting not available for elements %s of the ensemble.SUMT run: %dStandardization is currently not supported.Term: %dUnary '%s' not defined for "%s" objects.Value '%s' is not a valid abbreviation for a fuzziness method.Wrong class.k cannot be less than the number of classes in x.x must be a matrix with nonnegative entries.x must not have more rows than columns.Project-Id-Version: clue 0.3-54 POT-Creation-Date: 2017-08-07 11:31 PO-Revision-Date: 2017-08-07 11:31 Last-Translator: Automatically generated Language-Team: none MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: en Plural-Forms: nplurals=2; plural=(n != 1); A hard partition of %d objects into %d classes.A hard partition of %d objects.A hard partition of a cluster ensemble with %d elements into %d classes.A partition of %d objects.A soft partition (degree m = %f) of %d objects into %d classes.A soft partition (degree m = %f) of a cluster ensemble with %d elements into %d classes.AOG run: %dAOS run: %dAll clusterings must have the same number of objects.All elements must have the same number of objects.All given orders must be valid permutations.An ensemble of %d dendrogram of %d objects.An ensemble of %d dendrograms of %d objects.An ensemble of %d hierarchy of %d objects.An ensemble of %d hierarchies of %d objects.An ensemble of %d partition of %d objects.An ensemble of %d partitions of %d objects.An ensemble with %d element.An ensemble with %d elements.An object of virtual class ‘%s’, with representation:Argument ‘weights’ has negative elements.Argument ‘weights’ has no positive elements.Argument ‘weights’ must be compatible with ‘x’.Argument ‘x’ must be a partition.Arguments ‘x’ and ‘y’ must have the same number of objects.Can only determine classes of partitions or hierarchies.Can only handle hard partitions.Cannot coerce to ‘cl_addtree’.Cannot coerce to ‘cl_hard_partition’.Cannot compute consensus of empty ensemble.Cannot compute join of given clusterings.Cannot compute medoid of empty ensemble.Cannot compute medoid partition of empty ensemble.Cannot compute meet of given clusterings.Cannot compute prototype distances.Cannot determine how to modify prototypes.Cannot determine how to subset prototypes.Cannot determine prototypes.Cannot extract object dissimilaritiesCannot infer class ids from given object.Cannot make new predictions.Cannot mix partitions and hierarchies.Change: %gChange: u: %g L: %gClass ids must be atomic.Class ids:Criterion:Dendrograms must have the same number of objects.Generic ‘%s’ not defined for "%s" objects.Given ensemble contains no dissimilarities.Hierarchies must have the same number of objects.Invalid agreement method ‘%s’.Invalid consensus method ‘%s’.Invalid dissimilarity method ‘%s’.Invalid function to modify prototypes.Invalid function to subset prototypes.Iteration: %dIteration: %d *** value: %gIteration: %d Rho: %g P: %gIteration: 0 *** value: %gIteration: 0 Rho: %g P: %gIterative projection run: %dIterative reduction run: %dJoin of given n-trees does not exist.Medoid ids:Minimum: %gNo information on dissimilarity in consensus method used.No information on exponent in consensus method used.Non-identical weights currently not supported.Not a valid membership matrix.Not a valid ultrametric.Outer iteration: %dOverall change: u: %g L: %gParameter ‘p’ must be in [1/2, 1].Partitions must have the same number of objects.Pclust run: %dPlotting not available for elements %s of the ensemble.SUMT run: %dStandardization is currently not supported.Term: %dUnary ‘%s’ not defined for "%s" objects.Value ‘%s’ is not a valid abbreviation for a fuzziness method.Wrong class.k cannot be less than the number of classes in x.x must be a matrix with nonnegative entries.x must not have more rows than columns.clue/inst/REFERENCES.R0000644000175100001440000006426515144360505013761 0ustar hornikusersbibentry(bibtype = "Book", key = "Arabie+Carroll+DeSarbo:1987", author = c(person(given = "Phipps", family = "Arabie"), person(given = c("J.", "Douglas"), family = "Carroll"), person(given = c("Wayne", "S."), family = "DeSarbo")), title = "Three-way Scaling and Clustering", publisher = "Sage Publications", year = "1987", series = "Quantitative Applications in the Social Sciences", address = "Newbury Park, CA", doi = "10.4135/9781412986359") bibentry(bibtype = "Book", key = "Barthélemy+Guénoche:1991", author = c(person(given = "Jean-Pierre", family = "Barthélemy"), person(given = "Alain", family = "Guénoche")), title = "Trees and Proximity Representations", publisher = "Wiley", year = "1991", series = "Wiley Series in Discrete Mathematics and Optimization", address = "Chicester", isbn = "978-0471922636") bibentry(bibtype = "Article", key = "Barthélemy+Leclerc+Monjardet:1986", title = "On the Use of Ordered Sets in Problems of Comparison and Consensus of Classifications", volume = "3", .issn = "1432-1343", doi = "10.1007/bf01894188", number = "2", journal = "Journal of Classification", publisher = "Springer Science and Business Media LLC", author = c(person(given = "Jean-Pierre", family = "Barthélemy"), person(given = "Bruno", family = "Leclerc"), person(given = "Bernard", family = "Monjardet")), year = "1986", month = "sep", pages = "187--224") bibentry(bibtype = "Book", key = "Bezdek:1981", author = person(given = c("James", "C."), family = "Bezdek"), title = "Pattern Recognition with Fuzzy Objective Function Algorithms", publisher = "Plenum", year = "1981", address = "New York", doi = "10.1007/978-1-4757-0450-1") bibentry(bibtype = "InCollection", key = "Boorman+Arabie:1972", author = c(person(given = c("Scott", "A."), family = "Boorman"), person(given = "Phipps", family = "Arabie")), title = "Structural Measures and the Method of Sorting", booktitle = "Multidimensional Scaling: Theory and Applications in the Behavioral Sciences, 1: Theory", publisher = "Seminar Press", year = "1972", editor = c(person(given = c("R.", "N."), family = "Shepard"), person(given = c("A.", "K."), family = "Romney"), person(given = c("S.", "B."), family = "Nerlove")), pages = "225--249", address = "New York") bibentry(bibtype = "Article", key = "Boorman+Olivier:1973", title = "Metrics on Spaces of Finite Trees", volume = "10", .issn = "0022-2496", doi = "10.1016/0022-2496(73)90003-5", number = "1", journal = "Journal of Mathematical Psychology", publisher = "Elsevier BV", author = c(person(given = c("Scott", "A."), family = "Boorman"), person(given = c("Donald", "C."), family = "Olivier")), year = "1973", month = "feb", pages = "26--59") bibentry(bibtype = "InCollection", key = "Carroll+Pruzansky:1980", author = c(person(given = c("J.", "D."), family = "Carroll"), person(given = "S.", family = "Pruzansky")), title = "Discrete and Hybrid Scaling Models", booktitle = "Similarity and Choice", publisher = "Huber", year = "1980", editor = c(person(given = c("E.", "D."), family = "Lantermann"), person(given = "H.", family = "Feger")), address = "Bern, Switzerland") bibentry(bibtype = "Article", key = "Charon+Denoeud+Guénoche:2006", title = "Maximum Transfer Distance Between Partitions", volume = "23", .issn = "1432-1343", doi = "10.1007/s00357-006-0006-2", number = "1", journal = "Journal of Classification", publisher = "Springer Science and Business Media LLC", author = c(person(given = "Irene", family = "Charon"), person(given = "Lucile", family = "Denoeud"), person(given = "Alain", family = "Guénoche"), person(given = "Olivier", family = "Hudry")), year = "2006", month = "jun", pages = "103--121") bibentry(bibtype = "Article", key = "De_Soete:1983", title = "A Least Squares Algorithm for Fitting Additive Trees to Proximity Data", volume = "48", .issn = "1860-0980", doi = "10.1007/bf02293884", number = "4", journal = "Psychometrika", publisher = "Cambridge University Press (CUP)", author = person(given = "Geert", family = "De Soete"), year = "1983", month = "dec", pages = "621--626") bibentry(bibtype = "Article", key = "Day:1981", title = "The Complexity of Computing Metric Distances Between Partitions", volume = "1", .issn = "0165-4896", doi = "10.1016/0165-4896(81)90042-1", number = "3", journal = "Mathematical Social Sciences", publisher = "Elsevier BV", author = person(given = c("William", "H.", "E."), family = "Day"), year = "1981", month = "may", pages = "269--287") bibentry(bibtype = "Article", key = "De_Soete:1984a", title = "A Least Squares Algorithm for Fitting an Ultrametric Tree to a Dissimilarity Matrix", volume = "2", .issn = "0167-8655", doi = "10.1016/0167-8655(84)90036-9", number = "3", journal = "Pattern Recognition Letters", publisher = "Elsevier BV", author = person(given = "Geert", family = "De Soete"), year = "1984", month = "mar", pages = "133--137") bibentry(bibtype = "Article", key = "De_Soete:1984b", title = "Ultrametric Tree Representations of Incomplete Dissimilarity Data", volume = "1", .issn = "1432-1343", doi = "10.1007/bf01890124", number = "1", journal = "Journal of Classification", publisher = "Springer Science and Business Media LLC", author = person(given = "Geert", family = "De Soete"), year = "1984", month = "dec", pages = "235--242") bibentry(bibtype = "Article", key = "Dimitriadou+Weingessel+Hornik:2002", title = "A Combination Scheme for Fuzzy Clustering", volume = "16", .issn = "1793-6381", doi = "10.1142/s0218001402002052", number = "07", journal = "International Journal of Pattern Recognition and Artificial Intelligence", publisher = "World Scientific Pub Co Pte Lt", author = c(person(given = "Evgenia", family = "Dimitriadou"), person(given = "Andreas", family = "Weingessel"), person(given = "Kurt", family = "Hornik")), year = "2002", month = "nov", pages = "901--912") bibentry(bibtype = "Article", key = "Dudoit+Fridlyand:2003", title = "Bagging to Improve the Accuracy of a Clustering Procedure", volume = "19", .issn = "1367-4803", doi = "10.1093/bioinformatics/btg038", number = "9", journal = "Bioinformatics", publisher = "Oxford University Press (OUP)", author = c(person(given = "Sandrine", family = "Dudoit"), person(given = "Jane", family = "Fridlyand")), year = "2003", month = "jun", pages = "1090--1099") bibentry(bibtype = "Book", key = "Fiacco+McCormick:1968", author = c(person(given = c("Anthony", "V."), family = "Fiacco"), person(given = c("Garth", "P."), family = "McCormick")), title = "Nonlinear Programming: Sequential Unconstrained Minimization Techniques", publisher = "John Wiley & Sons", year = "1968", address = "New York") bibentry(bibtype = "Article", key = "Fowlkes+Mallows:1983", title = "A Method for Comparing Two Hierarchical Clusterings", volume = "78", .issn = "1537-274X", doi = "10.1080/01621459.1983.10478008", number = "383", journal = "Journal of the American Statistical Association", publisher = "Informa UK Limited", author = c(person(given = c("E.", "B."), family = "Fowlkes"), person(given = c("C.", "L."), family = "Mallows")), year = "1983", month = "sep", pages = "553--569") bibentry(bibtype = "Article", key = "Gaul+Schader:1988", title = "Clusterwise Aggregation of Relations", volume = "4", .issn = "1099-0747", doi = "10.1002/asm.3150040406", number = "4", journal = "Applied Stochastic Models and Data Analysis", publisher = "Wiley", author = c(person(given = "Wolfgang", family = "Gaul"), person(given = "Martin", family = "Schader")), year = "1988", month = "dec", pages = "273--282") bibentry(bibtype = "Article", key = "Gusfield:2002", title = "Partition-Distance: A Problem and Class of Perfect Graphs Arising in Clustering", volume = "82", .issn = "0020-0190", doi = "10.1016/s0020-0190(01)00263-0", number = "3", journal = "Information Processing Letters", publisher = "Elsevier BV", author = person(given = "Dan", family = "Gusfield"), year = "2002", month = "may", pages = "159--164") bibentry(bibtype = "Article", key = "Gordon+Vichi:1998", title = "Partitions of Partitions", volume = "15", .issn = "1432-1343", doi = "10.1007/s003579900034", number = "2", journal = "Journal of Classification", publisher = "Springer Science and Business Media LLC", author = c(person(given = c("Allan", "Drummond"), family = "Gordon"), person(given = "Maurizio", family = "Vichi")), year = "1998", month = "feb", pages = "265--285") bibentry(bibtype = "Article", key = "Gordon+Vichi:2001", title = "Fuzzy Partition Models for Fitting a Set of Partitions", volume = "66", .issn = "1860-0980", doi = "10.1007/bf02294837", number = "2", journal = "Psychometrika", publisher = "Cambridge University Press (CUP)", author = c(person(given = c("Allan", "Drummond"), family = "Gordon"), person(given = "Maurizio", family = "Vichi")), year = "2001", month = "jun", pages = "229--247") bibentry(bibtype = "Article", key = "Hubert+Arabie:1985", title = "Comparing Partitions", volume = "2", .issn = "1432-1343", doi = "10.1007/bf01908075", number = "1", journal = "Journal of Classification", publisher = "Springer Science and Business Media LLC", author = c(person(given = "Lawrence", family = "Hubert"), person(given = "Phipps", family = "Arabie")), year = "1985", month = "dec", pages = "193--218") bibentry(bibtype = "Article", key = "Hubert+Arabie:1995", title = "Iterative Projection Strategies for the Least-Squares Fitting of Tree Structures to Proximity Data", volume = "48", .issn = "2044-8317", doi = "10.1111/j.2044-8317.1995.tb01065.x", number = "2", journal = "British Journal of Mathematical and Statistical Psychology", publisher = "Wiley", author = c(person(given = "Lawrence", family = "Hubert"), person(given = "Phipps", family = "Arabie")), year = "1995", month = "nov", pages = "281--317") bibentry(bibtype = "Book", key = "Hubert+Arabie+Meulman:2006", author = c(person(given = "Lawrence", family = "Hubert"), person(given = "Phipps", family = "Arabie"), person(given = "Jacqueline", family = "Meulman")), title = "The Structural Representation of Proximity Matrices with {MATLAB}", publisher = "SIAM", year = "2006", series = "ASA-SIAM Series on Statistics and Applied Mathematics", address = "Philadelphia, PA", doi = "10.1137/1.9780898718355") bibentry(bibtype = "Book", key = "Jardine+Sibson:1971", author = c(person(given = "Nicholas", family = "Jardine"), person(given = "Robin", family = "Sibson")), title = "Mathematical Taxonomy", publisher = "Wiley", year = "1971", address = "London") bibentry(bibtype = "Article", key = "Katz+Powell:1953", title = "A Proposed Index of the Conformity of One Sociometric Measurement to Another", volume = "18", .issn = "1860-0980", doi = "10.1007/bf02289063", number = "3", journal = "Psychometrika", publisher = "Cambridge University Press (CUP)", author = c(person(given = "Leo", family = "Katz"), person(given = c("James", "H."), family = "Powell")), year = "1953", month = "sep", pages = "249--256") bibentry(bibtype = "Book", key = "Kaufman+Rousseeuw:1990", author = c(person(given = "Leonard", family = "Kaufman"), person(given = c("Peter", "J."), family = "Rousseeuw")), title = "Finding Groups in Data: An Introduction to Cluster Analysis", publisher = "Wiley", year = "1990", address = "New York", doi = "10.1002/9780470316801") bibentry(bibtype = "Article", key = "Křivánek+Morávek:1986", title = "{NP}-hard Problems in Hierarchical-Tree Clustering", volume = "23", .issn = "1432-0525", doi = "10.1007/bf00289116", number = "3", journal = "Acta Informatica", publisher = "Springer Science and Business Media LLC", author = c(person(given = "Mirko", family = "Křivánek"), person(given = "Jaroslav", family = "Morávek")), year = "1986", month = "jun", pages = "311--323") bibentry(bibtype = "Misc", key = "Leisch:1999", doi = "10.57938/9B129F95-B53B-44CE-A129-5B7A1168D832", author = person(given = "Friedrich", family = "Leisch"), language = "en", title = "Bagged Clustering", publisher = "Vienna University of Economics and Business", year = "1999") bibentry(bibtype = "Article", key = "Margush+McMorris:1981", title = "Consensus $n$-Trees", volume = "43", .issn = "1522-9602", doi = "10.1007/bf02459446", number = "2", journal = "Bulletin of Mathematical Biology", publisher = "Springer Science and Business Media LLC", author = c(person(given = "T.", family = "Margush"), person(given = c("F.", "R."), family = "McMorris")), year = "1981", month = "mar", pages = "239--244") bibentry(bibtype = "InProceedings", key = "Meila:2003", author = person(given = "Marina", family = "Meilă"), editor = c(person(given = "Bernhard", family = "Schölkopf"), person(given = c("Manfred", "K."), family = "Warmuth")), title = "Comparing Clusterings by the Variation of Information", booktitle = "Learning Theory and Kernel Machines", year = "2003", publisher = "Springer Berlin Heidelberg", address = "Berlin, Heidelberg", pages = "173--187", isbn = "978-3-540-45167-9", doi = "10.1007/978-3-540-45167-9_14") bibentry(bibtype = "Article", key = "Mérigot+Durbec+Gaertner:2010", title = "On Goodness-of-Fit Measure for Dendrogram‐Based Analyses", volume = "91", .issn = "1939-9170", doi = "10.1890/09-1387.1", number = "6", journal = "Ecology", publisher = "Wiley", author = c(person(given = "Bastien", family = "Mérigot"), person(given = "Jean-Pierre", family = "Durbec"), person(given = "Jean-Claude", family = "Gaertner")), year = "2010", month = "jun", pages = "1850--1859") bibentry(bibtype = "Article", key = "Miller+Nicely:1955", title = "An Analysis of Perceptual Confusions Among Some English Consonants", volume = "27", .issn = "1520-8524", doi = "10.1121/1.1907526", number = "2", journal = "The Journal of the Acoustical Society of America", publisher = "Acoustical Society of America (ASA)", author = c(person(given = c("George", "A."), family = "Miller"), person(given = c("Patricia", "E."), family = "Nicely")), year = "1955", month = "mar", pages = "338--352") bibentry(bibtype = "Book", key = "Papadimitriou+Steiglitz:1982", author = c(person(given = c("Christos", "H."), family = "Papadimitriou"), person(given = "Kenneth", family = "Steiglitz")), title = "Combinatorial Optimization: Algorithms and Complexity", publisher = "Prentice Hall", year = "1982", address = "Englewood Cliffs") bibentry(bibtype = "Article", key = "Rajski:1961", title = "A Metric Space of Discrete Probability Distributions", volume = "4", .issn = "0019-9958", doi = "10.1016/s0019-9958(61)80055-7", number = "4", journal = "Information and Control", publisher = "Elsevier BV", author = person(given = "C.", family = "Rajski"), year = "1961", month = "dec", pages = "371--377") bibentry(bibtype = "Article", key = "Rand:1971", title = "Objective Criteria for the Evaluation of Clustering Methods", volume = "66", .issn = "1537-274X", doi = "10.1080/01621459.1971.10482356", number = "336", journal = "Journal of the American Statistical Association", publisher = "Informa UK Limited", author = person(given = c("William", "M."), family = "Rand"), year = "1971", month = "dec", pages = "846--850") bibentry(bibtype = "InCollection", key = "Rosenberg:1982", author = person(given = "Seymour", family = "Rosenberg"), title = "The Method of Sorting in Multivariate Research with Applications Selected from Cognitive Psychology and Person Perception", booktitle = "Multivariate Applications in the Social Sciences", publisher = "Erlbaum", year = "1982", editor = c(person(given = "N.", family = "Hirschberg"), person(given = c("L.", "G."), family = "Humphreys")), pages = "117--142", address = "Hillsdale, NJ", doi = "10.4324/9781315802831") bibentry(bibtype = "Article", key = "Rosenberg+Kim:1975", title = "The Method of Sorting as a Data-Gathering Procedure in Multivariate Research", volume = "10", .issn = "1532-7906", doi = "10.1207/s15327906mbr1004_7", number = "4", journal = "Multivariate Behavioral Research", publisher = "Informa UK Limited", author = c(person(given = "Seymour", family = "Rosenberg"), person(given = c("Moonja", "Park"), family = "Kim")), year = "1975", month = "oct", pages = "489--502") bibentry(bibtype = "InCollection", key = "Roux:1988", title = "Techniques of Approximation for Building Two Tree Structures", editor = c(person(given = "Chikio", family = "Hayashi"), person(given = "Michel", family = "Jambu"), person(given = "Edwin", family = "Diday"), person(given = "Noboru", family = "Ohsumi")), booktitle = "Recent Developments in Clustering and Data Analysis", publisher = "Academic Press", pages = "151--170", year = "1988", isbn = "978-0-12-215485-0", doi = "10.1016/B978-0-12-215485-0.50017-X", author = person(given = "M.", family = "Roux")) bibentry(bibtype = "Article", key = "Rubin:1967", title = "Optimal Classification into Groups: An Approach for Solving The Taxonomy Problem", volume = "15", .issn = "0022-5193", doi = "10.1016/0022-5193(67)90046-x", number = "1", journal = "Journal of Theoretical Biology", publisher = "Elsevier BV", author = person(given = "Jerrold", family = "Rubin"), year = "1967", month = "apr", pages = "103--144") bibentry(bibtype = "Article", key = "Sato+Sato:1994", title = "On a Multicriteria Fuzzy Clustering Method for 3-Way Data", volume = "02", .issn = "1793-6411", doi = "10.1142/s0218488594000122", number = "02", journal = "International Journal of Uncertainty, Fuzziness and Knowledge-Based Systems", publisher = "World Scientific Pub Co Pte Lt", author = c(person(given = "Mika", family = "Sato"), person(given = "Yoshiharu", family = "Sato")), year = "1994", month = "jun", pages = "127--142") bibentry(bibtype = "Article", key = "Smith:2001", author = person(given = c("Thomas", "J."), family = "Smith"), title = "Constructing Ultrametric and Additive Trees Based on the $L_1$ Norm", journal = "Journal of Classification", year = "2001", volume = "18", number = "2", pages = "185--207", doi = "10.1007/s00357-001-0015-0") bibentry(bibtype = "Article", key = "Strehl+Ghosh:2002", author = c(person(given = "Alexander", family = "Strehl"), person(given = "Joydeep", family = "Ghosh")), title = "Cluster Ensembles --- A Knowledge Reuse Framework for Combining Multiple Partition", journal = "Journal of Machine Learning Research", year = "2002", volume = "3", pages = "583--617", url = "https://jmlr.org/papers/v3/strehl02a.html") bibentry(bibtype = "Article", key = "Tibshirani+Walther:2005", title = "Cluster Validation by Prediction Strength", volume = "14", .issn = "1537-2715", doi = "10.1198/106186005x59243", number = "3", journal = "Journal of Computational and Graphical Statistics", publisher = "Informa UK Limited", author = c(person(given = "Robert", family = "Tibshirani"), person(given = "Guenther", family = "Walther")), year = "2005", month = "sep", pages = "511--528") bibentry(bibtype = "InProceedings", key = "Zhou+Li+Zha:2005", series = "ICML ’05", title = "A New {Mallows} Distance Based Metric for Comparing Clusterings", doi = "10.1145/1102351.1102481", booktitle = "Proceedings of the 22nd international conference on Machine learning -- ICML ’05", publisher = "ACM Press", author = c(person(given = "Ding", family = "Zhou"), person(given = "Jia", family = "Li"), person(given = "Hongyuan", family = "Zha")), year = "2005", pages = "1028--1035", collection = "ICML ’05") clue/inst/doc/0000755000175100001440000000000015145302607012745 5ustar hornikusersclue/inst/doc/clue.Rnw0000644000175100001440000016521615145302547014403 0ustar hornikusers\documentclass[fleqn]{article} \usepackage[round,longnamesfirst]{natbib} \usepackage{graphicx,keyval,hyperref,doi} \newcommand\argmin{\mathop{\mathrm{arg min}}} \newcommand\trace{\mathop{\mathrm{tr}}} \newcommand\R{{\mathbb{R}}} \newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}} \newcommand{\sQuote}[1]{`{#1}'} \newcommand{\dQuote}[1]{``{#1}''} \let\code=\texttt \newcommand{\file}[1]{\sQuote{\textsf{#1}}} \newcommand{\class}[1]{\code{"#1"}} \SweaveOpts{strip.white=true} \AtBeginDocument{\setkeys{Gin}{width=0.6\textwidth}} \date{2007-06-28} \title{A CLUE for CLUster Ensembles} \author{Kurt Hornik} %% \VignetteIndexEntry{CLUster Ensembles} \sloppy{} \begin{document} \maketitle \begin{abstract} Cluster ensembles are collections of individual solutions to a given clustering problem which are useful or necessary to consider in a wide range of applications. The R package~\pkg{clue} provides an extensible computational environment for creating and analyzing cluster ensembles, with basic data structures for representing partitions and hierarchies, and facilities for computing on these, including methods for measuring proximity and obtaining consensus and ``secondary'' clusterings. \end{abstract} <>= options(width = 60) library("clue") @ % \section{Introduction} \label{sec:introduction} \emph{Cluster ensembles} are collections of clusterings, which are all of the same ``kind'' (e.g., collections of partitions, or collections of hierarchies), of a set of objects. Such ensembles can be obtained, for example, by varying the (hyper)parameters of a ``base'' clustering algorithm, by resampling or reweighting the set of objects, or by employing several different base clusterers. Questions of ``agreement'' in cluster ensembles, and obtaining ``consensus'' clusterings from it, have been studied in several scientific communities for quite some time now. A special issue of the Journal of Classification was devoted to ``Comparison and Consensus of Classifications'' \citep{cluster:Day:1986} almost two decades ago. The recent popularization of ensemble methods such as Bayesian model averaging \citep{cluster:Hoeting+Madigan+Raftery:1999}, bagging \citep{cluster:Breiman:1996} and boosting \citep{cluster:Friedman+Hastie+Tibshirani:2000}, typically in a supervised leaning context, has also furthered the research interest in using ensemble methods to improve the quality and robustness of cluster solutions. Cluster ensembles can also be utilized to aggregate base results over conditioning or grouping variables in multi-way data, to reuse existing knowledge, and to accommodate the needs of distributed computing, see e.g.\ \cite{cluster:Hornik:2005a} and \cite{cluster:Strehl+Ghosh:2003a} for more information. Package~\pkg{clue} is an extension package for R~\citep{cluster:R:2005} providing a computational environment for creating and analyzing cluster ensembles. In Section~\ref{sec:structures+algorithms}, we describe the underlying data structures, and the functionality for measuring proximity, obtaining consensus clusterings, and ``secondary'' clusterings. Four examples are discussed in Section~\ref{sec:examples}. Section~\ref{sec:outlook} concludes the paper. A previous version of this manuscript was published in the \emph{Journal of Statistical Software} \citep{cluster:Hornik:2005b}. \section{Data structures and algorithms} \label{sec:structures+algorithms} \subsection{Partitions and hierarchies} Representations of clusterings of objects greatly vary across the multitude of methods available in R packages. For example, the class ids (``cluster labels'') for the results of \code{kmeans()} in base package~\pkg{stats}, \code{pam()} in recommended package~\pkg{cluster}~\citep{cluster:Rousseeuw+Struyf+Hubert:2005, cluster:Struyf+Hubert+Rousseeuw:1996}, and \code{Mclust()} in package~\pkg{mclust}~\citep{cluster:Fraley+Raftery+Wehrens:2005, cluster:Fraley+Raftery:2003}, are available as components named \code{cluster}, \code{clustering}, and \code{classification}, respectively, of the R objects returned by these functions. In many cases, the representations inherit from suitable classes. (We note that for versions of R prior to 2.1.0, \code{kmeans()} only returned a ``raw'' (unclassed) result, which was changed alongside the development of \pkg{clue}.) We deal with this heterogeneity of representations by providing getters for the key underlying data, such as the number of objects from which a clustering was obtained, and predicates, e.g.\ for determining whether an R object represents a partition of objects or not. These getters, such as \code{n\_of\_objects()}, and predicates are implemented as S3 generics, so that there is a \emph{conceptual}, but no formal class system underlying the predicates. Support for classed representations can easily be added by providing S3 methods. \subsubsection{Partitions} The partitions considered in \pkg{clue} are possibly soft (``fuzzy'') partitions, where for each object~$i$ and class~$j$ there is a non-negative number~$\mu_{ij}$ quantifying the ``belongingness'' or \emph{membership} of object~$i$ to class~$j$, with $\sum_j \mu_{ij} = 1$. For hard (``crisp'') partitions, all $\mu_{ij}$ are in $\{0, 1\}$. We can gather the $\mu_{ij}$ into the \emph{membership matrix} $M = [\mu_{ij}]$, where rows correspond to objects and columns to classes. The \emph{number of classes} of a partition, computed by function \code{n\_of\_classes()}, is the number of $j$ for which $\mu_{ij} > 0$ for at least one object~$i$. This may be less than the number of ``available'' classes, corresponding to the number of columns in a membership matrix representing the partition. The predicate functions \code{is.cl\_partition()}, \code{is.cl\_hard\_partition()}, and \code{is.cl\_soft\_partition()} are used to indicate whether R objects represent partitions of objects of the respective kind, with hard partitions as characterized above (all memberships in $\{0, 1\}$). (Hence, ``fuzzy clustering'' algorithms can in principle also give a hard partition.) \code{is.cl\_partition()} and \code{is.cl\_hard\_partition()} are generic functions; \code{is.cl\_soft\_partition()} gives true iff \code{is.cl\_partition()} is true and \code{is.cl\_hard\_partition()} is false. For R objects representing partitions, function \code{cl\_membership()} computes an R object with the membership values, currently always as a dense membership matrix with additional attributes. This is obviously rather inefficient for computations on hard partitions; we are planning to add ``canned'' sparse representations (using the vector of class ids) in future versions. Function \code{as.cl\_membership()} can be used for coercing \dQuote{raw} class ids (given as atomic vectors) or membership values (given as numeric matrices) to membership objects. Function \code{cl\_class\_ids()} determines the class ids of a partition. For soft partitions, the class ids returned are those of the \dQuote{nearest} hard partition obtained by taking the class ids of the (first) maximal membership values. Note that the cardinality of the set of the class ids may be less than the number of classes in the (soft) partition. Many partitioning methods are based on \emph{prototypes} (``centers''). In typical cases, these are points~$p_j$ in the same feature space the measurements~$x_i$ on the objects~$i$ to be partitioned are in, so that one can measure distance between objects and prototypes, and e.g.\ classify objects to their closest prototype. Such partitioning methods can also induce partitions of the entire feature space (rather than ``just'' the set of objects to be partitioned). Currently, package \pkg{clue} has only minimal support for this ``additional'' structure, providing a \code{cl\_prototypes()} generic for extracting the prototypes, and is mostly focused on computations on partitions which are based on their memberships. Many algorithms resulting in partitions of a given set of objects can be taken to induce a partition of the underlying feature space for the measurements on the objects, so that class memberships for ``new'' objects can be obtained from the induced partition. Examples include partitions based on assigning objects to their ``closest'' prototypes, or providing mixture models for the distribution of objects in feature space. Package~\pkg{clue} provides a \code{cl\_predict()} generic for predicting the class memberships of new objects (if possible). Function \code{cl\_fuzziness()} computes softness (fuzziness) measures for (ensembles) of partitions. Built-in measures are the partition coefficient \label{PC} and partition entropy \citep[e.g.,][]{cluster:Bezdek:1981}, with an option to normalize in a way that hard partitions and the ``fuzziest'' possible partition (where all memberships are the same) get fuzziness values of zero and one, respectively. Note that this normalization differs from ``standard'' ones in the literature. In the sequel, we shall also use the concept of the \emph{co-membership matrix} $C(M) = M M'$, where $'$ denotes matrix transposition, of a partition. For hard partitions, an entry $c_{ij}$ of $C(M)$ is 1 iff the corresponding objects $i$ and $j$ are in the same class, and 0 otherwise. \subsubsection{Hierarchies} The hierarchies considered in \pkg{clue} are \emph{total indexed hierarchies}, also known as \emph{$n$-valued trees}, and hence correspond in a one-to-one manner to \emph{ultrametrics} (distances $u_{ij}$ between pairs of objects $i$ and $j$ which satisfy the ultrametric constraint $u_{ij} = \max(u_{ik}, u_{jk})$ for all triples $i$, $j$, and $k$). See e.g.~\citet[Page~69--71]{cluster:Gordon:1999}. Function \code{cl\_ultrametric(x)} computes the associated ultrametric from an R object \code{x} representing a hierarchy of objects. If \code{x} is not an ultrametric, function \code{cophenetic()} in base package~\pkg{stats} is used to obtain the ultrametric (also known as cophenetic) distances from the hierarchy, which in turn by default calls the S3 generic \code{as.hclust()} (also in \pkg{stats}) on the hierarchy. Support for classes which represent hierarchies can thus be added by providing \code{as.hclust()} methods for this class. In R~2.1.0 or better (again as part of the work on \pkg{clue}), \code{cophenetic} is an S3 generic as well, and one can also more directly provide methods for this if necessary. In addition, there is a generic function \code{as.cl\_ultrametric()} which can be used for coercing \emph{raw} (non-classed) ultrametrics, represented as numeric vectors (of the lower-half entries) or numeric matrices, to ultrametric objects. Finally, the generic predicate function \code{is.cl\_hierarchy()} is used to determine whether an R object represents a hierarchy or not. Ultrametric objects can also be coerced to classes~\class{dendrogram} and \class{hclust} (from base package~\pkg{stats}), and hence in particular use the \code{plot()} methods for these classes. By default, plotting an ultrametric object uses the plot method for dendrograms. Obtaining a hierarchy on a given set of objects can be thought of as transforming the pairwise dissimilarities between the objects (which typically do not yet satisfy the ultrametric constraints) into an ultrametric. Ideally, this ultrametric should be as close as possible to the dissimilarities. In some important cases, explicit solutions are possible (e.g., ``standard'' hierarchical clustering with single or complete linkage gives the optimal ultrametric dominated by or dominating the dissimilarities, respectively). On the other hand, the problem of finding the closest ultrametric in the least squares sense is known to be NP-hard \citep{cluster:Krivanek+Moravek:1986,cluster:Krivanek:1986}. One important class of heuristics for finding least squares fits is based on iterative projection on convex sets of constraints \citep{cluster:Hubert+Arabie:1995}. \label{SUMT} Function \code{ls\_fit\_ultrametric()} follows \cite{cluster:DeSoete:1986} to use an SUMT \citep[Sequential Unconstrained Minimization Technique;][]{cluster:Fiacco+McCormick:1968} approach in turn simplifying the suggestions in \cite{cluster:Carroll+Pruzansky:1980}. Let $L(u)$ be the function to be minimized over all $u$ in some constrained set $\mathcal{U}$---in our case, $L(u) = \sum (d_{ij}-u_{ij})^2$ is the least squares criterion, and $\mathcal{U}$ is the set of all ultrametrics $u$. One iteratively minimizes $L(u) + \rho_k P(u)$, where $P(u)$ is a non-negative function penalizing violations of the constraints such that $P(u)$ is zero iff $u \in \mathcal{U}$. The $\rho$ values are increased according to the rule $\rho_{k+1} = q \rho_k$ for some constant $q > 1$, until convergence is obtained in the sense that e.g.\ the Euclidean distance between successive solutions $u_k$ and $u_{k+1}$ is small enough. Optionally, the final $u_k$ is then suitably projected onto $\mathcal{U}$. For \code{ls\_fit\_ultrametric()}, we obtain the starting value $u_0$ by \dQuote{random shaking} of the given dissimilarity object, and use the penalty function $P(u) = \sum_{\Omega} (u_{ij} - u_{jk}) ^ 2$, were $\Omega$ contains all triples $i, j, k$ for which $u_{ij} \le \min(u_{ik}, u_{jk})$ and $u_{ik} \ne u_{jk}$, i.e., for which $u$ violates the ultrametric constraints. The unconstrained minimizations are carried out using either \code{optim()} or \code{nlm()} in base package~\pkg{stats}, with analytic gradients given in \cite{cluster:Carroll+Pruzansky:1980}. This ``works'', even though we note however that $P$ is not even a continuous function, which seems to have gone unnoticed in the literature! (Consider an ultrametric $u$ for which $u_{ij} = u_{ik} < u_{jk}$ for some $i, j, k$ and define $u(\delta)$ by changing the $u_{ij}$ to $u_{ij} + \delta$. For $u$, both $(i,j,k)$ and $(j,i,k)$ are in the violation set $\Omega$, whereas for all $\delta$ sufficiently small, only $(j,i,k)$ is the violation set for $u(\delta)$. Hence, $\lim_{\delta\to 0} P(u(\delta)) = P(u) + (u_{ij} - u_{ik})^2$. This shows that $P$ is discontinuous at all non-constant $u$ with duplicated entries. On the other hand, it is continuously differentiable at all $u$ with unique entries.) Hence, we need to turn off checking analytical gradients when using \code{nlm()} for minimization. The default optimization using conjugate gradients should work reasonably well for medium to large size problems. For \dQuote{small} ones, using \code{nlm()} is usually faster. Note that the number of ultrametric constraints is of the order $n^3$, suggesting to use the SUMT approach in favor of \code{constrOptim()} in \pkg{stats}. It should be noted that the SUMT approach is a heuristic which can not be guaranteed to find the global minimum. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the base algorithm. \subsubsection{Extensibility} The methods provided in package~\pkg{clue} handle the partitions and hierarchies obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}~\citep{cluster:Hornik+Hothorn+Karatzoglou:2006}, \pkg{cba}~\citep{cluster:Buchta+Hahsler:2005}, \pkg{cclust}~\citep{cluster:Dimitriadou:2005}, \pkg{cluster}, \pkg{e1071}~\citep{cluster:Dimitriadou+Hornik+Leisch:2005}, \pkg{flexclust}~\citep{cluster:Leisch:2006a}, \pkg{flexmix}~\citep{cluster:Leisch:2004}, \pkg{kernlab}~\citep{cluster:Karatzoglou+Smola+Hornik:2004}, and \pkg{mclust} (and of course, \pkg{clue} itself). Extending support to other packages is straightforward, provided that clusterings are instances of classes. Suppose e.g.\ that a package has a function \code{glvq()} for ``generalized'' (i.e., non-Euclidean) Learning Vector Quantization which returns an object of class~\class{glvq}, in turn being a list with component \code{class\_ids} containing the class ids. To integrate this into the \pkg{clue} framework, all that is necessary is to provide the following methods. <<>>= cl_class_ids.glvq <- function(x) as.cl_class_ids(x$class_ids) is.cl_partition.glvq <- function(x) TRUE is.cl_hard_partition.glvq <- function(x) TRUE @ % $ \subsection{Cluster ensembles} Cluster ensembles are realized as lists of clusterings with additional class information. All clusterings in an ensemble must be of the same ``kind'' (i.e., either all partitions as known to \code{is.cl\_partition()}, or all hierarchies as known to \code{is.cl\_hierarchy()}, respectively), and have the same number of objects. If all clusterings are partitions, the list realizing the ensemble has class~\class{cl\_partition\_ensemble} and inherits from \class{cl\_ensemble}; if all clusterings are hierarchies, it has class~\class{cl\_hierarchy\_ensemble} and inherits from \class{cl\_ensemble}. Empty ensembles cannot be categorized according to the kind of clusterings they contain, and hence only have class~\class{cl\_ensemble}. Function \code{cl\_ensemble()} creates a cluster ensemble object from clusterings given either one-by-one, or as a list passed to the \code{list} argument. As unclassed lists could be used to represent single clusterings (in particular for results from \code{kmeans()} in versions of R prior to 2.1.0), we prefer not to assume that an unnamed given list is a list of clusterings. \code{cl\_ensemble()} verifies that all given clusterings are of the same kind, and all have the same number of objects. (By the notion of cluster ensembles, we should in principle verify that the clusterings come from the \emph{same} objects, which of course is not always possible.) The list representation makes it possible to use \code{lapply()} for computations on the individual clusterings in (i.e., the components of) a cluster ensemble. Available methods for cluster ensembles include those for subscripting, \code{c()}, \code{rep()}, \code{print()}, and \code{unique()}, where the last is based on a \code{unique()} method for lists added in R~2.1.1, and makes it possible to find unique and duplicated elements in cluster ensembles. The elements of the ensemble can be tabulated using \code{cl\_tabulate()}. Function \code{cl\_boot()} generates cluster ensembles with bootstrap replicates of the results of applying a \dQuote{base} clustering algorithm to a given data set. Currently, this is a rather simple-minded function with limited applicability, and mostly useful for studying the effect of (uncontrolled) random initializations of fixed-point partitioning algorithms such as \code{kmeans()} or \code{cmeans()} in package~\pkg{e1071}. To study the effect of varying control parameters or explicitly providing random starting values, the respective cluster ensemble has to be generated explicitly (most conveniently by using \code{replicate()} to create a list \code{lst} of suitable instances of clusterings obtained by the base algorithm, and using \code{cl\_ensemble(list = lst)} to create the ensemble). Resampling the training data is possible for base algorithms which can predict the class memberships of new data using \code{cl\_predict} (e.g., by classifying the out-of-bag data to their closest prototype). In fact, we believe that for unsupervised learning methods such as clustering, \emph{reweighting} is conceptually superior to resampling, and have therefore recently enhanced package~\pkg{e1071} to provide an implementation of weighted fuzzy $c$-means, and package~\pkg{flexclust} contains an implementation of weighted $k$-means. We are currently experimenting with interfaces for providing ``direct'' support for reweighting via \code{cl\_boot()}. \subsection{Cluster proximities} \subsubsection{Principles} Computing dissimilarities and similarities (``agreements'') between clusterings of the same objects is a key ingredient in the analysis of cluster ensembles. The ``standard'' data structures available for such proximity data (measures of similarity or dissimilarity) are classes~\class{dist} and \class{dissimilarity} in package~\pkg{cluster} (which basically, but not strictly, extends \class{dist}), and are both not entirely suited to our needs. First, they are confined to \emph{symmetric} dissimilarity data. Second, they do not provide enough reflectance. We also note that the Bioconductor package~\pkg{graph}~\citep{cluster:Gentleman+Whalen:2005} contains an efficient subscript method for objects of class~\class{dist}, but returns a ``raw'' matrix for row/column subscripting. For package~\pkg{clue}, we use the following approach. There are classes for symmetric and (possibly) non-symmetric proximity data (\class{cl\_proximity} and \class{cl\_cross\_proximity}), which, in addition to holding the numeric data, also contain a description ``slot'' (attribute), currently a character string, as a first approximation to providing more reflectance. Internally, symmetric proximity data are store the lower diagonal proximity values in a numeric vector (in row-major order), i.e., the same way as objects of class~\class{dist}; a \code{self} attribute can be used for diagonal values (in case some of these are non-zero). Symmetric proximity objects can be coerced to dense matrices using \code{as.matrix()}. It is possible to use 2-index matrix-style subscripting for symmetric proximity objects; unless this uses identical row and column indices, it results in a non-symmetric proximity object. This approach ``propagates'' to classes for symmetric and (possibly) non-symmetric cluster dissimilarity and agreement data (e.g., \class{cl\_dissimilarity} and \class{cl\_cross\_dissimilarity} for dissimilarity data), which extend the respective proximity classes. Ultrametric objects are implemented as symmetric proximity objects with a dissimilarity interpretation so that self-proximities are zero, and inherit from classes~\class{cl\_dissimilarity} and \class{cl\_proximity}. Providing reflectance is far from optimal. For example, if \code{s} is a similarity object (with cluster agreements), \code{1 - s} is a dissimilarity one, but the description is preserved unchanged. This issue could be addressed by providing high-level functions for transforming proximities. \label{synopsis} Cluster dissimilarities are computed via \code{cl\_dissimilarity()} with synopsis \code{cl\_dissimilarity(x, y = NULL, method = "euclidean")}, where \code{x} and \code{y} are cluster ensemble objects or coercible to such, or \code{NULL} (\code{y} only). If \code{y} is \code{NULL}, the return value is an object of class~\class{cl\_dissimilarity} which contains the dissimilarities between all pairs of clusterings in \code{x}. Otherwise, it is an object of class~\class{cl\_cross\_dissimilarity} with the dissimilarities between the clusterings in \code{x} and the clusterings in \code{y}. Formal argument \code{method} is either a character string specifying one of the built-in methods for computing dissimilarity, or a function to be taken as a user-defined method, making it reasonably straightforward to add methods. Function \code{cl\_agreement()} has the same interface as \code{cl\_dissimilarity()}, returning cluster similarity objects with respective classes~\class{cl\_agreement} and \class{cl\_cross\_agreement}. Built-in methods for computing dissimilarities may coincide (in which case they are transforms of each other), but do not necessarily do so, as there typically are no canonical transformations. E.g., according to needs and scientific community, agreements might be transformed to dissimilarities via $d = - \log(s)$ or the square root thereof \citep[e.g.,][]{cluster:Strehl+Ghosh:2003b}, or via $d = 1 - s$. \subsubsection{Partition proximities} When assessing agreement or dissimilarity of partitions, one needs to consider that the class ids may be permuted arbitrarily without changing the underlying partitions. For membership matrices~$M$, permuting class ids amounts to replacing $M$ by $M \Pi$, where $\Pi$ is a suitable permutation matrix. We note that the co-membership matrix $C(M) = MM'$ is unchanged by these transformations; hence, proximity measures based on co-occurrences, such as the Katz-Powell \citep{cluster:Katz+Powell:1953} or Rand \citep{cluster:Rand:1971} indices, do not explicitly need to adjust for possible re-labeling. The same is true for measures based on the ``confusion matrix'' $M' \tilde{M}$ of two membership matrices $M$ and $\tilde{M}$ which are invariant under permutations of rows and columns, such as the Normalized Mutual Information (NMI) measure introduced in \cite{cluster:Strehl+Ghosh:2003a}. Other proximity measures need to find permutations so that the classes are optimally matched, which of course in general requires exhaustive search through all $k!$ possible permutations, where $k$ is the (common) number of classes in the partitions, and thus will typically be prohibitively expensive. Fortunately, in some important cases, optimal matchings can be determined very efficiently. We explain this in detail for ``Euclidean'' partition dissimilarity and agreement (which in fact is the default measure used by \code{cl\_dissimilarity()} and \code{cl\_agreement()}). Euclidean partition dissimilarity \citep{cluster:Dimitriadou+Weingessel+Hornik:2002} is defined as \begin{displaymath} d(M, \tilde{M}) = \min\nolimits_\Pi \| M - \tilde{M} \Pi \| \end{displaymath} where the minimum is taken over all permutation matrices~$\Pi$, $\|\cdot\|$ is the Frobenius norm (so that $\|Y\|^2 = \trace(Y'Y)$), and $n$ is the (common) number of objects in the partitions. As $\| M - \tilde{M} \Pi \|^2 = \trace(M'M) - 2 \trace(M'\tilde{M}\Pi) + \trace(\Pi'\tilde{M}'\tilde{M}\Pi) = \trace(M'M) - 2 \trace(M'\tilde{M}\Pi) + \trace(\tilde{M}'\tilde{M})$, we see that minimizing $\| M - \tilde{M} \Pi \|^2$ is equivalent to maximizing $\trace(M'\tilde{M}\Pi) = \sum_{i,k}{\mu_{ik}\tilde{\mu}}_{i,\pi(k)}$, which for hard partitions is the number of objects with the same label in the partitions given by $M$ and $\tilde{M}\Pi$. Finding the optimal $\Pi$ is thus recognized as an instance of the \emph{linear sum assignment problem} (LSAP, also known as the weighted bipartite graph matching problem). The LSAP can be solved by linear programming, e.g., using Simplex-style primal algorithms as done by function~\code{lp.assign()} in package~\pkg{lpSolve}~\citep{cluster:Buttrey:2005}, but primal-dual algorithms such as the so-called Hungarian method can be shown to find the optimum in time $O(k^3)$ \citep[e.g.,][]{cluster:Papadimitriou+Steiglitz:1982}. Available published implementations include TOMS 548 \citep{cluster:Carpaneto+Toth:1980}, which however is restricted to integer weights and $k < 131$. One can also transform the LSAP into a network flow problem, and use e.g.~RELAX-IV \citep{cluster:Bertsekas+Tseng:1994} for solving this, as is done in package~\pkg{optmatch}~\citep{cluster:Hansen:2005}. In package~\pkg{clue}, we use an efficient C implementation of the Hungarian algorithm kindly provided to us by Walter B\"ohm, which has been found to perform very well across a wide range of problem sizes. \cite{cluster:Gordon+Vichi:2001} use a variant of Euclidean dissimilarity (``GV1 dissimilarity'') which is based on the sum of the squared difference of the memberships of matched (non-empty) classes only, discarding the unmatched ones (see their Example~2). This results in a measure which is discontinuous over the space of soft partitions with arbitrary numbers of classes. The partition agreement measures ``angle'' and ``diag'' (maximal cosine of angle between the memberships, and maximal co-classification rate, where both maxima are taken over all column permutations of the membership matrices) are based on solving the same LSAP as for Euclidean dissimilarity. Finally, Manhattan partition dissimilarity is defined as the minimal sum of the absolute differences of $M$ and all column permutations of $\tilde{M}$, and can again be computed efficiently by solving an LSAP. For hard partitions, both Manhattan and squared Euclidean dissimilarity give twice the \emph{transfer distance} \citep{cluster:Charon+Denoeud+Guenoche:2006}, which is the minimum number of objects that must be removed so that the implied partitions (restrictions to the remaining objects) are identical. This is also known as the \emph{$R$-metric} in \cite{cluster:Day:1981}, i.e., the number of augmentations and removals of single objects needed to transform one partition into the other, and the \emph{partition-distance} in \cite{cluster:Gusfield:2002}. Note when assessing proximity that agreements for soft partitions are always (and quite often considerably) lower than the agreements for the corresponding nearest hard partitions, unless the agreement measures are based on the latter anyways (as currently done for Rand, Katz-Powell, and NMI). Package~\pkg{clue} provides additional agreement measures, such as the Jaccard and Fowles-Mallows \citep[quite often incorrectly attributed to \cite{cluster:Wallace:1983}]{cluster:Fowlkes+Mallows:1983a} indices, and dissimilarity measures such as the ``symdiff'' and Rand distances (the latter is proportional to the metric of \cite{cluster:Mirkin:1996}) and the metrics discussed in \cite{cluster:Boorman+Arabie:1972}. One could easily add more proximity measures, such as the ``Variation of Information'' \citep{cluster:Meila:2003}. However, all these measures are rigorously defined for hard partitions only. To see why extensions to soft partitions are far from straightforward, consider e.g.\ measures based on the confusion matrix. Its entries count the cardinality of certain intersections of sets. \label{fuzzy} In a fuzzy context for soft partitions, a natural generalization would be using fuzzy cardinalities (i.e., sums of memberships values) of fuzzy intersections instead. There are many possible choices for the latter, with the product of the membership values (corresponding to employing the confusion matrix also in the fuzzy case) one of them, but the minimum instead of the product being the ``usual'' choice. A similar point can be made for co-occurrences of soft memberships. We are not aware of systematic investigations of these extension issues. \subsubsection{Hierarchy proximities} Available built-in dissimilarity measures for hierarchies include \emph{Euclidean} (again, the default measure used by \code{cl\_dissimilarity()}) and Manhattan dissimilarity, which are simply the Euclidean (square root of the sum of squared differences) and Manhattan (sum of the absolute differences) dissimilarities between the associated ultrametrics. Cophenetic dissimilarity is defined as $1 - c^2$, where $c$ is the cophenetic correlation coefficient \citep{cluster:Sokal+Rohlf:1962}, i.e., the Pearson product-moment correlation between the ultrametrics. Gamma dissimilarity is the rate of inversions between the associated ultrametrics $u$ and $v$ (i.e., the rate of pairs $(i,j)$ and $(k,l)$ for which $u_{ij} < u_{kl}$ and $v_{ij} > v_{kl}$). This measure is a linear transformation of Kruskal's~$\gamma$. Finally, symdiff dissimilarity is the cardinality of the symmetric set difference of the sets of classes (hierarchies in the strict sense) induced by the dendrograms. Associated agreement measures are obtained by suitable transformations of the dissimilarities~$d$; for Euclidean proximities, we prefer to use $1 / (1 + d)$ rather than e.g.\ $\exp(-d)$. One should note that whereas cophenetic and gamma dissimilarities are invariant to linear transformations, Euclidean and Manhattan ones are not. Hence, if only the relative ``structure'' of the dendrograms is of interest, these dissimilarities should only be used after transforming the ultrametrics to a common range of values (e.g., to $[0,1]$). \subsection{Consensus clusterings} Consensus clusterings ``synthesize'' the information in the elements of a cluster ensemble into a single clustering. There are three main approaches to obtaining consensus clusterings \citep{cluster:Hornik:2005a,cluster:Gordon+Vichi:2001}: in the \emph{constructive} approach, one specifies a way to construct a consensus clustering. In the \emph{axiomatic} approach, emphasis is on the investigation of existence and uniqueness of consensus clusterings characterized axiomatically. The \emph{optimization} approach formalizes the natural idea of describing consensus clusterings as the ones which ``optimally represent the ensemble'' by providing a criterion to be optimized over a suitable set $\mathcal{C}$ of possible consensus clusterings. If $d$ is a dissimilarity measure and $C_1, \ldots, C_B$ are the elements of the ensemble, one can e.g.\ look for solutions of the problem \begin{displaymath} \sum\nolimits_{b=1}^B w_b d(C, C_b) ^ p \Rightarrow \min\nolimits_{C \in \mathcal{C}}, \end{displaymath} for some $p \ge 0$, i.e., as clusterings~$C^*$ minimizing weighted average dissimilarity powers of order~$p$. Analogously, if a similarity measure is given, one can look for clusterings maximizing weighted average similarity powers. Following \cite{cluster:Gordon+Vichi:1998}, an above $C^*$ is referred to as (weighted) \emph{median} or \emph{medoid} clustering if $p = 1$ and the optimum is sought over the set of all possible base clusterings, or the set $\{ C_1, \ldots, C_B \}$ of the base clusterings, respectively. For $p = 2$, we have \emph{least squares} consensus clusterings (generalized means). For computing consensus clusterings, package~\pkg{clue} provides function \code{cl\_consensus()} with synopsis \code{cl\_consensus(x, method = NULL, weights = 1, control = list())}. This allows (similar to the functions for computing cluster proximities, see Section~\ref{synopsis} on Page~\pageref{synopsis}) argument \code{method} to be a character string specifying one of the built-in methods discussed below, or a function to be taken as a user-defined method (taking an ensemble, the case weights, and a list of control parameters as its arguments), again making it reasonably straightforward to add methods. In addition, function~\code{cl\_medoid()} can be used for obtaining medoid partitions (using, in principle, arbitrary dissimilarities). Modulo possible differences in the case of ties, this gives the same results as (the medoid obtained by) \code{pam()} in package~\pkg{cluster}. If all elements of the ensemble are partitions, package~\pkg{clue} provides algorithms for computing soft least squares consensus partitions for weighted Euclidean, GV1 and co-membership dissimilarities. Let $M_1, \ldots, M_B$ and $M$ denote the membership matrices of the elements of the ensemble and their sought least squares consensus partition, respectively. For Euclidean dissimilarity, we need to find \begin{displaymath} \sum_b w_b \min\nolimits_{\Pi_b} \| M - M_b \Pi_b \|^2 \Rightarrow \min\nolimits_M \end{displaymath} over all membership matrices (i.e., stochastic matrices) $M$, or equivalently, \begin{displaymath} \sum_b w_b \| M - M_b \Pi_b \|^2 \Rightarrow \min\nolimits_{M, \Pi_1, \ldots, \Pi_B} \end{displaymath} over all $M$ and permutation matrices $\Pi_1, \ldots, \Pi_B$. Now fix the $\Pi_b$ and let $\bar{M} = s^{-1} \sum_b w_b M_b \Pi_b$ be the weighted average of the $M_b \Pi_b$, where $s = \sum_b w_b$. Then \begin{eqnarray*} \lefteqn{\sum_b w_b \| M - M_b \Pi_b \|^2} \\ &=& \sum_b w_b (\|M\|^2 - 2 \trace(M' M_b \Pi_b) + \|M_b\Pi_b\|^2) \\ &=& s \|M\|^2 - 2 s \trace(M' \bar{M}) + \sum_b w_b \|M_b\|^2 \\ &=& s (\|M - \bar{M}\|^2) + \sum_b w_b \|M_b\|^2 - s \|\bar{M}\|^2 \end{eqnarray*} Thus, as already observed in \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} and \cite{cluster:Gordon+Vichi:2001}, for fixed permutations $\Pi_b$ the optimal soft $M$ is given by $\bar{M}$. The optimal permutations can be found by minimizing $- s \|\bar{M}\|^2$, or equivalently, by maximizing \begin{displaymath} s^2 \|\bar{M}\|^2 = \sum_{\beta, b} w_\beta w_b \trace(\Pi_\beta'M_\beta'M_b\Pi_b). \end{displaymath} With $U_{\beta,b} = w_\beta w_b M_\beta' M_b$ we can rewrite the above as \begin{displaymath} \sum_{\beta, b} w_\beta w_b \trace(\Pi_\beta'M_\beta'M_b\Pi_b) = \sum_{\beta,b} \sum_{j=1}^k [U_{\beta,b}]_{\pi_\beta(j), \pi_b(j)} =: \sum_{j=1}^k c_{\pi_1(j), \ldots, \pi_B(j)} \end{displaymath} This is an instance of the \emph{multi-dimensional assignment problem} (MAP), which, contrary to the LSAP, is known to be NP-hard \citep[e.g., via reduction to 3-DIMENSIONAL MATCHING,][]{cluster:Garey+Johnson:1979}, and can e.g.\ be approached using randomized parallel algorithms \citep{cluster:Oliveira+Pardalos:2004}. Branch-and-bound approaches suggested in the literature \citep[e.g.,][]{cluster:Grundel+Oliveira+Pardalos:2005} are unfortunately computationally infeasible for ``typical'' sizes of cluster ensembles ($B \ge 20$, maybe even in the hundreds). Package~\pkg{clue} provides two heuristics for (approximately) finding the soft least squares consensus partition for Euclidean dissimilarity. Method \code{"DWH"} of function \code{cl\_consensus()} is an extension of the greedy algorithm in \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} which is based on a single forward pass through the ensemble which in each step chooses the ``locally'' optimal $\Pi$. Starting with $\tilde{M}_1 = M_1$, $\tilde{M}_b$ is obtained from $\tilde{M}_{b-1}$ by optimally matching $M_b \Pi_b$ to this, and taking a weighted average of $\tilde{M}_{b-1}$ and $M_b \Pi_b$ in a way that $\tilde{M}_b$ is the weighted average of the first~$b$ $M_\beta \Pi_\beta$. This simple approach could be further enhanced via back-fitting or several passes, in essence resulting in an ``on-line'' version of method \code{"SE"}. This, in turn, is a fixed-point algorithm, which iterates between updating $M$ as the weighted average of the current $M_b \Pi_b$, and determining the $\Pi_b$ by optimally matching the current $M$ to the individual $M_b$. Finally, method \code{"GV1"} implements the fixed-point algorithm for the ``first model'' in \cite{cluster:Gordon+Vichi:2001}, which gives least squares consensus partitions for GV1 dissimilarity. In the above, we implicitly assumed that all partitions in the ensemble as well as the sought consensus partition have the same number of classes. The more general case can be dealt with through suitable ``projection'' devices. When using co-membership dissimilarity, the least squares consensus partition is determined by minimizing \begin{eqnarray*} \lefteqn{\sum_b w_b \|MM' - M_bM_b'\|^2} \\ &=& s \|MM' - \bar{C}\|^2 + \sum_b w_b \|M_bM_b'\|^2 - s \|\bar{C}\|^2 \end{eqnarray*} over all membership matrices~$M$, where now $\bar{C} = s^{-1} \sum_b C(M_b) = s^{-1} \sum_b M_bM_b'$ is the weighted average co-membership matrix of the ensemble. This corresponds to the ``third model'' in \cite{cluster:Gordon+Vichi:2001}. Method \code{"GV3"} of function \code{cl\_consensus()} provides a SUMT approach (see Section~\ref{SUMT} on Page~\pageref{SUMT}) for finding the minimum. We note that this strategy could more generally be applied to consensus problems of the form \begin{displaymath} \sum_b w_b \|\Phi(M) - \Phi(M_b)\|^2 \Rightarrow \min\nolimits_M, \end{displaymath} which are equivalent to minimizing $\|\Phi(B) - \bar{\Phi}\|^2$, with $\bar{\Phi}$ the weighted average of the $\Phi(M_b)$. This includes e.g.\ the case where generalized co-memberships are defined by taking the ``standard'' fuzzy intersection of co-incidences, as discussed in Section~\ref{fuzzy} on Page~\pageref{fuzzy}. Package~\pkg{clue} currently does not provide algorithms for obtaining \emph{hard} consensus partitions, as e.g.\ done in \cite{cluster:Krieger+Green:1999} using Rand proximity. It seems ``natural'' to extend the methods discussed above to include a constraint on softness, e.g., on the partition coefficient PC (see Section~\ref{PC} on Page~\pageref{PC}). For Euclidean dissimilarity, straightforward Lagrangian computations show that the constrained minima are of the form $\bar{M}(\alpha) = \alpha \bar{M} + (1 - \alpha) E$, where $E$ is the ``maximally soft'' membership with all entries equal to $1/k$, $\bar{M}$ is again the weighted average of the $M_b\Pi_b$ with the $\Pi_b$ solving the underlying MAP, and $\alpha$ is chosen such that $PC(\bar{M}(\alpha))$ equals a prescribed value. As $\alpha$ increases (even beyond one), softness of the $\bar{M}(\alpha)$ decreases. However, for $\alpha^* > 1 / (1 - k\mu^*)$, where $\mu^*$ is the minimum of the entries of $\bar{M}$, the $\bar{M}(\alpha)$ have negative entries, and are no longer feasible membership matrices. Obviously, the non-negativity constraints for the $\bar{M}(\alpha)$ eventually put restrictions on the admissible $\Pi_b$ in the underlying MAP. Thus, such a simple relaxation approach to obtaining optimal hard partitions is not feasible. For ensembles of hierarchies, \code{cl\_consensus()} provides a built-in method (\code{"cophenetic"}) for approximately minimizing average weighted squared Euclidean dissimilarity \begin{displaymath} \sum_b w_b \| U - U_b \|^2 \Rightarrow \min\nolimits_U \end{displaymath} over all ultrametrics~$U$, where $U_1, \ldots, U_B$ are the ultrametrics corresponding to the elements of the ensemble. This is of course equivalent to minimizing $\| U - \bar{U} \|^2$, where $\bar{U} = s^{-1} \sum_b w_b U_b$ is the weighted average of the $U_b$. The SUMT approach provided by function \code{ls\_fit\_ultrametric()} (see Section~\ref{SUMT} on Page~\pageref{SUMT}) is employed for finding the sought weighted least squares consensus hierarchy. In addition, method \code{"majority"} obtains a consensus hierarchy from an extension of the majority consensus tree of \cite{cluster:Margush+McMorris:1981}, which minimizes $L(U) = \sum_b w_b d(U_b, U)$ over all ultrametrics~$U$, where $d$ is the symmetric difference dissimilarity. Clearly, the available methods use heuristics for solving hard optimization problems, and cannot be guaranteed to find a global optimum. Standard practice would recommend to use the best solution found in ``sufficiently many'' replications of the methods. Alternative recent approaches to obtaining consensus partitions include ``Bagged Clustering'' \citep[provided by \code{bclust()} in package~\pkg{e1071}]{cluster:Leisch:1999}, the ``evidence accumulation'' framework of \cite{cluster:Fred+Jain:2002}, the NMI optimization and graph-partitioning methods in \cite{cluster:Strehl+Ghosh:2003a}, ``Bagged Clustering'' as in \cite{cluster:Dudoit+Fridlyand:2003}, and the hybrid bipartite graph formulation of \cite{cluster:Fern+Brodley:2004}. Typically, these approaches are constructive, and can easily be implemented based on the infrastructure provided by package~\pkg{clue}. Evidence accumulation amounts to standard hierarchical clustering of the average co-membership matrix. Procedure~BagClust1 of \cite{cluster:Dudoit+Fridlyand:2003} amounts to computing $B^{-1} \sum_b M_b\Pi_b$, where each $\Pi_b$ is determined by optimal Euclidean matching of $M_b$ to a fixed reference membership $M_0$. In the corresponding ``Bagged Clustering'' framework, $M_0$ and the $M_b$ are obtained by applying the base clusterer to the original data set and bootstrap samples from it, respectively. This is implemented as method \code{"DFBC1"} of \code{cl\_bag()} in package~\pkg{clue}. Finally, the approach of \cite{cluster:Fern+Brodley:2004} solves an LSAP for an asymmetric cost matrix based on object-by-all-classes incidences. \subsection{Cluster partitions} To investigate the ``structure'' in a cluster ensemble, an obvious idea is to start clustering the clusterings in the ensemble, resulting in ``secondary'' clusterings \citep{cluster:Gordon+Vichi:1998, cluster:Gordon:1999}. This can e.g.\ be performed by using \code{cl\_dissimilarity()} (or \code{cl\_agreement()}) to compute a dissimilarity matrix for the ensemble, and feed this into a dissimilarity-based clustering algorithm (such as \code{pam()} in package~\pkg{cluster} or \code{hclust()} in package~\pkg{stats}). (One can even use \code{cutree()} to obtain hard partitions from hierarchies thus obtained.) If prototypes (``typical clusterings'') are desired for partitions of clusterings, they can be determined post-hoc by finding suitable consensus clusterings in the classes of the partition, e.g., using \code{cl\_consensus()} or \code{cl\_medoid()}. Package~\pkg{clue} additionally provides \code{cl\_pclust()} for direct prototype-based partitioning based on minimizing criterion functions of the form $\sum w_b u_{bj}^m d(x_b, p_j)^e$, the sum of the case-weighted membership-weighted $e$-th powers of the dissimilarities between the elements~$x_b$ of the ensemble and the prototypes~$p_j$, for suitable dissimilarities~$d$ and exponents~$e$. (The underlying feature spaces are that of membership matrices and ultrametrics, respectively, for partitions and hierarchies.) Parameter~$m$ must not be less than one and controls the softness of the obtained partitions, corresponding to the \dQuote{fuzzification parameter} of the fuzzy $c$-means algorithm. For $m = 1$, a generalization of the Lloyd-Forgy variant \citep{cluster:Lloyd:1957, cluster:Forgy:1965, cluster:Lloyd:1982} of the $k$-means algorithm is used, which iterates between reclassifying objects to their closest prototypes, and computing new prototypes as consensus clusterings for the classes. \citet{cluster:Gaul+Schader:1988} introduced this procedure for \dQuote{Clusterwise Aggregation of Relations} (with the same domains), containing equivalence relations, i.e., hard partitions, as a special case. For $m > 1$, a generalization of the fuzzy $c$-means recipe \citep[e.g.,][]{cluster:Bezdek:1981} is used, which alternates between computing optimal memberships for fixed prototypes, and computing new prototypes as the suitably weighted consensus clusterings for the classes. This procedure is repeated until convergence occurs, or the maximal number of iterations is reached. Consensus clusterings are computed using (one of the methods provided by) \code{cl\_consensus}, with dissimilarities~$d$ and exponent~$e$ implied by method employed, and obtained via a registration mechanism. The default methods compute Least Squares Euclidean consensus clusterings, i.e., use Euclidean dissimilarity~$d$ and $e = 2$. \section{Examples} \label{sec:examples} \subsection{Cassini data} \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} and \cite{cluster:Leisch:1999} use Cassini data sets to illustrate how e.g.\ suitable aggregation of base $k$-means results can reveal underlying non-convex structure which cannot be found by the base algorithm. Such data sets contain points in 2-dimensional space drawn from the uniform distribution on 3 structures, with the two ``outer'' ones banana-shaped and the ``middle'' one a circle, and can be obtained by function~\code{mlbench.cassini()} in package~\pkg{mlbench}~\citep{cluster:Leisch+Dimitriadou:2005}. Package~\pkg{clue} contains the data sets \code{Cassini} and \code{CKME}, which are an instance of a 1000-point Cassini data set, and a cluster ensemble of 50 $k$-means partitions of the data set into three classes, respectively. The data set is shown in Figure~\ref{fig:Cassini}. <>= data("Cassini") plot(Cassini$x, col = as.integer(Cassini$classes), xlab = "", ylab = "") @ % $ \begin{figure} \centering <>= <> @ % \caption{The Cassini data set.} \label{fig:Cassini} \end{figure} Figure~\ref{fig:CKME} gives a dendrogram of the Euclidean dissimilarities of the elements of the $k$-means ensemble. <>= data("CKME") plot(hclust(cl_dissimilarity(CKME)), labels = FALSE) @ % \begin{figure} \centering <>= <> @ % \caption{A dendrogram of the Euclidean dissimilarities of 50 $k$-means partitions of the Cassini data into 3 classes.} \label{fig:CKME} \end{figure} We can see that there are large groups of essentially identical $k$-means solutions. We can gain more insight by inspecting representatives of these three groups, or by computing the medoid of the ensemble <<>>= m1 <- cl_medoid(CKME) table(Medoid = cl_class_ids(m1), "True Classes" = Cassini$classes) @ % $ and inspecting it (Figure~\ref{fig:Cassini-medoid}): <>= plot(Cassini$x, col = cl_class_ids(m1), xlab = "", ylab = "") @ % $ \begin{figure} \centering <>= <> @ % \caption{Medoid of the Cassini $k$-means ensemble.} \label{fig:Cassini-medoid} \end{figure} Flipping this solution top-down gives a second ``typical'' partition. We see that the $k$-means base clusterers cannot resolve the underlying non-convex structure. For the least squares consensus of the ensemble, we obtain <<>>= set.seed(1234) m2 <- cl_consensus(CKME) @ % where here and below we set the random seed for reproducibility, noting that one should really use several replicates of the consensus heuristic. This consensus partition has confusion matrix <<>>= table(Consensus = cl_class_ids(m2), "True Classes" = Cassini$classes) @ % $ and class details as displayed in Figure~\ref{fig:Cassini-mean}: <>= plot(Cassini$x, col = cl_class_ids(m2), xlab = "", ylab = "") @ % $ \begin{figure} \centering <>= <> @ % \caption{Least Squares Consensus of the Cassini $k$-means ensemble.} \label{fig:Cassini-mean} \end{figure} This has drastically improved performance, and almost perfect recovery of the two outer shapes. In fact, \cite{cluster:Dimitriadou+Weingessel+Hornik:2002} show that almost perfect classification can be obtained by suitable combinations of different base clusterers ($k$-means, fuzzy $c$-means, and unsupervised fuzzy competitive learning). \subsection{Gordon-Vichi macroeconomic data} \citet[Table~1]{cluster:Gordon+Vichi:2001} provide soft partitions of 21 countries based on macroeconomic data for the years 1975, 1980, 1985, 1990, and 1995. These partitions were obtained using fuzzy $c$-means on measurements of the following variables: the annual per capita gross domestic product (GDP) in USD (converted to 1987 prices); the percentage of GDP provided by agriculture; the percentage of employees who worked in agriculture; and gross domestic investment, expressed as a percentage of the GDP. Table~5 in \cite{cluster:Gordon+Vichi:2001} gives 3-class consensus partitions obtained by applying their models 1, 2, and 3 and the approach in \cite{cluster:Sato+Sato:1994}. The partitions and consensus partitions are available in data sets \code{GVME} and \code{GVME\_Consensus}, respectively. We compare the results of \cite{cluster:Gordon+Vichi:2001} using GV1 dissimilarities (model 1) to ours as obtained by \code{cl\_consensus()} with method \code{"GV1"}. <<>>= data("GVME") GVME set.seed(1) m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 3, verbose = TRUE)) @ % This results in a soft partition with average squared GV1 dissimilarity (the criterion function to be optimized by the consensus partition) of <<>>= mean(cl_dissimilarity(GVME, m1, "GV1") ^ 2) @ % We compare this to the consensus solution given in \cite{cluster:Gordon+Vichi:2001}: <<>>= data("GVME_Consensus") m2 <- GVME_Consensus[["MF1/3"]] mean(cl_dissimilarity(GVME, m2, "GV1") ^ 2) table(CLUE = cl_class_ids(m1), GV2001 = cl_class_ids(m2)) @ % Interestingly, we are able to obtain a ``better'' solution, which however agrees with the one reported on the literature with respect to their nearest hard partitions. For the 2-class consensus partition, we obtain <<>>= set.seed(1) m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) @ which is slightly better than the solution reported in \cite{cluster:Gordon+Vichi:2001} <<>>= mean(cl_dissimilarity(GVME, m1, "GV1") ^ 2) m2 <- GVME_Consensus[["MF1/2"]] mean(cl_dissimilarity(GVME, m2, "GV1") ^ 2) @ but in fact agrees with it apart from rounding errors: <<>>= max(abs(cl_membership(m1) - cl_membership(m2))) @ It is interesting to compare these solutions to the Euclidean 2-class consensus partition for the GVME ensemble: <<>>= m3 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) @ This is markedly different from the GV1 consensus partition <<>>= table(GV1 = cl_class_ids(m1), Euclidean = cl_class_ids(m3)) @ with countries <<>>= rownames(m1)[cl_class_ids(m1) != cl_class_ids(m3)] @ % classified differently, being with the ``richer'' class for the GV1 and the ``poorer'' for the Euclidean consensus partition. (In fact, all these countries end up in the ``middle'' class for the 3-class GV1 consensus partition.) \subsection{Rosenberg-Kim kinship terms data} \cite{cluster:Rosenberg+Kim:1975} describe an experiment where perceived similarities of the kinship terms were obtained from six different ``sorting'' experiments. In one of these, 85 female undergraduates at Rutgers University were asked to sort 15 English terms into classes ``on the basis of some aspect of meaning''. These partitions were printed in \citet[Table~7.1]{cluster:Rosenberg:1982}. Comparison with the original data indicates that the partition data have the ``nephew'' and ``niece'' columns interchanged, which is corrected in data set \code{Kinship82}. \citet[Table~6]{cluster:Gordon+Vichi:2001} provide consensus partitions for these data based on their models 1--3 (available in data set \code{Kinship82\_Consensus}). We compare their results using co-membership dissimilarities (model 3) to ours as obtained by \code{cl\_consensus()} with method \code{"GV3"}. <<>>= data("Kinship82") Kinship82 set.seed(1) m1 <- cl_consensus(Kinship82, method = "GV3", control = list(k = 3, verbose = TRUE)) @ % This results in a soft partition with average co-membership dissimilarity (the criterion function to be optimized by the consensus partition) of <<>>= mean(cl_dissimilarity(Kinship82, m1, "comem") ^ 2) @ % Again, we compare this to the corresponding consensus solution given in \cite{cluster:Gordon+Vichi:2001}: <<>>= data("Kinship82_Consensus") m2 <- Kinship82_Consensus[["JMF"]] mean(cl_dissimilarity(Kinship82, m2, "comem") ^ 2) @ % Interestingly, again we obtain a (this time only ``slightly'') better solution, with <<>>= cl_dissimilarity(m1, m2, "comem") table(CLUE = cl_class_ids(m1), GV2001 = cl_class_ids(m2)) @ % indicating that the two solutions are reasonably close, even though <<>>= cl_fuzziness(cl_ensemble(m1, m2)) @ % shows that the solution found by \pkg{clue} is ``softer''. \subsection{Miller-Nicely consonant phoneme confusion data} \cite{cluster:Miller+Nicely:1955} obtained the data on the auditory confusions of 16 English consonant phonemes by exposing female subjects to a series of syllables consisting of one of the consonants followed by the vowel `a' under 17 different experimental conditions. Data set \code{Phonemes} provides consonant misclassification probabilities (i.e., similarities) obtained from aggregating the six so-called flat-noise conditions in which only the speech-to-noise ratio was varied into a single matrix of misclassification frequencies. These data are used in \cite{cluster:DeSoete:1986} as an illustration of the SUMT approach for finding least squares optimal fits to dissimilarities by ultrametrics. We can reproduce this analysis as follows. <<>>= data("Phonemes") d <- as.dist(1 - Phonemes) @ % (Note that the data set has the consonant misclassification probabilities, i.e., the similarities between the phonemes.) <<>>= u <- ls_fit_ultrametric(d, control = list(verbose = TRUE)) @ % This gives an ultrametric~$u$ for which Figure~\ref{fig:Phonemes} plots the corresponding dendrogram, ``basically'' reproducing Figure~1 in \cite{cluster:DeSoete:1986}. <>= plot(u) @ % \begin{figure} \centering <>= <> @ % \caption{Dendrogram for least squares fit to the Miller-Nicely consonant phoneme confusion data.} \label{fig:Phonemes} \end{figure} We can also compare the least squares fit obtained to that of other hierarchical clusterings of $d$, e.g.\ those obtained by \code{hclust()}. The ``optimal''~$u$ has Euclidean dissimilarity <<>>= round(cl_dissimilarity(d, u), 4) @ % to $d$. For the \code{hclust()} results, we get <<>>= hclust_methods <- c("ward", "single", "complete", "average", "mcquitty") hens <- cl_ensemble(list = lapply(hclust_methods, function(m) hclust(d, m))) names(hens) <- hclust_methods round(sapply(hens, cl_dissimilarity, d), 4) @ % which all exhibit greater Euclidean dissimilarity to $d$ than $u$. (We exclude methods \code{"median"} and \code{"centroid"} as these do not yield valid hierarchies.) We can also compare the ``structure'' of the different hierarchies, e.g.\ by looking at the rate of inversions between them: <<>>= ahens <- c(L2opt = cl_ensemble(u), hens) round(cl_dissimilarity(ahens, method = "gamma"), 2) @ % \section{Outlook} \label{sec:outlook} Package~\pkg{clue} was designed as an \emph{extensible} environment for computing on cluster ensembles. It currently provides basic data structures for representing partitions and hierarchies, and facilities for computing on these, including methods for measuring proximity and obtaining consensus and ``secondary'' clusterings. Many extensions to the available functionality are possible and in fact planned (some of these enhancements were already discussed in more detail in the course of this paper). \begin{itemize} \item Provide mechanisms to generate cluster ensembles based on reweighting (assuming base clusterers allowing for case weights) the data set. \item Explore recent advances (e.g., parallelized random search) in heuristics for solving the multi-dimensional assignment problem. \item Add support for \emph{additive trees} \citep[e.g.,][]{cluster:Barthelemy+Guenoche:1991}. \item Add heuristics for finding least squares fits based on iterative projection on convex sets of constraints, see e.g.\ \cite{cluster:Hubert+Arabie+Meulman:2006} and the accompanying MATLAB code available at \url{http://cda.psych.uiuc.edu/srpm_mfiles/} for using these methods (instead of SUMT approaches) to fit ultrametrics and additive trees to proximity data. \item Add an ``$L_1$ View''. Emphasis in \pkg{clue}, in particular for obtaining consensus clusterings, is on using Euclidean dissimilarities (based on suitable least squares distances); arguably, more ``robust'' consensus solutions should result from using Manhattan dissimilarities (based on absolute distances). Adding such functionality necessitates developing the corresponding structure theory for soft Manhattan median partitions. Minimizing average Manhattan dissimilarity between co-memberships and ultrametrics results in constrained $L_1$ approximation problems for the weighted medians of the co-memberships and ultrametrics, respectively, and could be approached by employing SUMTs analogous to the ones used for the $L_2$ approximations. \item Provide heuristics for obtaining \emph{hard} consensus partitions. \item Add facilities for tuning hyper-parameters (most prominently, the number of classes employed) and ``cluster validation'' of partitioning algorithms, as recently proposed by \cite{cluster:Roth+Lange+Braun:2002}, \cite{cluster:Lange+Roth+Braun:2004}, \cite{cluster:Dudoit+Fridlyand:2002}, and \cite{cluster:Tibshirani+Walther:2005}. \end{itemize} We are hoping to be able to provide many of these extensions in the near future. \subsubsection*{Acknowledgments} We are grateful to Walter B\"ohm for providing efficient C code for solving assignment problems. {\small \bibliographystyle{abbrvnat} \bibliography{cluster} } \end{document} clue/inst/doc/clue.R0000644000175100001440000001736415145302607014033 0ustar hornikusers### R code from vignette source 'clue.Rnw' ################################################### ### code chunk number 1: clue.Rnw:40-42 ################################################### options(width = 60) library("clue") ################################################### ### code chunk number 2: clue.Rnw:310-319 ################################################### cl_class_ids.glvq <- function(x) as.cl_class_ids(x$class_ids) is.cl_partition.glvq <- function(x) TRUE is.cl_hard_partition.glvq <- function(x) TRUE ################################################### ### code chunk number 3: Cassini-data (eval = FALSE) ################################################### ## data("Cassini") ## plot(Cassini$x, col = as.integer(Cassini$classes), ## xlab = "", ylab = "") ################################################### ### code chunk number 4: clue.Rnw:889-890 ################################################### data("Cassini") plot(Cassini$x, col = as.integer(Cassini$classes), xlab = "", ylab = "") ################################################### ### code chunk number 5: CKME (eval = FALSE) ################################################### ## data("CKME") ## plot(hclust(cl_dissimilarity(CKME)), labels = FALSE) ################################################### ### code chunk number 6: clue.Rnw:903-904 ################################################### data("CKME") plot(hclust(cl_dissimilarity(CKME)), labels = FALSE) ################################################### ### code chunk number 7: clue.Rnw:914-916 ################################################### m1 <- cl_medoid(CKME) table(Medoid = cl_class_ids(m1), "True Classes" = Cassini$classes) ################################################### ### code chunk number 8: Cassini-medoid (eval = FALSE) ################################################### ## plot(Cassini$x, col = cl_class_ids(m1), xlab = "", ylab = "") ################################################### ### code chunk number 9: clue.Rnw:924-925 ################################################### plot(Cassini$x, col = cl_class_ids(m1), xlab = "", ylab = "") ################################################### ### code chunk number 10: clue.Rnw:934-936 ################################################### set.seed(1234) m2 <- cl_consensus(CKME) ################################################### ### code chunk number 11: clue.Rnw:941-942 ################################################### table(Consensus = cl_class_ids(m2), "True Classes" = Cassini$classes) ################################################### ### code chunk number 12: Cassini-mean (eval = FALSE) ################################################### ## plot(Cassini$x, col = cl_class_ids(m2), xlab = "", ylab = "") ################################################### ### code chunk number 13: clue.Rnw:950-951 ################################################### plot(Cassini$x, col = cl_class_ids(m2), xlab = "", ylab = "") ################################################### ### code chunk number 14: clue.Rnw:984-989 ################################################### data("GVME") GVME set.seed(1) m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 3, verbose = TRUE)) ################################################### ### code chunk number 15: clue.Rnw:993-994 ################################################### mean(cl_dissimilarity(GVME, m1, "GV1") ^ 2) ################################################### ### code chunk number 16: clue.Rnw:998-1002 ################################################### data("GVME_Consensus") m2 <- GVME_Consensus[["MF1/3"]] mean(cl_dissimilarity(GVME, m2, "GV1") ^ 2) table(CLUE = cl_class_ids(m1), GV2001 = cl_class_ids(m2)) ################################################### ### code chunk number 17: clue.Rnw:1009-1012 ################################################### set.seed(1) m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) ################################################### ### code chunk number 18: clue.Rnw:1016-1019 ################################################### mean(cl_dissimilarity(GVME, m1, "GV1") ^ 2) m2 <- GVME_Consensus[["MF1/2"]] mean(cl_dissimilarity(GVME, m2, "GV1") ^ 2) ################################################### ### code chunk number 19: clue.Rnw:1022-1023 ################################################### max(abs(cl_membership(m1) - cl_membership(m2))) ################################################### ### code chunk number 20: clue.Rnw:1027-1029 ################################################### m3 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) ################################################### ### code chunk number 21: clue.Rnw:1032-1033 ################################################### table(GV1 = cl_class_ids(m1), Euclidean = cl_class_ids(m3)) ################################################### ### code chunk number 22: clue.Rnw:1036-1037 ################################################### rownames(m1)[cl_class_ids(m1) != cl_class_ids(m3)] ################################################### ### code chunk number 23: clue.Rnw:1061-1066 ################################################### data("Kinship82") Kinship82 set.seed(1) m1 <- cl_consensus(Kinship82, method = "GV3", control = list(k = 3, verbose = TRUE)) ################################################### ### code chunk number 24: clue.Rnw:1071-1072 ################################################### mean(cl_dissimilarity(Kinship82, m1, "comem") ^ 2) ################################################### ### code chunk number 25: clue.Rnw:1076-1079 ################################################### data("Kinship82_Consensus") m2 <- Kinship82_Consensus[["JMF"]] mean(cl_dissimilarity(Kinship82, m2, "comem") ^ 2) ################################################### ### code chunk number 26: clue.Rnw:1083-1085 ################################################### cl_dissimilarity(m1, m2, "comem") table(CLUE = cl_class_ids(m1), GV2001 = cl_class_ids(m2)) ################################################### ### code chunk number 27: clue.Rnw:1088-1089 ################################################### cl_fuzziness(cl_ensemble(m1, m2)) ################################################### ### code chunk number 28: clue.Rnw:1109-1111 ################################################### data("Phonemes") d <- as.dist(1 - Phonemes) ################################################### ### code chunk number 29: clue.Rnw:1115-1116 ################################################### u <- ls_fit_ultrametric(d, control = list(verbose = TRUE)) ################################################### ### code chunk number 30: Phonemes (eval = FALSE) ################################################### ## plot(u) ################################################### ### code chunk number 31: clue.Rnw:1126-1127 ################################################### plot(u) ################################################### ### code chunk number 32: clue.Rnw:1137-1138 ################################################### round(cl_dissimilarity(d, u), 4) ################################################### ### code chunk number 33: clue.Rnw:1141-1146 ################################################### hclust_methods <- c("ward", "single", "complete", "average", "mcquitty") hens <- cl_ensemble(list = lapply(hclust_methods, function(m) hclust(d, m))) names(hens) <- hclust_methods round(sapply(hens, cl_dissimilarity, d), 4) ################################################### ### code chunk number 34: clue.Rnw:1153-1155 ################################################### ahens <- c(L2opt = cl_ensemble(u), hens) round(cl_dissimilarity(ahens, method = "gamma"), 2) clue/inst/doc/clue.pdf0000644000175100001440000146765215145302607014415 0ustar hornikusers%PDF-1.5 % 1 0 obj << /Type /ObjStm /Length 5086 /Filter /FlateDecode /N 91 /First 772 >> stream x\isF~SS}*Kޗء D*$e;wQ,*W辷.^4*QJPJKUzW*XSJ PɀsRFJJ ޠ)Sq- /**k\q>T6JIo*%*gRut*eщm.B 8UDZ*FUEc#T=к "h =iW,7\4ҀԶ!-2 ijҸMI hYȖ$TFZ{1\2hY;K1hѲѢe@D Z6 h !VaR,ZFCGSѨZƠʡe1(`~*]@^bZ4i B _(p-ih9(<A Ǔqo?@pLΫ$5?9T88^ &|i6C{crʱ}L{-'|}ȃ}y}9Bir&/e9~x9n*ʮP!E>tnC3yz _fR` ʾ9.mҵe/˾uR-^a6K{gdCp0 ʔაo j;ReLevbwCh{UOyƢFJò2*e fLn_/ikXTbqn|ɛ'\D!*~dz''9:+9ğuKiҼI: =4yIx?pk8%D$F2m+FOR[ݨBPZ$k=%4DdMNI.R? z)4ᓓQ]!Ӯ.7 ʿ?LLNXl`U)`?i>)N=ט@+1|>cdf`@M_#9ߕYGLj2=}Н70eqvbf<)zf=%FYZiͿjTE[} rȽ@1 pť.YԦ(RHUd0 _>k(XPD2sH**ׁ?ՇaQE $>Y*R@ZI,+2Q&q#WdX#/ xq2iWeRoImF ZyIJ-Ԇ6,$8`vzLeާm" [C 6][P*TB Og ]ڍgE4H' fˣ% +,/OӬ??~KY0D@s8ӸR7I%L)ĜJYشRMyA# G5iOmQŖ=̣_"e1 ~pB=Ŕ]ea{&@vdrʐ' :6L\ۘJ&d@.HL!rԥvJĘ)WDMndQn`0î̵; Z\+q!~مFu-~U>8xL.k$1Yp$F $5gr_lҦ:]s},mYC\\H8҆g>ܙ:7GzRaÚ'>_tO/W!6@vljuHCqYkWhZ5Zx6޾d#qjG-=Pqx ]vm:q q%pah1 @zڧ'6]&'|*E@T\ 1Lcr2\UePGPYħ|O:?]΂sEZ60J9RJ,D%Jjb$*Q"@dFIYQ3zP5S;.YN;4rYK yHՂyiƎ)P'd.3qFhfo8i#B_BA5Z8%ػqROQP#F7#idpL 9Nuk(T~FtbMgc|ZWɢjf&C! yD̅!w)@pMh5ip)>\oV;?ܡu)ZP]ʴַYmit)ҥL F#-mnM6 X;@2um * ( !K iCK[sLYPگw\SkzdhS4H&5SEU :&jS )ek/-3*mM9S+X97pC~Wĺ6PDs. cHvNeЦ˯#kT]HNqK˦\,i3ڜ+ 2#ȫ ?MJ^3Yir-oT΅ksSneϯ׊Һ7.bܢ@?:KZRĴ"W'GV"W-7K޼0Kf. ߖiu5ݔU7Sϙ_HL&p~8K*< a?CJ_*"@ DiZwqlvτ~o?`ړgB[=)Е|aV(!O fi1/i5 ~^pbM|{ςeIX,:ziJ07-B?hH%λ{Ӌ=$JK8_ѻVBH#>(a[X!  $)4=^12MB-.4T tL>X|(MܥǏ?R6X=7uw;!MnckgҾaNF·u*DH|Zը?8κ~o?N- Y gL <2[dV&NӛR@K-LK(AMd ۽-栁w -p9E/qnjl H>IN@8zћR&5ae=k!7$IӒ4ª3m02JI-|3@uuA"`g̈́%pL eJ"rh=hDvi0;>cZZD5 Ւ;FGFoشׯ 7^&VZxL:!p>**i(DB0 LJ+gp?6%&!=0R;}{C!t[f6Dyx`[ }'r^fVЁ!p0;$_Q' pt6L8NLz5C [I6JI5 [ ;<N̈́ aj6ڳ0uw'Tjcr"blSƱHߦɆU/\S2wI鰖%bjjr-x¥dstream endobj 93 0 obj << /Subtype /XML /Type /Metadata /Length 1658 >> stream GPL Ghostscript 10.06.0 2026-02-18T10:25:27+01:00 2026-02-18T10:25:27+01:00 2026-02-18T10:25:27+01:00 LaTeX with hyperref endstream endobj 94 0 obj << /Type /ObjStm /Length 3313 /Filter /FlateDecode /N 90 /First 827 >> stream x[ko_1؎UؐpSM,62)Tjٝ%wHzD) y;sh`1YLFl3N39J!X[+[Ť;8a0iˤstϔ0RLSJ;)pI*zq F͖izv<3ad 2Sx3J2+"QY0B*e Q9Y*ǜ6㙳P暛Il=BxV+yczq{҆vGP{D{ J=%Q1H@ fY1EA5;vt$!-Fv{@(bR/#iD/ĨDZIW=qqLxDXS>YA2X?HY- :Y H9KrGWzbLy xD DyRIHĒq4D1A: dB2xG0I62} 8/_~ɪgVͮ+r$YdO5^].fzɾ*=u{lsϛJwaJ₽egƠ}f_ggBZ 8hts|U҈P*a,K8pe- IanlpムcXze%SHf;Cìü;3%PF[Guȥ,vO; AQ ˒}}fb("T;%}AG)CnUIn j1UJ.i6 (@Sֵh7!>mj[~OjII6!fڭo6rQlemt p e}:<]|8Cn%m<) ~v =.h-'5R$Yx=y_of~Y"D˵i # и\^&]!8`t:ZHt-%ѵ$:_ 9.䘒+TB:؟m~:2mna~Զ q\ǹ>o&46^[0ɶ`m ٖ|/ܝp'+5(/x0F`JVe{idY'ˣ_|ד(ϧ=~̗͟DJ|'W n$ӫjqȺ|n"w$9/Y45$/9QS4%#lV52>KQ74K۷-}:z6|QޟCJV=Uϫ﫟wU]UՇj^-jY>O|!I}K8I4]&׿m.juoV= BlyIL#T')4? zJgѷ?|.zC{1{Qrd/~ĜYJk8NV6h-H7S?J!++t#:iY=,в^TUBuWlZ8^>VjEu]Z Mֆ^Ƅ`Ԧimuֆz~}VNJbN%k ]Yo#Qz2J q4HG t}nsp,#7lZÈր5րzd ˓d}O `=yd &&lD` ˽;$AYTov?2nN괾jdS÷G@"rD,@/ ij:AXK{G 1e̩z<],qmT[СAIyl0$h UМ=.:4"C %j28%}4igoƀnhœ5 w F4;G]5}VF iǴdD!zXZ#-cq qD 6VrVm Hs--<}Yt;%X|;6Y8nH95DyV}ZiuPcD@Z? 9ZB~(( \:a!<h'?L+cyQ_^fO;QwZB0 5픧)ϼ.S6SwHFwF9ACFSg(x`ږ!OܻcP~;rS F, '2hhydV=hO{Д dС@YqF4+*z/Ro1ù/vͰx2`endstream endobj 185 0 obj << /Type /ObjStm /Length 4032 /Filter /FlateDecode /N 91 /First 857 >> stream x\[o7~_-~-l7SNIy婭D\InGe$˲2!+ɨd*52k"Jx^$X,.Ē%~Kb)? `ԤXm0Zzë [s0)2)jhDm1-|ɵՂ D wJE&ނֹh2@C{M%#hIcTFz$80iL ^Y&)z}B 4&t  i11\*Gi1ẙᢤgJ=ᥧhxPu o @;Vg@G@@}"^h?~`8fKv1^_M|}p0Jq\]:O^9҇'[qZn5fDdNFY5AC&^W&hQ.+. 5k,T_,ҽXmvbi'vbi'vlaHQ` o o -`,2 -Ҟ-ҞM-`? Nn1 q???;}yrtx6/z,2h<9ꌠ;j?Uw3 ɵ^tJУ|ie|zfC\[TkbrE;hiugё|ԟ&=_Nѵ~"!a~t0.wS25(<^ +'=Rz#t F.QwZ`w U bAsAhx!/D7o:\tEwx}O:UI5=A|}ѯcq-o@܈j^?j(bĭS%>})1zw\f}ׯ #S[C^v%M:^wopٯPSc`6(ϧ8T!#1 ܊e.YrI$tHEZD"GJ@>>ȟ3~4CẌ́+Rl#!J6d2Et2!s6>y,$7ؽCMoҮ_*O,3.s/3b/^?govЋ茲vep![]Ejzq攑-&h.z~ٹQ=N,/8,ֆsCG,Mo{^lp-UuB5""ZOmVqN1&{sa }=kk!X躕;n^A}߼kD4SFa}nCrDn\Wqr'=9t?V~)3pSqS߹PF;P %r#T7qӿըĤ׿ 'j4EnUE) L߯.Iѫ?ٻiTdW\ؑ"׉Lq7e(f FX{N_-E]Ҝ>0hyygrF|5~UÃkX{z)"}_Y&NPb3~l+"ж)*l/͛__E{?uY4{l׀9kʬ)MN΃rم[Ԁ1@"\MUfԻfݱJRF$R hil-qptQJpr ߐ~;Aa*ofHg2c~j?_ Nr2GsDs\*qJֽAӋcgN|\ΰMGVpg冲V("q8ssAܝBsZQV5e6eʝ2%3=}^/7rФNQ,%يC Y].ugD}}-3Uge)I__K{WXK\ݖɵ_5olMl墣1_ke5Sc%T ` fQ\^Ǽ_.2%zK~2Gۜ@n>7vSX݄~:$:C\CCքV<6;YvEf]YWduEf]YWdEf}Y_-$|mIv7Gnf2䴐x w<]@;cjX?wKsRƝ8'nu0ohd@~GPZsP*5}rgs_ w ÿpĕܹۧu[FQ8%D&q{0.(DXaܝ*]oA2fZ'鼯j)hϻf"؃h*xA&ZR .b0LVk3Fqr w̵f3GdW wc(ݯFZ9mineӦolU*R˓ S4ɪJCLiaFqwuw5Aqk>,]%+nwMs&G^{vxzPf&Mvn˙ĝkIlR{m݄ 3v8$Vr]R*~nهY04j֐WZJF\Q mw9Dm4|8oV!0AɴڳVYBk^S%)Q%mO~it#Bk3rv\27 Qʼ6\MyZ0ů9!xHv1F?<4j@G֒{p隡|Z؂=Tnhb┄|{{ِ1hZKK^M&7tMxuq+:~%TЊ+aLdKj+x&7roiqa\9.,{#Y+(>{B:%uGPg6u6t%?f4#ph=EJmL" "6x6MHv%ߎ^4 ƽ3A[ . N{`\ R0v |QJ.m9K274ۉHmWmlm!%*ho!As:孁FIyPLEAKD,-^y bP&P'&BFE'NqnKi\ ^-x䘌ZgK<Qu6~z8ZPC Ụwj6/k=ୂ$% hkָ ݓkX5U5!._*/>õpAPn#=΋KEU/H맖5utwqL=9iKU2G#tt-#/Pewiuo]v$Cvۃ5^n=_#$[=z%6bM#<[K'ݲCާ(hGr.m0aa @#b%)*m5q3QPT&*sH}Շ %׷fpX93ykf>@=Yي#?Zh? E~c"mO*@;cO5hS7`aFk+wI v O:1 pr5;?8Y}f0{n/Сe=l3s9vw͜$Q{C+endstream endobj 277 0 obj << /Type /ObjStm /Length 3180 /Filter /FlateDecode /N 91 /First 845 >> stream x[r7}߯n[ZJD\NK遦+i l-QrɃ`}ccZؘYؤe'#Phv 瀜F4 JȱDHϸq=xAFḀ3eAQ <>LЇ5@KK7<Bk ]xף,pf@ A *Ȯ}` %Ln(Cl Ǡ^@agg,}Ї˚xk{#|PO&b.~+qgl:Y4 >NtY #^7h/Q~/Vx^sj^mRz5nf0yca,ruXa9~;|wt0oog^B}=NOG3ތ&O&ꇣlxv>am_5ltΊ*/@Jԫ=0L`׉opތN<,ZZv{[~Yaӳ{͐tL}'KٜYfz6|ތq x]XHgkuUߩ RWQj0Flxuުf1n-[UC5^\ ԩ:ǃjTN5\ ƪy?.;r٬,:W]75R7P5UI.%}魔ھ.hz.WsjhNߎL|6*O[`mG9nNgUhrJ礌5;_~L20GZKi1s@d2,IDvFp#r20AZԓh{(ƒaС Fj{t 𠾥өx*<(͏ 2,$}N^Dom$91},\y /@h ^$IHLS2o[izhp20%#mlS#rA܁^3ޕH2],u@S ع&wհMeߊΑ1{0{;՝7X AQ${bCӮ$5&f .ccyofI fܴ oZ5^} A"誖=(]g2Ʉ+ qRl2CP;Ao#kijh=|G6IÊt蜶po72^ZHðۣ[: վ`aA`y::fJSio_<'>0ne-1GF vC@{A{C}Y好Y[ cL CVq5kUj9^k{WD5tI5H5F5@LrG=Qm6Ikw3.aқ&5ئws[%6xZzLhxy.@md9&o;-=6dS9gBAfluHGp|t=K4/b-x:Lb7֔  z 0 $|^X@0T-> >8:Wlr\sKnJ]}aZ(~w_tn|OS #(`2€{Y͇ iͬ9M;#xY-ʘv@u2Ϡc[z lM6c\IWn/)3X)%8hn#%˵9O0G˱`z8M:~j ]ԜP^qN<tpo΁}sM[Nu_#־F}X5bkpb9d.PŮg^ƯrvvivkG|^j-csښ[c9 s.g wJz~jS>f1XOք5  n 7}r-]k^[έjz5?|?FI.,6^ 6\i-bA㏲U(UN!'_ 0Ù637x~Į\p4˟I ]-۳(SN|\⤪8 -ЙrFWChe=-OJ>.Azb t{KUcfHmqdF_5od ]RA@DǁpꌷK7DO|ռC^k5T*a JXC%֋^bzƚڑtV\NxiX%. R]4/IPxNh}pW2YdFd#ۓi䀊5C=D}PwetE{Uu:JSۮCiǁn9w6fc+ c{ݻ5o3enZDsSXaendstream endobj 369 0 obj << /Type /ObjStm /Length 2660 /Filter /FlateDecode /N 91 /First 840 >> stream x[n$}W 8b m8 kg %B`9=#m%ԣуbTKp"N[U]J*kIX8MdlӜ658Yp^4ܨSAXU5I7i5:~6.,aІw-O-: m̙F|XlegiY*ӚKӱ\ hA p]ʙm̥*l\jmn˘!lmˉX$\F\nx2L6)1W( J26.pS] U)ep5BPW$Llp2$ +* k ZT6nQ6Q\,kP">Ï`u1~vD1B=BL ,!v j8axy'LqXB3! 0R 04Poʻ̻;FT a | G chH06;:(1is?;-$r jjxZ]\_zɠk׶JWWد֯_{ޟ?iOX]/W@mw?/?~wKvB%q_/Ʒxwyq~y.~]~F~>/կK<~%&c;?vq.wL=g:r[3\_faqlƝqsMOI片+uAckV`W7>nG?'p 6P+u2-#zտGpDWw+&akyU(ړR {Qxlt][ԩha .'s6j>o~p;.L5!&۬k@ۑI>Du) ~ud٥AذI£{ i`|~m|dx)-x_%֨]sWU ҂HJ aShL3o6 <0/69䓁` Gxk'F!#| &gP6i6zɟ21 4QT0m#8f #]{\ 3M稃 ]WGgnPȑngNdޗZDh L6:!%#ָe /N )pЌ3ʭkRJλaG|(Aް8ti*zFc)'y3<lxӰKyIχl툍 /}s9-W2yYRi" @3qn$= NeJF𥦳r'SpalЁ Gv:ZÁ+l+AWPGtu~pQpX3G \֤'Zt dž,pxbCiR41ܴ̪"ptcpV6#M _s\#%[x1?`mwٔ/ej[);P>b$PK1) A[;g&;y@D0axTbfO@K SIC*q/xSf%:vy̓F?\^|޴y>~Zq>Z5>?;EŇկCi0F&Fކ_ ! rrJ? i!҄@APEO}1xʔtR >45niOkb^D*L?#*L# 5>ɭ[tD]O8g-FVz%<棤ᰧ21W+1ycJۚ^ SIkƺ^sUr1vlt(%yp!2ؐ0lSfϪX0;a]%+ǻ^xavKpH15)V_@ W{dhհ8RH#:ԫ@SM@LTV@ऱLfep3"9|~t1͈!ѐf9T x9đ2$#:Cy?t;M c紩~18S/.U(S'ۀKc ұWAvp<͎n!gT6YIZR,rz"fGލV.ٗH_ˤK!oeų@u2C+CnhñMC#Z> stream x}YKw{Y -K%$m4M-31ϯ}$5$\wcSp)6?n$}ݤ曻bKWҚ Kqu(7w۝jwEvW + (k}ޤ0~9];V9lxhpL+'K+ݏȝH*k|{˰;[qя@_9[z R=\+=kHR(wkw#f9RQ/$KW1mw; *]/d7+>x)"+ Iˉic5MׇYs$S^LC|Jק҈60T .bj1鋴"'%aI/jOhْtIυSS*RXK;~G}@ڀ;aka#SW!+!Ѓ@ @ DM: 01sR?N5" ӴK,^lOHbטehUiWm}^CqH3Ft)poUjCI9f)naQYz<hjکE*q r:SJ pq/ZXPJ'TXL:!wc_.^;*MyG~u:n"k{łhOex1vXoqGI|-`qC08iI~q%Vԯ^RǼ(b{e (b`|B҉;/OArYHdQ)E p,uN/A7@??[P61`>Tx﬘ŖGWX}H SϠ3'-d[:q_K|GH`%x0Qʋ!K s#N@+ SGzeg/\$Ⱦ' 1s 9T.zABTu@ʜ"Ooe;PJ%_jdrπ(K$D P^g\ )o]SC0p7H#L^ !; 0#~K.t`PC˸ vO%$&p#8DLr?brA$Qe %cE|ԑS2{lv'F#- \uXcʬ2\rBabAm. Z_Y'1_3B 3 n|tfi9g1E|*iᕋJXkd.J'^JL~4WoG,(xS1]&#"EMsnɣ_SR 閯Kh\KV#jG@T[J +:e&h™Kٹ45UDE+^o^L5w~M vsdK 9 "lL ry#kESsE MaF%`ϡfm{ ٭χ2 UjN,\$H Tq J:@AS]`%:E^x&0u&>8P-~즾e3jPHURC0VCU hAH1>7f2^zKB}*hI1++CơJ^|[qB\!-F8Bj.0X[VPSMC5k9)RCe KCr͡(KꞦ9)w$()UUq ŭ)J!T\] 0jƙ%j."I˜fQGir$ڪC0`3螘'njK R=l=4*"o*Ng멋׀l5|XeW'm|\]y-{M 2eC.NeA~*?Ni{7bVB.ˆZ+Ds }}?h 9laMٔGKÔt\:`TsPV)exN%q\'oΏ|*n95C=N=`TשTx{usy*>/\ ј13Q(1 VQ[o n t+m՗'.T֥EG \%:>|E U^珪JtfHRn!O&|tK߲Ne Ni;wu(N8P1NU(e91`?KZUpөjzxgԌm6ehEa|鉝YsJ|p.@4TJvIR8Yp%`IƬ;L" %7_#u`qrs'. 8i'+NqmZI4dnhi`bmG`&ZFKug:LCAN[cq"lEygxnrYTs7 d"1N(Hk}. <`B)UJgEF֭:5yA3ٺoLCpfy=&m"k( I4M@aiUOqB?.#i{~!ߦrC|3ce ϜbuĹ+^&5b¡.%T4gC\J;iḡ{oχ.^8".):0/|"yi+R|]5Wy\[e@,u}7#+H MF ZG`;Ao0U[ ,ЇDR8@ک=ƾyEސe*Mu $Zw> stream x[Korޟ& < I6HE  º0J=y8c_A3"Q]JuSCu6 e>|F٦.q7?Z_rmoE *~[i_]ѝx-B(i)O}MYa2lhh_P=?qySCܬ~OV&}bn?DҘZ:^Nvi4'Gh4svku =5f09%p7+GB7q8 LJK{Z937ȲLQRյIO` _hnU`F[ iVkYQ pEJ,=(G,3V[o h\mj[lγ?mY0-O ʋh(8ߟKU0Ny,'ǂE|,mFU??=S*:vşWUHP_.xE˚8U֘ ^Prq #?Mݩ=mVP4,{jXjJ;Q@ 46fwݩ?<MPU8ޯSTnsҫxs$p>vCZ{CC陼 3/@jϴEWVފ̛!7v`aK9g_EcKqښ}vײb@n*"C)rC>ß?93eJ7`Tބ胶̗a>ا2zÉr0aA T8,?B^Y\U6r D2qDQ0'Rϙv趞vtLa1 rƸpՅXCR i.ˣx;GKEwb)&;ʩx}k_ЩK^'E)+YXT*ݐS׆A)rlP~'"%c EP';.=jR8Vqy=a䬇URkEUNNzćK7K1@Zv\S\DBCBs%qXiך5ݡCP0ٟh]ǪYj=SS1!|:cI鷄ò6=2xҁ25b&8Z&[zjOP=` <%oM[Ժ{.6ej w~욗QR Ta V" ~{"Y.[-B>C{n{4CK{QΖR#22.Ċ-eა`F K*j߰GN %iwGBM:4@ ȯׄ2&/ ̚oGK0cw%U]ʇrG2P~:>tRy*QIs+SJUل&72r+b. ,qp;+,Ȅv4WdB.SwﹿG^o,/ﳊkbhy|Pnx/U{؝PB䔤V G;62. cĴjFvYTY7XXݢkLVϴz%- ,xX6rD0uDVFoHd%X&@4D3B@O\/* Mu)m͹yQdZ*|A%*X?҉s_E:HZT׼5k׿Zje#,så>Ն}TjtrfFd@2G j&ʳ35`c}oTCMx&v/Gubʞ~kIQ*݌{>uʃs,kʖH=Q'|Q9?5s]ek7L4LWk U[)vcCH'a#_^ʹɔqwwR6}=̋$U4c{j/ @nHΙYy&QZzzOuۈyrMSur7:謁+3 = Fuf7ku٤19<&k¨^7^Oá&UJyO]EVdZGEH} GgiO\&_1W jxt*ݡ>'+JU\&2$.1p3JSZ+3XVzO0ۓ{ALv]$!Cl|ž)ּm&5_ȟ"~i-w^jl"^ϔ "n62(T6eqk9ZsBf싄tg$r9vgGVe"QUn`\b5_V%F٥5UGlE62f`ϷvK@+ *勡czC}u~f]G.ty ̬F" =O#fbkE O_F_,*(F䱳0=NogeB|( Ikv 1endstream endobj 463 0 obj << /Filter /FlateDecode /Length 635 >> stream x]=n@{7}?6 lc7.I.@KC)B>3#;EG`4?<[qLڭ[|mԺS{=m6nlÁ}t4]Sk:Uy􀣜?LP53W bAjOćA|`|GƩj\5U bc\"N"qāT "!U 1/R(T(Y5"B"B2V h 2Q D&*#De2T"@d2RLTF*He 2Q D&*թjOhԂC-0`lЀc \-8[pȹ9S!t :\NAK)s :r.AC%tȹ@2@` d2 BCA`2 A!! d0 d2@` B-[4j!BP r!`T )ߤo5M| הo7M&\SI߄k7pM&})ߤo5mƋ6n> stream xz xSպ.{ofh A&QT@@B@Ki6i&35ʜii6i:C< fEѣp=J }viݽ[;ۇ fi3M >Ccoa``(`ĐC>!!' 2c"q”1S~Aq?j …ig,E/ܱ,kgdvV^&qm: :a3^c0WOOyEo4 A[tgw>7KcǍi}7~ /}B݄[&xuRI|򟧼2SG=d>yz6:m SdJLQKv)4Te@TĂL:y-0o~LrQ`'=*uu)0sQu~f.Q;RV:b5\[}ٛN-\_"$StL.ٳwa&bjBZag4vdYo@*)|Y4WjIJuk,j FʽQ)\5lñK.;s[#X>bq B='|'9piEe2sfi8ZOtz@)>MЬ8=wƤ7WO@VX˅B@0p8 \C׼%r{k[Ne#Z:EIsgXͰ:z4 Gvu`"kWobs<亾8uEoS~EhQ)nxUqcӏv5g&Ǥ6F<8~Jz w'[ v`(P`YE+W/紜Wu2ȕ8deL&ۍ L)Wh=vp'k2@u~좯G"!'~ {`gDi6 M p*W0)MiVZQ*JJ>n^D]ݏz=cVB]f)Kv )A苤ق)iLoոRO`7UXց`Su^NC!#lfX~֯͸~P{}lH D➡`R5oo7D7(PǩKuNo (V<уgq8+& eyI㭭_g&woJZ5M&NA2Hh]]İ,<:,a|RΦ[-ǩ>߸_Y t,eXnZj8dq:<Vi as{HR_9pɃp\h Q*MOgGe4tD@BgY6 X$ɵ Y WGSG8^AW5~+>ı pN (",>CHдxJI3K兪byH~β-;} EB!ਗ਼UgN {L# Vh5X"[w|(=-֗芃[?#XWp7,6oU>bUR^s("nߺe|}0JdFU-jREJ+ՙ:1#݆J /Oͳ\XK%07_sFZY`7.b7& |s+M.rz*O])sWp^qMjr?a?^Sy G( uwzТ+OucKZQ5B굴lr 5uem?,-$Kፇ`t׍6i;oU.tͩ%Np>yL׮wLAdXlf6h8i\H4** ՜w'e_vO5GW3rry BL"605-7 vXVj6~ F(^_Y3%c, XxWtKd)(5`x׬ږW% 2 W\Nyg[ypyTp0tL%o\;ߛ[q-fu(>k=/$ EFѵ#X)y FH@$]sIGEE> &rVq~4FR~TדRo׍vI 襸X۸O#aRƒI,zW?uƈ݁h j+*4W5~cn1u<MSZOLb%Rhq~)?\~Ⱕ^brҔcϵ:Ժ79GChZdbR[~(?X6~@Hd,O[!U䯵6 Ww(ɶWD5dIeǤLGJBD>khRH]͆GD8H0=½ (} ^`mv4fEr9It5S1,ܐ(L;7pwTEx1n'?x?6P?|ȩo3&Kx *PjEƣE+2fK|ɮ`ʥ31*ŹlM- 8VioKEn 8-Zַl(Ke ]L?%Ɯ8[?޸ƪko mu9zh#.E <\"ɓ\B.+r4#gdRL*`jgjdo^wDU)naEjVY (y9YWnYFPaM%U(U%*wei5N]k-*{>P\QDON&ExŮ'8Xupձ1d牱ylq(7֑i fI(pոhܿ jF.5?( ?kfpyLVؚ)񂤝;@ Io/Ć0w\s3~nVeP(cߧ>#=tG|WA y7J kk#dx1?,[@8][]0cʷ˰$bDdnZiC%,nSS,'u>si^euD9c)UW~eܭBoĒ6A9u=3@@fjufx1+@4"z Yų-\%nǺ&[Ϥmݖې8r ðZFalZڦ JC=hBo'qXKa.djڲ8*v⑧chI4a 100s"\z. #JkEΎ$U$۴cIRb<? ]VHѭ8*q(; fúVyp2Tb &C=Q.EنAXI \Ă4JSJȇT2ÿA[zuˎ%qf%wјgڸ8*)6"&eu@ʣ:N*pC?;Ab|zh0WwWYPs!R0 Nѳh n |* ̴Mm'ə)рV[23:A;mf>^\e7$}0=')0zټEK$hVN*858xau&FpBF‡٥T|8-anb'++u^hXIY x6ͅL6u+tnWW!( b $|w}JvL3ϛM$v. 1< :棂BsAjgzJ*</ܺ$Qgbl³` Oc-ͼ p ?@I4b2 zg-sj49A#]߶"{^ #O_BP;1\t2Hl=%$emeu@4j<3L0gԁzu0hdŚrUП|q&^~@5T|S]r7bBVQ$ $J*gL3_]3mW鎺t.Y:mԑþB=(`;KK'\3ޣ!:Vt N]T(,pg39ZC?1=L@Sߧ}hunO`-QrAn]nmZ` X\>?/X뇇"~JY4%.km<Ӹ1C•feI2&8jQuZVwƕo꾻|PV=na1U]PT|H~S&LA /\O~~ʅzz+Q;( ZJjP`&yF$JNcld^ڗVEӼ!'gCR+\֞~@  ܌4KuY '*0. *S6EV<ʮ)$24QU l^,`'L:v&w为Sԝ^ @gs|%Qf3R;PLq&D/N}Qf3Ȑ2u)l@ͅnPA]i8ҵٙ U3>%P1Onx&[34|=dlǒTvgNU|Pʙ$^` i˺i$ςJ4[9јDc37 X*:ylFH&g`B,iqK%_-V#HLu.oV?mi]c:l3[,VKm%{Rk%Rvgd/Z4X.ҁGH#z$fm+R |k!sJ? akԇǬݽ-r}Ni S'PiqxxM;h΂t-ѡ~DBOvR>]rf{fM}m4xWU Œ"Ud;V}V]AٖJQKu9'sG'᳞ʏމ|Pf[ڠj*¼u ވr9bUh)|i|}3cE([uAqN_#XKsIBY/| gmx?2fX"= th-Hq!fӖc{B*A)q3-9{PX3ۊºK9"ȠʌZKq|S.: qLÈ$i&h)dkksgp|^M; ZN(N℄'=qQ\֩,8&V?r*nPt/2[ _֩tj ߎFĠK]#!): muG _GCCwBԱ]ηfs/[`튵VרX.,[ݰ9N+@>dOq N:s܉Gϟ5I;(>;cUU̚D5L&D@2\ZC޼ܙc~klߞU}+esgrwI4hlz0ɺq S_ZQs$kAK7k*^lN2Y!yn]W!:wqmo [^NVUzU|NN0c0>=| k;tpkSR d?SN#/=v4f YoWpqz?u~uH?ȓ}1K-`^wF\-xnL=0 Ჾ6e%$0UyT^]D^뺎XV+FJg՘@*.)gClwafTM:R4jF(Q,N0}e\#?Y3˖Es3e",;zxUA"\ 9#;h+SbeyiYVnoe1S t3kxi]hAj/k&ju@e~ϐ{7=G/5zfbx!h23@*NXorVe֍S4٩ղK/5j+5b_Rs-入[-^x f6\HS fe%%U%voY*)5?:ɚvn҉҅2QZ\Z ^'UeJ*bNx/֪pm.n[ //ܰ%~[''K\KݑAt@;9;;*j60S?;Ș&)8ܦH<~>}X ye2~5%Ҽ@MEɓyrt\4y@\ic|uuvb ׁ=eIR&cvJtLѲj?c2LGz(K`grv6uNSMM\)|Vr֖rG}\]{gm%H#@Rh4 n^QujQN tZySYQ˘ sVN@7"u4GQ}39(eřK4e)mjKiusi+>+)*,Y\KU>}r*B%XHJS9˘mɱzԗ [Q6JO^KeF[Rn1' ^#삑C !`Gӏ,yB`}_aC=n GA3jl=ˣفl]H*MJJLIJ?hUA6x]շ@H݊ úu:,Oh[S1K8KAcm.W-˒)Z/aXlqH`BFW ԩujVZ0I0L֒=Ha-vMeV=g<,6⃗hFN6@IVD]xiב+pzyepf]'MhiDuDd΀3VzRggMBK! DP gVDK󻌝.z0#Zlu49K-q5L N>{ Zl>*ɑf+̖=6nW,qM G~<drqMMYʘ9i- Гѐ(&7( αޮ%Ds}u~PUղَR 6&N>Ǥ b^8_AϺ^8_ݺߣ6wВՑZ\u|thPT:q&HR̮ТvhJz0?p~Onzendstream endobj 465 0 obj << /Filter /FlateDecode /Length 330 >> stream x]n@{7kK6N"Q8" "oٱ"ŜN3mq:Ӵ:ϸ㔆5^b)e*=ktKV^g96o%m[~(C.]kcv,K;e1 >U~|lwF%V`er֎Q!6(`x0 xp"ٱIK֍1c<Dh$n$bP(:HJ|,Fl?C/endstream endobj 466 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 3245 >> stream xmy\Sgo^R!7 .[bkۙqCQ(odd;BW¢qZucL=aH?F׸x!w62 rrJ@zFfֲYLlp\w$&ւ ̬f03Y:f=l`^g|M?2>f)yYάdUHwZ2)CA[5N}g8ourQXW}=p]_<W;~ S&?$/Z{p_{D+ CM$[}yxr:>y,tirS9]-.D(qff]vi"AN&{/^ XںLO p%\p&\\4 ~> inkp|qQ\'.0ڤ،8ݙִNSIBl>˽G YHVVY NJGIԝlMgpC9rGą;/{:D@dkaU5ЇCsP-N|,\ <{tSQ!>zpO"׬8I4;~}HxKVE6d'>JT}g[;f X9G>$r|Fc6l ݄ݣA-XQqPْMajOcD}p\kS!AsR 1?S#Fl9/RY_hbǂ6:"Z3J{P! Ϟt6d^3Dw+q ˉƷew6YN#BZt%SfO{C|Sґrp;,e"6դTeN^w`/n wICjԻI/ςs**tyCؙ8>w#;a5oĮo5 (P*,JPre5u#o6 >AGD8ۃyIedȊIET7qp7]!ŲP+|NV,dS8鸎}Jj_.@!_aSC"#,=5qOeU .Ei >pjmkR'KI[2z/p1Qn9Zc\ ?c.: 'F(N6ܭҏs(?3.9!s$4ov#k(Ⱥx>Nڛp|iZ6,=;ܼ~%2f[-svIq=aWl [D[N>Bq*='GM`_#c+AC T%gҤr(/-S)SI.[klD}Iuq]- !E%T}) ɝ|{|LI55`K\hdX|Z%+_i#gRTe/+ڼ5c3pm.7kZ[Ufsbx+Ct.[ٖFF؎at*L" k _\A{~JTYzc:z$8|J+H讪<5:,%  ҫ0ik"qmf\!h) ̒Q} dNH@(7~z**3si >-zcf-szǙ_|ًܵ37QIzڗT}hҠH1=0R%R /npqwH!O&H _}u| 8 g"/7Ά߂w1APRVuĞ 4MЬnr4b5SP/Wvmy25tO3ZWT#5q:ļ9'7/ɀtHJ[?ݰ`Q{AG`$3( vOҁ84 󯏒zQ1iۺ_Zյ~ ~:oIJ[jZjt2\ނ={)-I۶SyFP Je qi9teIuS uP}љ!pwe\+@"`O.ro/`7;i8wjsHjrFz˿c_{7cbܚB "/HÞąX8Slͱamt{QӃS<ڼ-g'ZCcW QC qMuYHėҶOfN{]\wjgL}ɩZzyK*vwztj>Yb#A2ά)jYPW3GѱJK/~$^G. L:r~et ͈z?9S'/sr `]_kIXg 0:+ӊ?lX&?6Y.v ΠljBd$Ǜ#si[>wA+֯]ncBQB1maGM]ynC d3 D]='0¡'|v jiiIiF.>dۻssq Ľ+%.Ӈ 2/Pm2A:WVmSDi5sMqSW)ޘSgulWU E:BG+:^_z7j⋗-N;׽D3{[[gRjGStw6d7%h%^2llzUu>].*"Nd,EIRA)w(G^w!_Q;SÉf#_Pۥ43KTq E]_OBendstream endobj 467 0 obj << /Filter /FlateDecode /Length 336 >> stream x]1n@E{7 YӐ"QfF.0"ϟHGzVovu?ײX+_a%_η%TAE{9lÝ߻S>~(|n: a"O_~0> stream xytSeH7 <&$MҔl4i !`ݬ7lɲorjj˒lɶX$@b &4&tΤv\17g2ܿ#=}["guK7o^9S v;n}~ký0>(>_;.RR2*e}C+um-UVD_oW,T4]~gCu-?VV=&Z'mm=%$Z(,z]EHT%Z,-!Z*Z&zABR4MEɒJ>rtX#yErS޷i_{?t:oЌ3Ce=JGDӾTdMN)!ˑu*_4o] qG<F5av 4Y[2R9hN 1}i?+z3O_^2_),ЎUkh8{u]ՠX 0; HﹾӉsl&mÕ6Ӿ+oOG.^X)~l|Tx2/F^}Orl6h>[9qKC ;?9w`''>޻EMV~/VTEn5<xᅕA ?2W/ye)A;)8;7|7$~V+w!TYbSfV^UvrXG>ШT6;izx t?nt.t..Ivx`=Z_k 6c9QzMiXX7 YA2?@gX6Ձz y~$|qw=R..[inMVSll!ohD eѨ,Ov{NHDwTr >oSn/؃/HvX[ve6-b;岃sI<1󡃖iK'i,&.;y|/$ M0 "2z7CwupA`aSl/m'878]Ii6 u]N(3v;&<#p'O|H3=)-jG3#&O bi\l\{22Гa\ (ƺ4{"x2pCE |6lݳmw\&Ġ֫- $nlc_r+ˊA2e3 3'ӧ&}s{9:tv\r{V6Qq1=WlzB?s|Q*=UZ4%윓"G<>> r&\kC BĮU'*Q *L6zswߌ4ohRl[a!hU#9@c)xd}74:9t&+$= |T AŦ L$L$II)FãbδѬp-(jr'8ڬTu8裖`v9P,L}ΚaK<áܾ6Ymh'Jڢ[>߹*ΦmNlv++LA +x`ի /Vqǹ^@ԏwebUbj%lho|H],4#~.AQW;%F_#{cDPi&H;Þ0 JNUy@5́ETcjqle!w!8'=)f"&C m]6K;D%v:~fۉd&^/+[{黒CVbzЇb*ֵ~D7Nvh$Gj&*(}[=Yv7I\WSi3Zdy_>9}~6qH"V2/"۠~{Kbgu}[7+7&@#PG"ҹtnab2=tq& &J&yRT(zDӼ=X'c(rw%Lj_4c2.̼ٴ6bgq7/lkgKݍ&.RՂ5]6#i@krJ#beݱL:XEŽt'rRϫOrݶ=_ܟN1pL-k{;ʿ+rABMv 6o`/2?z D{SOT.l|n_NN.<:afn*园[r=L_ٷPSܥ%Hە&jNs'm~܉G'6WB$Kˎu!5Ck&Bl/  )n=N#Sv m+Z ^{qg\{Q+$@*ʴ9 Qa|:)movo(Wu~?ȱ*@3FfMy T6VuG r~Mom1"6~QJu1~EwlիꟇ``## ב(Lp'd0 G. ꢵb63Gʼn,m mVxATUcUOgz[^_ЈKsdVgaA\acS~9~a=dfsXHmŀќ:r4t#~$G}}|K~^If e__ML}-OKU<=Ey e1I9¢BȥV 2\O,oG0qP&kum/y$>c ~ƨ`gEl0B!š#ܑan4pA*jZ/s01-Nvݥ.X=~w\_\uE:ӯdL7&Xح崺5H0PA㩽Dpȅ xkNһޝ4mk>bN2&aOFnz(qxx,4JDO':c iR^K2@8*݈U+aAepZ_̤7fflyڹנ#o+ToVS4anIkA  ՊeUhH4= bclA[j;s])oEz^jkqۈ\˻G3<jVMIt[MЈզjO"ۯ;w^w,čd;#=Gn؋M(+|TjЪEt?^v)ii;oC(?lTf ="9k1LI[BF鷟޼/#vJhP,8_~%ۖ+a~-׼> stream x]=n@D{7~dn\$\" S,}fFvC`vuݺ-~έ;Ӷn9ͷϦ6]voKB[?={ih>/2:mmq]mGy~:Uf쇹UA]X*5j#_DOԬ*8=>TUsUPRTتXת걀{> stream xz tS-A0(" * 22hm$<5&ҁL2#<((8OI?-<}-}ofɽgo{EtFDEEX?%EE=5dzFz8;Y8(!#)Y((#}_>$bTi3ߙphVْ9 9&&%Ki[,N],}1c=kϼ>~ °ßyGA &gsB bxXL %/Ka2Eb9F 'VӉ; eb1I"fob1'sW5b:&$8S#D7џ@ $B zcx 牾T "0EE,Ʈ%H.j`E7vXYFַI}cz{N^{O}ݏ+DIkbU/ņ\v=,?q(~9'~i'U^-6 3g |:\1C*/}CxAa]x~tQ o K GH8|i`cxm7<PP*]]9*K@EIft|XȪ(TJu+epXA!_,R`褋Ѥ"4Q2d-?1#@,v:"x1}!2/&덫iTGn0KY, ;?v\C2E`r3,{ 3Hhnʯ޵p nΧw?NprC-꒚p6g H'"LJfdd 42<`ʒPh IlԱa^%Wa4ҟSm+c&~K!a29U"WXLJI\97}b.M$CWnwH"ξbJ9Aw8pCS_?kH~ӓY.} `=xxV11I*4c."$#DGnWWx74-+_ (F*GS;p i؁]GQp" fjFq3@g3ai*aF1!hKKۮSA9(36 G!QHg"; ZF¡/N> U +b,sf5k־eW8g/ːuy=>PHrRqA1ۣӷ"OW8{</j5 ND'YOBx_9ɱMB2^GȌ,Y$f .oؑx=P/QwؾSAç9>|R헵ng9ޅKsW')FY .NKM`Q0l G;I{+r }82Q(*M3cFObk= k`_(d! 2!}bM)-hb5UZ*y78-Ot,sB (\epu{Խsw^ׇY P`V*-Ӂyrۀ32&.8@Օ'6/Xl6й =/k0YyZnCC 0 Ė 媵4\@ +6 8ejMjMs-敄*y[Z)m|EƱ6HRfwH[dʊ÷ x&ك14MZ6LYY%FYXnP#ԖL8>Ug CBwx׻3ǩ-:̘x`^tݚ|mfi@_aǧ;s:YS-!#3yvt%(d8.࢚xaqpPk,(0+W'%M9ԓ8_oz(jPS6ˋ-P\|ZF\"z. ;_4S9 0AEaƱ?"83!<1ɾv X ՚Z"$OK[$Yd Z`n ${o dL°Woc\ 2;iX221Ű|1.g0FGgo>}&rgiT Tۛ[yaVgmP#q[wGW;QY LtsPRh R-2Ac`7Qjqy]^[e ńedjm7I2}Q2&&}>.$>*%!]ew~LNӥdv rrl_4-M&: &J$r8vixu[\fT-7`"7t2p@d ' rZ0#-[F^mRQfJYrn);[*PQBi8v/bd$ƊC L#bԺ U[E<l`0Ls곫Bq[>qE<+6;dSDEXAtZTIkUY=385]i+(bR88(rيp:  ܲ+ʖەvEK[1ܪSE?b|0@sm-d䥩𡑨z1>(WψN].?_Fn?ŪuƬ֫Eh W+5uY[~-40]b tءZYLj;>cg'8",U?h|{N~DIQ!ݓ&ls}&Fѱ"\Af7jx #w>w4 {24.4< fZ2@X7'|ˆ9 AioTƠ4UF3 (./.|^?^rLT"j=wlʣ)3)fHyd)/~>s͈1u=pp{/F[MVnn\I+zA75fԸc)xl9sw;\/cQѥ *0$UQ0rM1\7YŘ&sTeEEtaqaxPfeZ@P..-RbuvÆp.r,2wGQ(NݵeW.u!d51t9mp+,Pn+i&лU ҲjKGPPkl8[:3.)ť݈aSJyK;cX$nkI[hxKcXߐI,IX͆5T:+{BNfͼ;+lD5 G"w*Kj9@ Nnhyë{+>Ej%=.ui\Jf T-#d@T*|YJs+weAxenn9EUUBNWЂYY3guP+Ձ3"L82p@)FKGC7\K D8NI2@-I\ / B}Q9glXC[f ޡZ(ihyHM*$ٻ'7ܬMY'dԓŴp,FF{8l0vǂU8mx-w%\Fs/x!.LZUpp%U.uY|MLӥ;+0'wcT' rBUc%a*8Y;ѬWГ#QNR^DY/Mb +?~?Rʣix!s(= 1^)!k i3$ھr n {=6Sr mo\H~m/i'N1 >|xaOEa P^L+\'j*+i`5nלS^0bShf`md|mWuj~PMP3691q5Y{_ʕEБs'mr>vB?~0DTZc2N Dތݡw|;;3E^#"RBm5W-6܈(PV M 2&q xTrSw~WT^mb "d2ho͛6Лؒp \Yp.3uG#ެR<)%$0g)ڰaMpy7jgqhq ?0#{L:7/G9TVE^"oeh93^up[5<ʹpl)eup. z[ b_ ,:A#&AMJ}: Θ l`3lL_^p6TE? Bc'=$NP{%U1ւ\W˙566Y5f6AZj/2]O޿T=3@[Ț겭v6Z9/()-,*2xF`0c[oؿ*&dT?"\zdKB,Ee2yM>u[i G֟W?y'C 7j&Lx S$ZE8IԅbY9N ~!&ݸ+ y K1?GsPy,.5([MT 4TM9) kyچ y|Ffp@v8o-3x͆7 \s\ DzBSK8Nujmp=oR]ըnv&_n`VLTV$Z33 ^jZXJfl tJ9BT_Tu`}h2tFAnUu4p:RrN*^>V_}bZ֐nk\ġ%<̘YsCterid EĠڸcuۀ6db?Ѭ%tBW?*?Zzs׆xRc=ILRkպ}~7qy.6d߻w鎦#v ZrA.X7r,SlxVvXh'9];9K}`\}}'ci?hnjj>f0mobn #}³XIL[WhX{1cӖpm tzX*uᆺlVJ%A`'/SiT- *WjaT3R仳JapkV\\=Nv|{}Qsd_Y:=SLg ҅TD5뜡;e@32lt#.zN1~R*Ao: O:A;%ღ/<|NS[A+0{$pщOB@ oۉIXw3WUU h.*9蹃#h 4P8tq^s/„ 0dgwlmii4&<+K.H%\fYJ~|[AJƽ&tTu:z3Jd^FweWߞv^Cjrs)'##%L[3 %$h15{,9saΥ K$P:6G|V<_)Zx-r؄xZYe6[ݽ_><6ՃŶCvfF]ރxLMk׺MX[Qt &O„n]- LZN--Yh>J3T&U9{}ZFRr7fZPS&,cU9ԑUTu .ۦBPx+۩!afBd!rU{"FvXE5D}}; ;M0elzV :f#Bp:q%%0$dBp_[SSSח`KFL$VXLUd/}ѯ߸; 0*rӴpa@rdR:[h]f7 Y>PT1_NHs|Ƙq` xZp7v@5Tµ9UvZJ1+k;}J?Ũ#&axZ0>,w-1V{?~9S-X> YΆ*-JC-1"ϤUjy#ٗ`EB΢:6a߅슊&20> 3Uas+Oao4;UWYYW]ܧ~} |癸߮ksRp9u!3Ep~Ľ4; v=P\@7Z[_ު-X)iml+;f^5TߟwZ oq~dްT5P _o0h@#wm[ y%>];.};Y,M"WQ! 7=?]~?L E* n>}~kehLʑfeUHk˚ Go G K:΅YpN.mA=9^|~Qo/|[z!bl <>KI͘ߍ}<'%2i:jVTYXe-xچ\ŀgrqʪV:QX8=EQyQאu\Y`,pcq 0}=']%,9|[oQd~3`2.8ii *<% z{wPx?1pc"ex_~o!y5]­ȝɈ)XQS60w иTFZ bCYX֊Bp2`^sx<싫C0Gx0fn:}3[2lþǑRYT4R=ڷX13 -o pxQªl*K)*Ϯ, Yi'yazxMSIbM=JN\T2wVZe&GJ k#ϚkY4GQ #|ʁ>R>VTFBRpx5dv8x8 `.=(.Cyه`N:9N~7:˥hTqBPI`=[!<F}$-3 %^vSS[՚  ]ػ, 709L <>^>[}gecj<=$ȴ@:C< euy}{_F'>ǶJA9K˳fscf gf' 1y9%o`c}#-`YRh,$Ł؎,c_Hd'_1;u<\$+*˩/ T_V1%]3:fZPt}"B)YQﵨ/ 8F5Pe=}k4OWGxPc953I۟JrS -ټ'kW #.O'(p4.6һA_7endstream endobj 471 0 obj << /Filter /FlateDecode /Length 320 >> stream x]Mn@ 0`'&dѪj{Etų)N4ny3m0Nm)8eec/vYR4TMquicZ;eM eiGjbt,jPueTQ#k&מ,(#,%^JzŽ+(`2RRǣQcgsF#f+>$q0 5M( Eʞ{VYg;}#G<̏uo뚦//?N-~ݣendstream endobj 472 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 3229 >> stream xW Tǧje7nmֺkSV|fE @^ !!SGWԵ/Zuknv<ԳgϞ''9gf2w}3H43*l{˗/]< .AODd93jD?JD!KegeIy d'&e-ܞ/kahvF=!"$+;'/_&/W?)%u̘U\EFDD&v1D(K#aF%"XIl&[`"K'xEDRIl153^瀔q~:A_efyg͙78呗$ڲs鄑{~ vwXB1\ pbnIQ5O2P>i! ͛0TKAoe+$ *mn<@ ɹ?$tC^#B @Ro8J]_ [i9#MOpB'Û7%)prML~[K_H՚Uih/(A}?=ͣEC88ӗeZ CM\AdbahPo (IgGBwXM%^:a`v3YʼnaP= t5K-AD3CLGqFiJ9E%e*M1Ç;]OGuޙpw?]^%toHQhrf}+'X:\~Q t] crԜ,ij6mPrB-48ͺf?ՃbϾԿZ9Oq=:\kTy;6dcR%%Jj EU!̨tp1De'O 7)r"~= PB1\$dVe6vşSN^wY90lOПH ͥ(jS,p3j7B1yƼ:Y׎ъpF\oÂцwdz)a1X m:Ya=Mʴ496 ǙI4xEmH \ϲ(r72"0"]^-;)鄞Jsm@gr=%%K5jN\o1N%؅fH|^.[[Xͥ f ̪2gjᘩPf)kN9a4C}z/D<^ڞ5"4wZ˝ѯ0sT́dW؍F{=4g$PCMzt4GAYVH)W(EUРB(-ǿ"9/% p V'8ϓߟ: gZW}Qgu兝ROoW""mdPD@Lu}sbd['E>;O?tt<6v5v.K.?$u;}%i94>jW܁rN 4s/%=̢*u2-z͡ʹrOFx߈^m2P9;}DO~]#HV?-0юPKx']2[1h3ˁ֓y`.-(NSY-bf)8k=W[mC8 ɾ܇ `} m+:Vtdlz8>h0;!j =|tMFiA*JPIdEZZK. klߌui[%S2/H=oT>"gԧMo`WPɕ/P@ȹS4֒lVd5J6 ӎT\XS{ g"rF:JXcԛp QhHHK'p#xA< B˅Y>C=iVhZ[tY1 3E17Ǎ&2SHI<1f߱=pj"t/9J'_|xooe wKݚ.]Gޡ|[ӛRcW0CduVF[ ;;sG;X--ffHVCڪgOh_ͦS- \dOoQ|tD;_\X,kp7V[,ޣUa,mZT -T@^Ҋ߬JIaiu6g՝QbJ?4FR&O r;OoUiYWre@k('Sfek\^RzLLw;ֽ+ z?s|D$GUS Нi_O#0;¥ȈsʧmJwigغݰ "iԜr ųӌշKTs̪W-.ks+>(KM`j4-Î簭݄#/dJ3(?,Y:wu\ xÂ?3!pp9?=# h~B^BoOjxra8$H̎L@0ε}gP 5#=;3l@p5`))ȫ* Z=xoJ콉=R0>YXq%oS¬*}|JX#{X+Ygb|V)O-ݠY0[؀6ݺ|FwtoX?_{1U%2Y5dgvX\1L+T\(ܴT/<ζPPノf:1h6V6q )hT!Sj+ԒJf_3 9 nendstream endobj 473 0 obj << /Filter /FlateDecode /Length 3838 >> stream xZKsVJdXeB'fT%k;8 h@t6zMB_Ǔ4') 'b}r~stz̉M1'\$3,Rܬ^'&qֲgy \Yr.٬Vdy_\u?R_׼nv3 Q V`-+TuR!r&,,bu񋥣79Ky媍b갰?{eowp ֕|G$;sxpNn`u^׸ <C a^&Y+ \sUrQu}B_6պZMpY,B_2vTL 4be\sZ8"ă r{ _pFB0syԣ_eAn@fӄv J9&|ielpu;P&IjVn,SpBi8ut8*? KO\Yڧip`-ԧ*%R=mcj$U #TYCF@}NPB!ç|x9硔% 2^&i ]|T! B@2Mh*9?(@1^VA}[s^2ȅ]$P :#u/9g/.. t9]w-iZfh 7ºP=;2xK 3 x̬l Ly ]3jTIdh¢r=]E=CS %BQrTf!hqM[!ȾIT6baR_! s3MxUE7W!UqvmA|P6f)e=4IH8YØa;bvXke_NRE0>(.X?n}M94KXJTQ IIJwIܲ;x@b :P"!yYևxB4m+>|(=c/H*,k@JӰ5Qp,QN <ʼ(N!,&9Y t$~EVEdmĤy~妶D/Xh5.qMa 'Hp¤QY&/va3$ѹ~(6Hzrm҆d$xUTND$^頢b݂Xw?l.x}p, /9<;mwJPdV:#YCNȖ2QNy4#ԇrۈ==|꓏]⢬rn!Vx0 9 M8azQ$!B6 L$} HvP .jWL͇QA ڎn[߈Wؔe\'ƤBf(v˪;CXIA6o4S&ʙQPw..U'/Hm @]tĹvTރէᒊp b"${JN`_cFʋboE/:7ޟ$,. +_Á bazGq_ F (uOZR(S*f2>="ŷ6OM·pq;Yã)'bE'A; 9\mabgPz][~掊״c 4^&jGp8I,q0UoP`z 8 (.V=];tMJ"LД( 9Hx1U@l 47j*Yfqf\\ӽT(Mr3}hyϫ-S,%6z?\զIE+9 ( zd"ᾉOYF;f@_kM _Ve+6 þfڑP(5^YZڤ <ПX oF2jGO"`~EMmrvsv g=ZkBh_m^~PcG*̂s~ !Jx͉KD&y)*"$3Byޖӕk,qM(W\y*=V NoU_k89(c 5dR$uՈ%dvst)1)./޼9=A(%pU| ׾P6i9v P .y,y`U[o ׽r=?]0 |'zo4FxQь[_L\{9lm)~dc\]bpF[q'ǾO73E]O:͙_垐SqID SrUL 0t ru?zM0/D!cFiuB2t0h oە1endstream endobj 474 0 obj << /Filter /FlateDecode /Length 3674 >> stream xZKwF{]ZiΑ n3 qJJ H$=o|U 25 w㫯 $IN?|,?Y>֓g9yyʔ'.eiNnDjҝ͔.Nn6OfgEk\ZSla,Ͻ nɼSRJ-=EE1#U}մxJ|_ UtbCme+k_iu;|)GMmh&ChO#޵yOj"<'əĤŸ, PLpd IF \igEw܉aU\JÊ$] tmJڻwN[YL8zfR ̾튏Sb'ȏeЮiB|^]^xX N|x&Fޔ k5lu:;ӆ,}{zl\4%SE\`G)I59%27h3>ٙƶ&WB*c9r'ԋMb_'= R(Co~hjdUނOx5,Msۯm6h;d,㗙LJ>$ ~F}.WJ{s^Zf?bh1ZJo%@3:mn8{`v 1Dސd0#O[u:a?Ȍ۱p2> څ-;h2]&}q(dAJ%t$IZ'p 9P^3؛j[?.냍>'gqN{))g84[}>%c$!6;x1yy;$D"M5f 0czK(ټFХ (܌TJx}r2\I,y~<#nadVHS.noY6KPLCO\5~6aU;oyIąKR-n@Tu8mm !KuCd g=)KbU--a񌻯BuZ,*19{#p5Ǎ@> _7:85T|C+k>v<^3TuA4xu~'6ߛͅ0&:pY7Ι82/6$S;O)- d@/68'lBuGqt喃 +pl$o`#N-V#urgZ5NĢAi͇]8A Gm8wDHtYA Vad(3Yجp^& ᾿8?kvtrͻvu۬.Ϗ.\% EU)> Ȅx\٘!JɞHHЫ U5=?.g8s ZWE͡/nYFZUlnУb Z_]8r™tdB6ExHi^f U5t}XwfQq S㋞e ӿ䮞Գ 6GeT| (S$ا 4I_x+bUc+ ]m>_vQy'IO* NZx̯vld *'AֲMUeh,& vSL! !pm#{a}h&AF}˨1M'|kXȻχۋ?-!Ŧ$!VpmLБ4뤯nPdq?Wݦ`񱛺U>2PseH5B@g/EA(t^<F3r:MvzP;]l"&ҔFJr)i9j@ǰK2$9բZw}E>`j=fՑnt*~d"6!\O,M1%62I&|P~݂<HdQ16Zs x91+4հB;jІ%yhS'8XX,b%{0Z~l`N$aSl{ރ9 =K)h.eTܣ kaݛ+ߖbZ.axa7i1L)6X: Y4VŲ̓ :5H,PEUA ?0d %e8KNq㳢COQkEn|eaf9)k1SJ!DS{$yn#D=&AK$7\2Oa(4E,qdՋtG.(g`T+&4>d HW?zZĠlؿI3&yOQBzɳT t1=GP|c:т6 c uբYrnr#;Ì0+tBïuFi}$Q@'^Vxr7dkq,ѓI VK\R;rX.n= OS+hTmq'8y[1Ǚ Wy WY/U\N=G&NTS, 0#am̥NضgBr!&Fbhо,R +?h~ V"0J5wz\!*CEYM9>UENI9&Mc춘UQMwNrþ>!![shwQJS#kT*%hTHUo~%yLy]-yVgB¨ż㗊vȅ:*?0q/vr(>\8A2DŽ`fX(;dwڦu1 pG,ߡ~ \%CHo0!-ʴb[As#CBx1zo2κڗ4ϙy.5CQXC N#S8,@W;]߁;Zr6#ṡ|[;. 3ȒUu _5=9NiлgR)NHIHd^~9I^˞W0'$L3$q|:c*Cj,1D>LeoN.\; _-AUH JI5k9k0BDr]Qĺt|XǮgʱh?l:TI=},oj,Tq4H=1T ^n\j|c%;s.CP[NUj'0 I5q jcl'pj>G :> rjĎ jwY4̑6? sŠ-YRJ@s!-$f̱> stream xZKsSrq2! 3d^d;vSu* ){rNw ʔy{igo8= -6/Tvje晟OiM">›H4_>nM+#s_M,>ET G ͒ r |0`W|UOpd-[kQ(\QL8r(?tYԶŒ-a2}h+ =2CTA±ׄ;^< XVsE{aѻK[_ʢsm &荦!{iy7?8nŨk%M:;u"?p ȩKA>>M mP;\ C)G@r0MIO>]3ƶr %, [E2'- 󸤍">#!YC#h!p&,#lU(+в;2߃ukmHk|y~s(OD#dHS,j~ b([1_t]Y[L 25fyd_d17 P$dj΄J ; QxT@Y!2&4E80 3؄z9s 4_Yi)i%r= N=]OA98׮ΰ芼]xV􃿼Br$ RzGcXWcAA 822@h|6 _4yrH顈#!EMլRaXQo]bR'`շP1;[l;54Sx (2`Dm|xv_ٛfn{L| i2e <{~4 ]ˇ2[O{7 6 CJ&B zH7>8R^s bq GlWq`ޫ7WQ[$'; HaE=' F\2#xz$S |G(m慩qPz?m fR ʺCNb2>{T!G@vvqif ]懕/1q9!/TR)! E^@$5{׏sM^#R ^D)$3 G2fNf|+u]3L iU9+<%ERS6 4 _B_Q(<]?j?Djekty~51 KCXgr?VES, ؇Zt s a'rR $ӈfv9өڈB x}^ךw巁 EtB['x!5C6JS{\]N!,2n=EK0*{+-UdfuEIo=Mw?7} P;?*B_慏0hĚ qEOJ r׋ U(5))8BR#҂B Ch~a=Q<! lawC0C429Pdf[CP ;—,Ć8, x򟃋@A|PKG akq6PaTƾ (5Tn',Ԛ88)*爙 =Dl`,TD h<ä=4xx~Xc\^ҎǑ*:*w~Sl@ĸgDWє9n_ҋP+ul?Szx(2!a:x`Yu$0k=hps%AoVcג-  0'@GK}׷g 0J#+UP釬dF<ޫyC)/We9@i O[@߅K0?E.+4"$>RV7}*~Xs$7`qߗjYaIf ge캬C_I8>1[G9^nT'laPQ~PLqWm{z/N)W5^F/ zx[[\h#ׄJPuLg77R1y<ɸGPkmW?9S| v>G'} %dzwZo9$Nvn6-ۦo w:Tg~L]a Pt!a+-GBN9I*61GCEϐqbtlVT=.@ ZIdm|dzVݖ*XcBrQ{`|B/6%AkFzBHcا<#Qz>TPyKp/H@eg;<w4u_C5 !TPZF<7`YutuH>A ǏiCJ鰯OtҚObm"(㽢$;,K:џ(H:Ooа :.iӖg":Ƶ÷eɾ^dN_d!nüsO%/L}n}m&׮KnPJꔎ3 P[Baf^]=+ l6endstream endobj 476 0 obj << /Filter /FlateDecode /Length 382 >> stream x]1n@D{7%-[ݸ$\,L\)R2Z~[;˸Vj{8,o 럿km9P; ouɟK:u(uڜ.)7u5zu<5;J> stream xXpSgrzC"BIxd P Anl܋pmu˲en\ EcZ P%&]Gpy/ dͼ;sw#Cx~kBގxmԩދ>W|zxg{&ӆHDxI Rs2-OOLvavd''!EQRgf-ΉY#6/ny-ޖ}]ʴ3fNEVQ0j NR먷jI-&S)Tz Pөwer jŧD Q@j4P;{ L*jrO:2~`C6K+ !^Ԡ_|үQ+{o,pxV $u rs6E]eB$nqECǿ}ʸts<}f:u]y#a1̋ ehpZ@`_D )Ddˢ`#ծ鎋p>;p N瞉sG[XnW#Æ]{*7)`Sֆ%f&mxnr~M;߉˩#?Ax/z=Z)[0rh7m\|pe82e:L$e*Ld ~0t+=EZ/:? nͬfJLj'b)󻱘q)RD2e]/*AJ=,UZ7vy;1lYq`No] d |ӝ)p'7r%IGUN-P/+Zdk@l=}b1O{{)DE9a:q&|Ε/rDw,8 L\yn77@;{l}kI\DU+f7$]S뙿d퍿oBY0_Z yZ6 5NC].L(QTO`@WoQ^ct,6KMlXȝ*NIk荟ڐXNj',5sי١"\d&[&,,ڡ)++ȅ2Ԓc"Ổ*r(aaByi(}Tn_6<}^iAZܺͰު_dpma}Do;nF]vc˩%! hQeS#V׽Q缱bI43wO;cdQE~b3Bq:Xf@|WHBč^ .Jb_߷k?SD \A~q4usq j=CgT:rYN@1*}w%18Of q~_mj{pØinЦTF4&l>lPCԳxE8+Ѕ~ocqg4YM=}$e8!J_uq9}tT)&v{vN@sی38hK40HIw6VQ[r.D0YT~so}lWPaU[qw N/L e6֩SO0 }'ǵf` %WG#9ZAƧ%e&nv>oY\&ty +gp]I8lznu)ܣu hI@VjUIœ I+iKXjbLX~zgFmu'FwWtq_ݡEb:2P#T*,H7ȳs9> Oxݿ-n^7Ǥd2 r!>CZ=]pCL+VmYq w's)|qdW@[NU`WgnXG@)2_]x2RDMЬoR`&|+L~n(G$c9gT&epն܈ܘ}=ߒukmQ}Ӈ^㨵(${7IB*,.&-( VGdJc* \FJa'}7P+}S"ٟevGWo MOG/9fK-bϓrG8e> $Ƭ6XpTUzlDZ$,Ut(;JB){ՊcIAؤ4sܷa̍ǁ-ZWڭUb]t\ J`]ZOa4k"\<5w83xڝ#s3Vl$^BYn$M1W켶[dL3[e9bQZHb7esztfw}ݝKl"CȖy۟ 8"AO*l3S32#Wyjǝ_ܴzH^,vbKN{{KSkZ#9gn4 /Ĺ@U=x]G1B!P8|NS5T 3\Vq? 8aÇ:WLc! 6AƔMا#iwc" <?~ҍٰs~' CB׍HC Ű՘.p2pF.Mo"Յ~݅fqsoVS"8%9Dm"itԮ2|J& 9R2b[LIkW.CtTdP[[Xd9]+G0Vü5MQy'1Wӂ^`/}v{c䤤Eaxf1m/U}f@\G:N$.pS8&#sϾDY[]q6$_ 'xG˭vn2_4·2wvhVf2BHc@x6${nK]t{%$\?ro?n[^zr^Td.2^>Hq/uL=>q|HliNiijNuDig4/`}Ȣq9I{w'y cwOlOؚ7ߪĕ4D&3z++?T3D-\obkatVYE<\)m*VDA$DأkTFIeP̙,w)1uW8Ew {&2͸؍̛~m _!\tW )x>rqű8ZHd<H"sO8^|€`+Ԕt1(KUs̉ GQSuendstream endobj 478 0 obj << /Filter /FlateDecode /Length 571 >> stream x]=n@D{7~l@n\$\" S-}fFvC`$xoruun|Y_?uzYvc7]gs|۰?hGKuj0mX^4uז鿯"oϟU{> stream xz xUo@U(M Z:ʦ B"@HޝN'=w IY&("YTttT\40S"sߛ_Nr,o"++ŋǎ3t~=&$Mo2 u90u'|~/w}4J$\S3lnkj*-UԬ?O!bS+UP=fFmpH,)-]5Gzyk]d}aⲗ6\'O8G=|nj%|bXH<@,"$ K#b)1xxF$^%^ Fˈhb1( 3Yēlb1xKBܞn9Z,S$9LGPgHJ$wdFW~?/25#K?$2 "RiS%Y8/I?UEuxkѷyo_6{I% 1VE#5hd(m0 N 6Ü~V弉t RS9>8zz/\ܛ˜쓤LCzPj 쐨rqPx9NT>)>C~WHk aAo<HU䇲/PeJKK؛!S(U/Ctપ RZr#hiMy |N.:1G3鷛|vO")ҪgxU%b(" _wږ \Dv,QL'U]P"^A•KKz@_ ʔ-В#@ӆapԯ7Gq?FyH&Fӧ>Ozfs=[O>\vRiw*k{ovzt#AI諀֒Q$4 {= 5n?}_gy.*.D^GC:~`<k0aD8ׇ52zQu NQ.-.[d$R"J:\]mÅ?/ _Ln.&hH7H<$EEK肢?s׈{w/[`JKsq#>ijJ*qTPYfˉG"*酓Ṗ8%JD}쾌[!9=ǬF)6JrӃBJJ>U/>_4aCHubۼ~e|Y>p4eGpۻnP>ʷ\g\Z0w%ݲa=Wr.»?ٻ GrfߥHsZ}fɈ!sTBʤJL<"Kcc0c$vnd/QiKrXnddQ.\A2yY6E}${Jԇثw5*(3QD{KNX85`+Yt)UG2 5ܯsL8+Pc(#hKDa)˽eMEo'b֞E53.+,?IϽp(W.ϙlPB-Y@k6lRs `)'l-RwY^R3?`6䣿gFŘaZ1?Bu ИgOsLH* CK][?\ r&vrwZ=cNf]v{4ʝbDĮo*wO'<{ EVeqAЪz{oL)ȯ8c{v); Of)0R5upzfK¡,hF%4q-ҴzW~4ջD=op-o6D1آ|΀P[+6e-%6hShOu6m{`I|Xz` YH%! HC5p>}~O2F#d "ѺB=,T T$akÓ+Д/vS*U*nKE 2:G${Ңk_wXLM izd):ٯD<ٯSjH&n*: llMy)mBg^ad-c}~G$ {0cJ?Nx>D֨[ +SqU@S| =y=d ,o/rF-`hwmA2$r7HkXi;RL!Sp`5/Ru+yq=DBL"QPnqёP %[̊I *m^}Ϟmbm%I0muv@xF5U92P*F=EoqȗgV$hņh)=y+s-nrt6PB)5 gsYim %ɔ\&L0![?CFIrL$bwK&X\UpzR#__s|qNBY. "]@{&ZIJ-9֡h?Mbaw编K2kc*) nd_Kp_~LuA,y]d돶IR^v9.Gܚ'n[u]PA&Ob|NLn@G_×'Fqг? _ 1#aC=dx6kZqu„Sw&C#&ϭ +07P zLU+E2aAot⚘)J2+qQ73(*&DS)H./;7z,~zؖ-b*D>M32q'J{%DtM5q3Ik\:ΡAryI3^R@k =mٓW;a`3}2]_pMte!HS+H\楑W7Q*rδpiO;3dDbTf™&M2Ҥ4́z1g%KSo6m¦w1ˆE4C][ dRY{NfC.}da6jWSw@S.s2W0'𳹋n.[}S$ܶݺ l9`!R`>xfdy kUumZMrtsRYp6r6ϐϜjj(bp$P+=P1_ Su8i'2^@cEA"Sқ>9/?7İMjV]X'N<4enZב1qWLwFIe|OuM ޾94Q7eʈ%o j<A9[]m>}a~aBzCUY+yJDte!X 7q]Tʍ-.'}g*vtBmNv;Ċ ._Zǭ3@1m,Ҁ>7HKkGO{vٴ/6p [mZњRMMdeX*H @bwWfm{wzLCh#y3gS{qO]i3)}<$"{:_bț(榭)<\p6`N͔1wC`Y ; h#c;N<2NѨ_|gq}/P9ןuzje/NcNI܎Srlס#JY5sE]Gꮇ?4>Ŕ~xʥ)X_]?^#7Ȕ bTLmYuu}Ƹv;-#b>8O.ŸfݝYlEy1[Vl5ݺ`eo Jpr–N^I[1ѠaZI#qk kj>SpZ8MCCPgFAĺwȾmzPO3iަ5Oj[':~|-hZd5Zt=8=UЍ]YYH JuU;M .zkuJ_z_T( fh_=z߁^%!o,m[yF>Wi+ |%`f_ Ksמ4W|%Cڛ~e@ݠPkЀ_D ?w<1yL^@MܥrZs @؝g9nj?= LU^@uL>݉)*(hE@zGv~p,x/Ix `NՕ ViĂ+#T@R䔳L{k!̼jʦw0N ʌĜIi"qT.Vue c]xo偗,ܚ."]|ZUUWnczk钚)C̼:&-urWVSf!4?_ԯp^lS `@eS5v[Ȅi * seܭƛ ٰ0_6쵫Gf]:.0Mla)~% 牯yHOu RQTZ(ZPʍj [!y@fd{{ͮw~ xxHդzwB{v%[P|&ᰦp ^ }AEr9ANN:ٲvWf j[3WRtU#@+a 5oy=r%Մ MiꄑTFeAK0K|ܨt37kMB(]5T;z&ϩvfvv@oʠ4)Kendstream endobj 480 0 obj << /Filter /FlateDecode /Length 339 >> stream x]n@{7.5N"Q8" "oٱ"Ŭfn=/޷%|=4nܶ!~);8AүYqzׯ58;X|4UO{SXx]>}Ǭsw䳘1LesYW<*4<5<\1l=l 2C:AWW`"43Č6HHS@5WW O)`0= q1<viMAmE|j#(+GPAWY-"2ZfE^efT{sكj<7!mip?l-Vl]Vʡendstream endobj 481 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 3264 >> stream xuV TgCJTPeu/uf굢"U ʝ !{A\]AѶNk۩V[gv,ǡ~|r{yPs(@07$؆׻6+ܜ˅@d?l~ `NExt)J#r$~;S21i>A$LeLsvNc0Y\wzޯ`񂲅^hQJ-Q2ssFCbpL!]Z-ejƗj4ի!"2YLiQ`ȂL:)/.8WFNxgz;B/eF. <8X"¾Q-H9/Ka% #[bK#ʢYN@]jpaMk[)DM>zʶQJ߫J4yEYŊnR{{m޷ƒ^˕Н]SzsYY10 /$x QԌ8Mʛ2 ݻU{06&ךTY)Y@9ЭYPWjr>۽cҐ2Vn<;Fb-R[J/6Te枅Ե\؏3ATM9 f\͈8f.V?%Y9}e#:q<9#őjr maTʹ9eLRy*~hMcU*[m:Kk-9}O46(k(}]vSu`9i)hi { os䴌k,Z*4kTGӎ}-Wb {}ݼBKĭeV`'3] 9/⒬Wo(9k+mP\59@,=/=ͱsOJל8,(mlnhֲeu6tްJ5P]\<Y$-Pj@KgE}QCtPR3ʔWQIv}~2vd+O=({;GB/#]ӎF<8%wؖF=<fF̗|? opW(Eu"r,$'^.őyR.6ʋ) IټՋ{INIMSC]e{CwU7o[t4-Oq vqp(g%v{dKmyhb:.y¶B(,),aLYQJoj˙XuR.#s_I ƶf`sd3Ttg;OlvG'NxP0O~⼼p]h,DK E) :ppɱO3&`~dZՔH L9M\l,nΒ v>0O{4 +YVIRr:U_Ml!kIEqibpB`.DO1fi{!KfKr^f:>0;I;y,pn4FB|x? G79h_)4,:m/M!ƏΜᳶ*Pm֔C Rx!1^< lϜ u;$,}*qc-'yF?/w۷ˡ;z~>}P45wir<.f„=2ݜ l:I(I`g?vT7-tB^nDot&DAo1`ée9FX {2J).cFu#QT dI:hŵkۜUTmnU&-qKx303o=vÄmk6>ˆ=yˇ*^`"1=2Dꪲ'\!q%'p=RK{ʻ{M.orAՄaD(݌1&ɟwGzpLFy+S:zh7?V?֝.yk5eyE:e U\seŰwxԕZkuuMo.{w~jT2P;!'+SƗس/S{pB㞆S{|hx ?Q7z%z6iFz G3OI`GB~%ȕh f\TswP"D_⎖~`wi6_m d2,3ӻPҹ4~nǘBb ͘ iY.^q|r5fJޤ'@(V3>=hϰY%/(E?o=ϣvD{Q6DiVZ$hj!/MZt!5MR3_}v*̙H¢TqЗCQW:$ʴ e +͏/}l;7l"“QCv5By\/"4A*<9u ٓ 4ꂖs~ 9 ׳OjT\gLF]X͗J2OX}\G^endstream endobj 482 0 obj << /Filter /FlateDecode /Length 547 >> stream x]n@{>@75N"A(d0ErNb~vrunv}e}̷:۴?h{>ς?S{[mҺ0\ ?珟:a|aa|TRa:vzIUʓxDed:úW갆* UWaMթ:kpjxrhrpjxrhr> stream xtg[)MOrlc 690 @BYVsNU:砖ԊHH ƀ1c{<3ggw\3+gg{}@rM.|[ +)pm!\{eGME{nB%WLgVc}Srcm +74nsWD2cf+^mbrn7k*h+n\iqUj׮'~{և=$;$$wJK,-Y*GLrd> $oJ"yHZ2KdU#$%J^L̑<&+y] ɓy$%OKHJH%24\R"!%WIK\/i Qrdx$E +ʯ8QX{eݔ{ɝW=yWk^n0MƩQūYviaWoє(}O6tw|ygwogw}>7ouy*}軇Z^QߢB궩ę=4X~ 2Ȥۗǖ.79U,i&@Jզ;HIw{$B@JYwGI(:V MC_oF/hЋ&=fЂd7 ‚;%– : 5i"@ ܹLcȓ$.0cZ6sĒߓ+o:Yz2fW`S嬛u]HfTx[6 ^թrB'cu[Zi @jgW@rH~^y Mݟ!*dZn,-O/#Aӣcݧ(ɪծH=F‰a>Fwu$/yG V#7 'YVC3zޥ[˸_2Z:.ASC+?JxC Z #N,[FKDŽ+x\(480P F|6t;)dޮ@sMǘ;D{#a<3{Ƌj @5]Tx%|~L]p7/zIn+ѭn%ϗ^(~%2 J9K:u-jN"ݨ$OZ NI}U!Q L<x[]Im}}i4 c.۸Y(뗮9/qmo D}V0, #?d" (eĵբ5m 9qO4SQ'@1WjNctQ9Go8`#%U~!+"]%١5*(G•0Gu閰^ÖkBY䇣b}:F&tO6K.nNP{6y⽃_~ĵCҙJ+b y;E)ރȃLl!a5=o-AZ-]AIvvwrQ> Q2d٨+iYKLXX/rDGQOV%c^JKbs1Y&Zڄ4\7+6;>VqT 4nK秏 -t'RP[ BCE#m3)͂ևpD|M9-e=i}\Siuݭ}wʱ|Gٲqsjcth# C7ї;{)ޥ4Z2.x|mvo X  2½#LeǸ`dkeZń玖# f f|+hͩY+,m=97 z;|yx{ r "]1_έU5|A۠OC০m6Z .KGMO 8>G=}~A_44Pd"'ćP1{kc' 7b U0cl.R 6gw/2襏!;p-qMUtP 5͵KCyǖNfߥý m]Jfz^rR8]-`q"OdrX<*Mi3Z\ 9:@Ƕ,6QKӌ q0|(Ett:١k/jݕP ,`e]fQ7`^~Ddrua#J6q)v8FĻI('pֺYEpJO f/ezozJV%h1b]JH.1==pȆAl]Jo(GwŲ{X1L`EεRCJnljr?P!]~ϡ]."su"rHi&o`q#G%gdK;6ZWMe!KFƆ5㺑IL oaxlIuR䢤5 sk^Ltd_l_t_`H`Zڜ:5FuDz=tW$6(pc K涾8\6 ds۾?[Ru5 [ AfX{u(ցo !?nEQGȒ/eN'Ϥ$ޕ{<\d[P6}f vKh&\{ )z .ߨlГܛm\K 6"Og4-=~󱓇^v!mύ_g\P5MxeIaC@]ʦQL6ӬwP ab1Ƽ-6cD8I,vvXj 7Jţ.aC5'3\0䪈aKm~&c[kXt|$8vc)DՍVQ YӚˡ![v xpoC9gMQAÌu:s#ݺa#g/`x ֑~;B4wOED^Njpb>*T8E*AGf'G>_@L Om`a+_@V=<}I/ L!v7U5AcˢUXD*!u s6+H @ 4L=!9¥:[k:zZF{Us-`11;aŮ>D1x8e'T*.A1ЎAqJMyl9 E*)NԁUaP)|‹v4c;bka׈k/wcGXt^o# vL.&YL]Gi\~Od=X4]DRf8!^=(/7TAWWȽ)e{[Cen_(S([/xEV Al'C)> QlgHξ}JNә#Qgl0UU*Vxz*_`Bbʼn\fv]5ڏduB91v0?d'?3% Ûګ֕< ew҉]p2b{.l+mR۳iZ49?qVWۙjĶq Sa":N-ܪb}ut:T/eĴ>;Q"35eZ;W蔛Y֛֙j뜤3ܑǘOPl?-EpOġSA~ƱwDžG >JS @R'w9&M-sz\)w TJih ZsWr{I:ӎT)R6)kMrg%&4pΒ%fh~?>z>~ǙQm-g36M=Blp@p7CwF^kX&*]35 {`_XcCu+WQ] KGX$/90.O"n+S?'yo &Vʄz5VRvKD+ 5+DEBJxH2p2)ǪR`nF?GP7] +m+{Ćd!dψ34$ \zN%;w)[nf[T!45oU~YJ]FY'KqK!g ӄ?iG?aeL6C% 2=;wlYY#T:?ý{~̓ۻX!;LŖfF%[FFKsvLanrd\Q?%S_ދcnMуߢikKxGFl,+ႬGGA>qYutu]a<*K2,ykZ5 FP*I($Ӧjdۚ|sxnlСߢa+dZѫ: a'i /c:zQ5Ln867it LJ##!:q4}|d3uIjrg}I27>98v Ղ"v\TgLJ?= TfKE S/KoׂX6sAkՉ_}=aAlbRkhMvxaZ}6ۤsRJR7$tX? 3#-*oa*@s<{aӤ| "~>BHa0]g~%=71 %m6 ?saUr Ka>B!"\t .ֈ{:V#w(M8Kv❱ oְJ#^hz;0Nj׵ڄͧSȽI>;A,Az/65 o6_:ܸc*7[+^Z^im141MẝTпKjfXѼx[8uѩ}]@*rGrsT#S1m*h% }jJ5ðLɥX,˶@}Y1*D2w.g 78XT@~7^vpPzQYGL Dr>"_q9 jJqpqM\- S"G[诫PQ7LSO=8f KElⶬ ,BGK휜*] hdI&~)KF!,X3(,=hWrl=3˕ıqTftV6]~U1{`&›$:>qZ꾴ICy:I}o73[&y?r ꕊƕ'XPړyH#}@&AbkZɸҬ{,[I7X.\ف-@ "醭C%/5<(McR {||<0~ ¥d-.3Ik`x*4"CgQhL'>Bj{ x~ ZՉ9[sLF)x'O\Bn*[&3A|W:eH#}*m:\wFbpO˔+ Z~E_IC ub˦k]z,3rƆh% s|okwB%|܇&nRNSFv,vFgspRf(bkQP!$p;%CD4'.UaX ]E@ _ 2:xGD'fS9ճ1y˽zx](5{B=7H~ ^"Q%Eiz(uY?Yb|~!11Ov*yh-6٤HIV(]ji;sZVXX+.ާc Wk؊ax6FSg,/2F œ"h,OQ^vdiߪfӬ JJF.fIw#|!20TX7o K/]xC8NwYM^S-Ud3gMS|`rb ; wbMV:4 K&޷;Axܣ8̫6Uʂ$x;84Ū3>&=;15oL@+\%EF)EIϵ/GG%޷sN%;shFt[ 4s-a*um@!erنd lMRl"ڎns0+;92AuLBխ6][D7u3bO*Blqzo@?X;8})\QA!f]ݪuurx6*۠cRedV[$ŭ@A 8H,Ama-+wIC"o'8ɤ14(T@.c,~+UZkW:<"CE @ Qΰ2ߌN| #'ذ;I< ëWu sw hr&K:| O6.|m# I^7b^(/Ϙe?53NTM&D!ZF8vF(hZiРtL9zvj;eѭ]؛s$ZO8bRҹtt.{{=Y9 F^:7eޤ-ڞMui4CVN ~·3'3^aT[aݫP4N<]b{ZjE~i߆4?RLsVYSt93aa}h|G8澍-՛(^5u@ bH@$h᱂ Ffv:HZ6cD xL .Kp$) 䜪lF2'v R/~'uq@HmmDA|  VD> stream x]AN@E>o'"RTذfn#/p,ܞ`,~K/IWճ+LJe?ۥvq\_q.oYކ? Z[N7~o6Gۥr: kmN]i.㯯ya pk(JBܩG]pC(=!@D9UBF&UN0ỏ3g5F=fͰ242T5U6V635541526 0s(LPFDOS_c_ xGIߨoP7 L, pǠn7LFAr2+sȹ9S!t :\NAK)s mض\ZbV4_Yϣendstream endobj 485 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 4539 >> stream xX pUB}l! : !eo!NGN:H_Ig$ 0x31h:GkݝڪTW%])+q_F*`EApP%Hz/uP̪*I7ϯZA\9L%ڠPЌʪY"D*+֬]~7nz|󖊭?IS}lqSj!IDBQRQR3YԳlj,5K=G=O^^Qo(*G SPR%ԯI{M*]~g_? OV0G>4p{ vW`w33NT [.W9;Wt]_u (E F,d{HSgڐbC+<-9 aV"yD.dB: S|}6RYֶсR1AFʡAcx?ۂij5w<0,:- 3zѢޡhJ~O:T W!γŀBQI4&L$2~1 ɖ̶]jQ/ౢko2o ?ljFF!u9ԓ+:>J#GE{flpH˸*{G)!7@7w}l\܌OYym#<+Ώ 6[d?$GDc|рzCHZ˛UԃepM{U׆nM౦-Z8x$duwYB5mXG}HQmVosht:uko(PॷI]J Xa&Qn#d?ܗm[(Bo;1lr ^(Z<&Glޣgw̥SZ<}AQҊ PhCRt1T@6a}g $iPMg< (b@FR\@)71 NThc6=jŊ_+y/l=]zid / ES>zvw2K'QNgrrL$d)4: WKV2&;̰F[B&SЊ:F5ȡ%2%z$?K0u:"Ob|_1dz?>|%al]جZRPg$hq0dn5#hSrY\ї'V:+#phv(O@"H'5J#6pZVWjIG*0ylSJs{ k 2q’<r"V_Fe.)- -.iRY}]sss&g^@bgq]H±D@eco#܀6&Mj$p&-E(!niĘ~VPۤSoJc8{>hjE#8\Z\(5Ãy*fJJ& ҡd+HȖ!]? }KǸs\a렵R__cZ+= Mx+-byHo˓ &EhPv G™Ty | ϖ_hk[&@$Fᵀ(aAxyɩ!K y ۬=ԒsQ3e_;/j̍j{йT|6f12M4YwɈďzeP _=F"]O{eE([P~C|3# ;-粜 @?TyU'ZGooww@&"Q1M2-dsC弞hZE#-v_<?JonYY|#Q@fY|aۯy@%U*-x lawÏpvd),aFefLYw鼙~+$>3j2e `+<>W SSNT! pA$2Egy\+0;n%aG-<,&Szd!]2#F3t: /BL8lVx<$5,lhw<.Bц@#|F*Bzּ,WDɮwU:Ư)T(E<wJÓ$l =dĂ.<~ v/?R&q*ׁReDlxs tq\T6|K\mI$ZBlvK+?鋖r[+D[))PlM $NNcU V7gI@O䳂/o^*[5 ڲ036ڡ H=`߹^Ne,gVYZcV$8YBY's6?l P yk酅 I؛ݛ'SoLT]\+inQ?9L|AܠzKut&'m\&E_ )Zp';? Ui£ȫ|tb jU&N`vxS7hh_Вt ݔ0°hMXM^/M,[vEɁ^v?rT,4/ 5.ʼ}W`<{lxpZra/[BBvmV#AWSI.~!*^Oid>cџ%۟ s,.dW }.(ϊTMwx !j I\pzy!*Xej3TqQR+r-D$0W{5 (n6h E'O> stream xXM=/;HH" 9#H\,Iy&@0iU^UuuYeo߬]}{gڦ6z;+*s]BY\&Wva &q[h$/ ;Q#רo!B.an`6d-@@つ!3]Ş?`DtgCԧN@"z{Y%(=VU.ÑNjB L` X-}THb2آPi"{A#f^x0c6u-]DHp-f#ֈŀ\SHײs[l:#=a ;- {A*[ g>䜢UݼF's㜽nz8u1!DJ*sDTyǫ]l'(!<hViFeۏmT2+p9YkE_D[h}F]:WڂD #a#1~(e EDj` <ܬH+@Nw bG#B)'%Vt}}Gg! A2[pzzw}NZQ4Fuxɲc"8o^e9-R iwOd..$e* I_Ai샴+U,LSL+l~~lXlOCܢR%,F2ҎUH肸=ę2 ͖օߓح֦;d:N : PL?!w=^A{tAp>C4!Xy1#>@NU/;bRѠ6,P(_Gº;|rk^|{+ `3fMEX<ظk9WcM2, 6n~x'o>9x9w1"Hwff8ބjg/MHs pŢ ˃"+ܳB sǎa &e즖E`i sǹݏፚX[dgw&"uIDX ĄvHL)l~wh%So>_0 7ye(>DQԌT3"T~ P6tV| MӌI|Ul*"P !^2Ue>҃)!Puy-BO:5g7mv)FTALX^oW_endstream endobj 487 0 obj << /Filter /FlateDecode /Length 2054 >> stream xXKܶ>GK0%M$(r*JqrQ.فCҲHjV=,I4Ƽ&1&M}ؼpW?rnUroǍ·ٗmvdM~ؕD}i ϥ`Ce:|E( REtᬿL F"qv`XFs^,g ۚ(f#~N$K9pvhR2۷h.Rތ'XBl`c!nN$C{.Is_-jJ ,4pG6If j5" h.fn'7ѵUD #0>)'[q[}`/Up`sV־5vDp?♠C!Bl}۫E σs*1uܒ9rj\LLQWEP4}F]h!RgSW#-TxFWQX>i.֜s/߻ 狼6]پ0|ܑ aoտ2 ES?=|tzME2^z8 װ$>&h]pSUk>&na4ueݍ__VT2͓T7`T2Ig})K{]$yHUjV..X8hu䴋0Rkٖk <. vN>k$qf$`ZqHغ0 $b(4\di[=D?Z: DQ9D&ȡu\Osp8/jj|xrqZN4\K0/!O.iI<[aZKO v!BR n—a)F%@5ϗ"ԘSx >?zmH5| #Z:T x;_PP .ȃ,F^Qj;(;j(mva@3HF u=ǰbe3WJ c),E[m/wZOG񒦝VMPE]02P[`]ɿ).@Pnm3zYo4zV]uynZMUOڃWp M`̛y ?4W ,n=+gѲ'8O쇞 wYX0Kޡ ]bDM&؋퀮&A!.Xg#1$~%vLN~qf]4!9LESK k?)endstream endobj 488 0 obj << /Filter /FlateDecode /Length 3161 >> stream xYɒω:[ ^\`@P% 3}P;F4BjU_fn6ü80(\^~wmH ooD׷_ݼ[bn =Ȋ6 ٭M@]}paٚ< Є{6~:׈Ut`z~ }D1&'HU<6::)jygVy雛_}륊6SFJD{Kyj~ZA:<$9^aae7Z5׭|,Ktlڨ(3YSot{ evjm^' չ];6厶h"t+Hqdv-axőyrv: ,E F29M2ճEuQEK7vVG0VeSsZ5A-G7 (pZ#,Yyr:f+g Yb>`&Ϋ'铡m58_[,"ώ\ߍ0VGaJ͌%_kH :HMOGcȯ g06YfH1}@?l0Hnٴ76|u&b]ɦ<C۫9%.@0_zlx#y!l=8rDA=6i8 z+'bxϲS6q˃E)6lIfB%EFY)$L]zdAF$0 ?N DqIԩk=JYh}уò ?1SE:w:2!/oYU7(\w#[c>5cl+C9eK#i1s11>y $%槊-95K~$Gs*4xMTfdUPTͼ0~/y;L)iuȊKbO[32E-ָvBҁ$b/}^p1[H T Ih(ܮX:oLZ=y 0|0tdMs4х<8,Et,"7\w;W=bÈm~7#rYXMۮ˃)L;nn-=aT_tl%u}1Dz*Jc)9O;^5;h^ؑxqS_8X>Pp"<}l4aVq&pC0#xmYՂqZ`A@=RckW\<\JIp#n"qA)3Y=ĸF*9F}OE1RFyNli '}]t^(P+h]Q{p/ 6>mB~Vs%Kk'K%2P_6B5d#${tcM0+?R +4s-^$oW}" |椔=1HTK@B$z/$G @ޯEN8CApj|A9t@{#k, Lh[zpf`'ntx,:~j/ƪ\A%w- /K Gk2edkvT(C~_hdv̿H֨V$OhƲ~Ayc|RL L SΓInkcq@8b'%֚3Y*_6 2P 8n^r_*,OкQ^(ڑ`:w/[RD>tDHyq'Sd9Kه/9Y=L_ ޫnLMMW9+ѹ:O&U <PyM&xGUױ=Ci9 FGzmSvTԁ\޶0&Fo`a~3RhN4?8b!)[*Vouoeeb:'1Wz›>[(6;n_unknkW-Ү?TϤ_ZP,Y[ &:,˄ꘒ뻛endstream endobj 489 0 obj << /Filter /FlateDecode /Length 251 >> stream x]r w70 ,>-!^ r q}%9${8NlzkMRSSۓ[hj8Ci O;R\[CZ%gPX?kK~GCphADh##%NJkuC'ׁ1R'r DADD/<򴯗=g뭜QGX7 _LKſƷ4I:kTendstream endobj 490 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 1714 >> stream xT{lS{.4s6SV{&6Mm5Җ&8 '!rlv;8?;G$Ct4#h(T6i4{qt#ftu{}~HM!D"Ѻ½<?[Sb0H M i m姉h\RUV5 B?A澢o8WYU\&8@|" "%~L%^%v~ihȔHy %I:%_Yo7)U*.,p6 ƃܽ#@-s+p۽Mڈ:RF)FZ9+: eZf2^˰`Z6JrPx/v},Ka7,z4ϋ&vt(6l;qTc(j9#{\N|4߉j.5mK:~RwRRB?j)dIXt"ה8,}?c]B.̯nLj5~ތ%8no< Ć _?|^ rF0B3^]mC-^W/%P>>Mćˎ|7_  ? 1 Dq-.'vnᥰvǏ.\4܃}uꃷ޻tiCYa*F>ﰩ^WiUD8cQ#ETYt;N)5]*Y$q@b$yQ.^tngLChBPZ4 T.c(!N|'5Y$ au$.4H08GlfeivQxՀJ~DQΰXwGilCVk: ҽ4+`'ha[@E8i\NAk"@Y*mfbbٶO9Do^vD"VqápAoZ]@͢/ŘNy#V ԋg2ev*o>P<'q&g|$M_ӊY[ϬTWhyox\ԽG#F*6cMod+Cgp6k]hc'I']APp MqJ;̪-\]IUck6ʧ9e`MA33hf*p!L"\/6>Ox_~.am.k Kpu:-:n,/9PEGK{@p&srxIUIGJ9BQ"W mo {v$ %:Ogy`S6C)batlFӮh`/Fț@ X@xe~ ~K!:]뙥9u0XT  目&(G H v&KuXMf 6uf,kB&.N"dj4l9\Ns9Qu96h8#ƙ  %-W*Nb[~Rmz8R:7\}endstream endobj 491 0 obj << /Filter /FlateDecode /Length 249 >> stream x]1n0 EwB7V \%Cl [Vu":ARCk.eH <6򺕗|K Hv󭒆ц{)6 _ÕL'Y,0ZVendstream endobj 492 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 1882 >> stream x}P7z2) ܮT[kמrjU bիr'* oI@HK $$%"f!(Mi[ zJk։>Pw* fsXԹKwGhDv}*\]V3a -u𔘘G N^5K6}#qBS)O(364h"&03aǩ1j!i_@٨''W݂ӏ _-UiN١}=*w p B{2#.IƧiZ; IfHkXb #IWo'sa3S 1-3Z_iB.(Be/jnKݙ%$;v 8..ځ;hRUVi  YizԤc}y8/}{xaB 28h9l̼qƱo|>> stream x]= {N ğHИ"L .Ƞ}USx;|]O=/|o2K#FXϒ{CoUHB`eMǚ&, HX*b(ёJt$J+3 ib(c<携8}(eendstream endobj 494 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 1086 >> stream x]}LSgWaENM1l:B52&J/ҷ6e8vvVcg%۲eq8 +ϓs<'y"@ T,Lݓ1$"Bqk8FcDg"& ^ZC3VSש9jmMbfmuyF\]%B|S[+)-C( )Q>:2Q:?" FY¹uU$–id BR0Tde*1Fh8@?dpu[;U$hV> ^P>brc%j13;{\.l4L &!KB-a>H)K/Y2?+SI[(QIz-  ڛ`+ӈ(z1[t98We̱(> $Q 7fl>SP:)+*T*p.wk&s7n.XIV,fsLuUGt56LG 1e#X;)zejs6AwKqK8|}%Ƴfq~NׂvP 9) /%WXhlkWwZ1ҚXjJӅG[LkZw7Hݨ"=8,$7ؾ]X_\Wa}!Hk0QHELendstream endobj 495 0 obj << /Filter /FlateDecode /Length 183 >> stream x]=0"7(F G@l$w⽙/];gg. gn'D@`+\[7 L\n*?95YFϺ:rq 0*7 kBHNu7[(YuBY@PU|_I;'/1]endstream endobj 496 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 534 >> stream xcd`ab`dd v r240q~H3anq7k7s7ˤs ~g((土[PZZZ&R )ccư&LV76P>*;w\F.`׽w{w~o̢5aWZt޿b&==[=5r>Ey#\R N} xb-C۶S$lZ]R\?Ek[*+92mܸl[Z[+-R3+N> /Font << /R314 428 0 R >> >> /Subtype /Form /Type /XObject /Length 64306 >> stream x\}IJּVqWP8;$?p0`d|M]8e&ڎ}u+g;KWzۿ~ue?~oԫr[=ZwC}OY}IG_6 !ʴ4%16C^^Z{~oomklre[‹~?K^usԐ6DR撘)A695ǁg=+fJ;g\Z9-x=%%AX6 !u/5K!%80v.-1Zc۔^R-R!y^VF ג^!1l)eok8`n-֏;&GL'e]ꥥpcyKeۯG ݿ&4+wJq3A+x!nϥ/J܈2-p^5{vd_G?_V\A'vx5k'ls}:Αn zOƔR2]xݱ;bMR1M(mb4 I0!ȿ|0}?5:rcyDYoxλ]xѯwos 1Mo!ʔw0w;(?C9AY籤;<'G9_7^;4 X~p~ {`{(!`XW[o5r4݇I)vA.m'?%]i5ƍ(!!/C^8x*7AYRٹ~xDje$;e?9z/B[o?==~MDxoˏ房WL׌׫XS֐6B”];qH sKkxwc+&/vog+]ψ0%Q]`L!5_ע5)xJ4oqYhT% ccHa:^O>Ii8$95>Z{\dJ̔c2O`z8) s7`L0pôm0G- qઍ\,bR;ֽp\ _"8n>,A;85$ HVpȤMǎ0??X䚱-͖xƓz%7s]z s& J<@ ޣ7{2V l|_;1 '&v%a a3Q?W|ݶI!!/CNZk|)WLf^ܔ/5i+{RGxʃ#CCb+D* kX dya{i qhZ|ǶX5%;%ئr#o$̱ec딆yl{:- 0x|cj \񁪽unŚu}Jn/}VcYCwkH !aJNi A˱xz{^кbd4$L)kR bo ^Ht ޣ-n/AYDŽ7. <\K8K")HL0{aMs m*1l(xņN-J6MËƩ;1j-v7G5$zfSD* Ɯ_搓VXySIpGL"꿕(}D0LaCh; E^P('M`$^6\}]S8ʋK\b\#Rc_Ks`ARc٘jbd o98x+ƌ˻)\+>5[/ nvW|}Mh9CS%[ `{h1p2':feMBjf5(@M_CS<1 ̔_ɠ-R.# <&p!ω}ow-\ Y0- 4 QKHl!f}(tL LŮNŽ3!|oDc w +H:[CKhڝ>>S=-?om|%[q1]}b1q9NIXC[7 kw/l~vv^ڥɺ9oģ$e}M?VQb`X5$1QCbDufAj75na)}RM睱+?Jiı)T(շa\Yӈ!g{4ߌ[ݵM?S ؎=3{s=*cX i/:+8(` 2>9Pr)uRC\㈯ a\c8 ޣȮ]m mi]4,]ksիMnvS"5$>؄*Ό&vmB8$DrIkx 2=ؔ+5;vzqM>exInĞ~$0$aĊ]気AzBӱj8M޴!ƚc;69NKHl@BeX]j($dC8$%8887A=ZB`X냍Sf ,5aH=PN^jNz#i-^ v&|`c%)%&,.4H28c%΅g놾3gر?R}-pibj Ɨ2%a At0Rz!RC>9 '! |xaԟ15[$BLA}o e7u+X߬5m8f vvQ /:%8h=xؾpg) k0HʁDjX6C^#88ZSFؕ>)  \0_h71i0Gǘs%SuJCDχ ␰気ӓV"k5ݣ c]; EҰquScNJXm!6ErKkxۊ~ZchI5/|O,$/>7p58 k΍+0n&~ kG..Zk #|?| 3Xq(݁'5>|zRp MqH sg)xAF1nnn/QvˏDe 1èx(ANi86H==cΪ:bޔhb0Lx݃b3Cnk ePUGItpۀ00.B=\SX6۲C65}&OSDGD({S\mC sGkx#cT:^ ؔ/fa `b08Gl& kH\ő{rk)[95 H;%%1ig`m8gAHp iwk8A:ahQp{| -mdDo ^ E:hczK~Ch5$>'' [AMc-'a'= ~nku>퐔{Ɨ﷐0[7q͖ioSSEmqH R~CqpM]/҉2Ne Xy5cc%) i,6qLc qiS-۱7]/]l+dh _&~WjlJ~-fbEÂ=0GL!0/SAq,|VDֹu&AX_r>_~m KpHȆpHЉdH=<)54O5]ő#5Քؔ5ۃ37 jš(!M6@#3`h Ahj`/ޛcpʔ0b[)}(I#^![S*[CM s)/a/=~@)|7%,u 3U e!LH %@y ]vY*Y6&$%čPxkf6(a8;-nL%xIC..Z{)prA7%z4$DmlIÐm+V^^Z{8Qփr sM "Z*"omBat"9m*xBZ >)$R|13N. kH=?yļmy?08k}<͝ co}@Ve '\*mqG$[*8+ײ 1`h [-rDIxŦj\!Z͂b'1 sT馆s7DX0lS6 %l/8y0bSqXfkC@L=Ʃ 1B4nj5l %sn mu 94,?\Y-/:?kXDKP )H I)wer$mc 'A>(֏=K qdpfA#^QE8HH.Sq+00[ĄqN^l̑X1nWyptIXC?)tp"| FT$a/=@L' &BSՊ@q 03ã( F- #m{,8_ [29qŌG݌0815$>)%dsKm#T^$8x yވ{@IY0s_o=t8>!ŧ̱n{ș QE15,nʴ}n,ӏHxUϛ>)[9Mi)0NJAa*X_0@AKawl70 k{MQ/SQ Y[OaFJc =4D0%`k4A0(z= 'rRxq ݜz #{0G`LD钰^bm#␓VDF/b<ыLE[^&:9?o GΠǿ-gF.B?"8C*3REl8TDq=R_n'?,Y iATL| &lPT Gr!`Xv|y◔-ʂy^? ?Jb~8$!1mDR R! ^8gRqሐg11-! ƴ Q5a;Zi9 @ WU6bM((#̊K2c3BDf[EKDe,er$m~D2G+xB"ODHwSʁ[#Q>P'f.XgnJcVIL):rI : ޣ5ƍ7Q:YDDAW$U#2h3KBl@B(r/Dj"dB8Drm)x',d|܁W (њP11GBQ 5 L ƧKp$eqH sI+x "  ; gZk]>k#ґ.H*ְ@2Sb761ǖ95kVs(5[|SͳqxnFVKibX'57|iCbH[, "mLMJBdaD:".Bc~K@ w=Dشq/!-?cT륻1)"Ae Oӡx*8By6NRE*eqb' M0ž?Oڔ57؅h%a<!.;[IѸ#'YtR*ff f*gM )nKSAAZCS9e)xsEHa6qB)7~ 91[qKWIHJ%a!a/a/=4M0}jj, l~Xa,}]Kܘ ʐ䙽 =0=3SBFRP*ţHs4VH1֐6 AJNXsl<#։#&1搗g\CV%ʦV2jtC=Q_J`L0pNo4Gm|5ԓYXpJ0HWa5%S?(URaq~9oixUo-9+ 9Xꗒ *DoD4X*aX9b???۶9[*x;<ˋSٷTV$=1'%aXcڀ)k bh s/'DMrnHE`qGAL7GōćI ె" l/a/=J _SLOWj r%įO|8GєdIH 氋 *<ٕ(m˻)05Џqܦ nSzɨ`#qs9>URz,fe,Knl/>[S5q4Iw6|)}~4b]){ 5v{tþ5vXZ&{hq5FH$@lкz\H=^R.)IJLE@' ?u~SIS?L#ŧ> c!={s 쬫#aR&Z;d7" >+E 7`LM1b95VԾ+]l,E MKqԮMMiH@Mi qÒ򠩠JC>#}?Z`w$]i8s$>Vdq0hc<(1z0G- qh@!qmx^fxcJ?,4ssܒMW83N6!]ICN(TS-h<_ٔgM5QD“cǩ烣YBi mDKmۦltm/a/=G#甔=VY[xD = Y@ iT$Zg 88G+}P"W*ȦS 3D&l" k& (mİ?B@UfKdzV9.5ea! H`jH|8c̔e]0]{k^{Hcۤ_Qna-1nP1T1֌^jPU!w`Z ޣ/EƋcRq$F,ÞYqܝ \m{7Mc3l]E qcɝ)oVmi;\ɅRggYuiq9bBDEaÑ-qYBi_;6Ac qKkx˧Df}(МqĈAths?4uM f@F`4HyP,lQqM蓵0;D\WԥkQ~ᶀ6eX em XX@ ޣжg+,ƱܸU+>Q qVY깛R6 !J4riA_b{!RCO%zo>)\XQ|PEt&9"KIҐxo>)6eXKO?`е-Ǿݔp~r[Rats_.'5DЬR Ah3 sIkxB{u֦D8f@hrL18<\f2idsJXC M5Du6 aͺ !75:}\(bMHTwa#:挒u&PL9NIXCC%! F= NJ{Е}janedԠMEvWKܘpX)8lPH xɪ:7\8YeאָB%sʭxv1唄5 q気d9){dy(Qij1Kquw딆^Fq;ahQpз E-+ cWw5C+b*%a sRvǶ1$a{i q(1dt_nYX9bV3>uF(s}0D?miW)8c-fa(8;>ZAfd@lIKú[xQ$0^0T jqjbmȸ95dr$=0:lȬ|(0咄4ۄ V(%p0?#@B ;'+"X[Bi"\ f McKN$()2T=)} p;wE%L6 qg+jX!  {a{i q-{3xYs`,l R)>;/ōiI(Hb\dy` CPɐ@~",yY'-+K֐XǛ0-mW-zU7KRxoz)oև\EY#qP H ?1wH= AH2o!w>Eh0y?7}clN~1xnJT vvQ1ESɦpܢ v_h'툕k7e i0>(ؔ0$aCS95ǡL#lGD$gS r, Y= p*l[n6R1m|~U(A QaF >Z{J/eweA|cGt$ާZn5q*$qߜX1m@S>iX!ϯv ޣV*䵫ϦHisCĩ% f'3܃ż&B”i;k.oj$a qhu^EIaC͍ove=bLnV>7GaG"kظ1)=SCdT0aSpB -S\U s(Xf0e51B4nQC3S*'39o}RJH#(\$:85 ␰H=y7eMcQ267۩c=0sSfsZt]MJiPv8]/}0Cz-Qp[HzQc^U[s w@=h7ƗU2% {uY=D].E*K 馲oiLX%r"h) k0 q"% ȆJ0G4ǡpWqsMcܳTSU渑fy"tq[a+d'la=I/< 4ˤ>QDw _L847N8)`ys-H-ĨM3R?гhF)j9\6e:8T 5')GDY< D kβ⨖8Q.0bHEUZ6%G%Q%a %e*~"h?l08yY))&;uߋh:w և Sg #RÐm sn/̑~K{Y-/XsS* Em48G5%V`(Ee78U bz>,~H(M~JJ8oHe}{QB<i)b5IFQ95yU_칸vR.V'='qĥ",bj֊ `޸vRTA+_ v CPs~iS')C p6&:bh>D(xs. #T.# T }j$ :.qM;p*zĦ샡J WWMJ玌4 qsG+x`f-M}Ah&ܝy݉c c};I ұ)4YvAA@1SX'Z*~u)qBx6D9҈/nfV6o lW|m3Q/숙 i3cn^[|.b la=nW'k^ T<2s A8 KCbڀ(1کѶ b qgJG|?>ެu :ҟ<w,U#mL˕ezWkJjռJWOJ@-٩2js!ּe 3z#4U0)~i %cF(Vy,R=F4wijS:{ӷˌ2-fjbm[%5$L9x0P\6q* sosׁh71?j(e5"7?Kͽ q O!񡖴I9ل'5 ۔ b気_B, 6ck|ڣ]41 NI1,!h ApQ'e$vF={Dt3c`L9BS)iMt6GKฎ 32HG(QN ]`U`NqUhNY$?C}U؂zS֔a~qxPPhci9{|-C )()@c-c̰]?CЄgBeڭ QfxZ4u1!%ֆ9C `L [Ú?MjG>tZAVaQUX@KHa[Q.*+l%q[䰇RjY' ʧAAv^^Xl9 5PXD]UFأ.xLJdxS#qHȅ䘖xKASQIav ɍXEq;$a7Ґ67: Toj3N.vئ) ]{k$oV}kHLbpQA&{!75'+;'[ T튦nt Id]ԐENˬڤLT׈O'a'= Maw-4 BnĘ5RGϊӆjj[Cm~H=ꏂNTeMAhmpw0F?_w0ǍT`&Do4OC n,&,oHN,\JW, #AYS.}ӄpHȉNsXS Sb_VƺO*T}Tl&3 AR1-@Rܽ a@4Y!pY4ڰpR';7V,*C88NIX1m@_n [16%a[5 IiH?Lã.6竇=8*[auzRb7=)(ttAAxQs:rjT:E'u $FR!PO6cZ)臙 l89_CPmk q'%6[d0{ɢ1,r`kƓ'>Z{^-=ޤ\'[ ;"G#lbҭk>xh JEZyj8um̻^#889xO)-擑N:]ԗ~&y[9᰸ů \HX#5sPHѢIY8l3>{ ^h0LPF.PvyVk f+6( !ȃNK{ eAXa\׹Vwaf xb(*岴vbʾ9#PA  MJ *=6'n~` q\KHp7tKHC ␰気cKflvX 31A@k2= BAb1%jSzxmByY= A>(GR""fCyu0Pٞ3;Hay ~ 搇@;ظ 6',Hژ!LX NVۓnb)`m"ť氏QHȐDyAǁzf75cGq28+A8Ĕl 1Is:,,LM>)H[n(WĈcϗUk1QTO΃#%o c; 8ν %uJR*]Z?b t" ~b1Uc x#ݕ=Xw75Jx`N|$k rl`[Cbڀ(ˣ=lqPcT̚6#.djvc9=iH|0' L  95FRP5߻)qu#6 Z^Fc̦ᕖ6*)Q3 ZA-cCZ{҃洈?;qrT7ڮu>8NKPR]mR.RqT Nn-4LAFYzZ1gߣp{D|GmP CnN~(q#FՇzA-PxxR&&[9 & Ǡf7| )¨y@f]sGt J*~Pw+ -o'̑^Kk*6~5TWѧ"^؆Q#~u0`L!aJ5D҇ @XCKF`#OIa9Y,ߜ($̡jV`Xfg4H! .Z{x^*U*52`8ߚVe>Rj.,i i0*'):12m#␓VDFmʍDF\#[bERBjҙ/U ڔi iMec"V(rSX.E=h3r@Uu0Gl aѻ&)Q2zRp"jh F0}(w0j=4&eFhP8HƬ= ıu[B|P;xqax8mvkۚͪ ~(Bcך㴄4k!]w@wKtv#P|˰'AaײI8gWKXvPm`L}ζ bT둄9oix#/yŗVF/v vĺ\49!q6\s'_.W^.Þr!v#0λTEg`PG(k Lv%FE}2T Pސ/ʴ+v6@R Z{ ƕ3>H|'sCl*\ ۦ0_wk8PW-M*}0}(1,YL3e4VG=;BYc,|-!Mc@̐(f6kh)᛽08t>#K7F h 1Y u8烡Z@ /u>V&TsG)3 - qx}RQ'&*5:5+fO. kH\U1)Cm^^Z{\4|ęILu<џ`08<v$ل>a]0G:- A("}P"G5!HK8#PRԨps2DR$66EdRC*/tsEJ=fx,Aʌ`,M,!/"{n±!/~KßqdSeM#8)IU=;&Qm\:HxJ?(߇%at='{a[`mi2B/)45rc$!JymJG$a}95ǡ6K#.뒂>GzGŠ8c>4$JJ i!¢VQBqsgɿZ3C)"^C%lΓ5_W?51U#-! mk`{a{i qdC oHʈXhBo~EfGR zv+2- ]3hov˂oǹK`֥Ix:ʌH`-3b i8&7M̐^K{ w8p+#)7 V7x` 5~Dds35M% hַ aD`9oixC/<:C9LBCB;hSӡlέ.eXCm1d0}(<ĻP(ߢ!Łݵω~6 j4)oKC$L"VJafH= =igsrm)hpCsUzue+>͍T`L0'ᔄmQfH= UJv3R°|tBDR8_8&;C8Y&5FH$EҀ׹m~ҋH=,'I^w>ǖQgg剐9NIH,|~,SRAg&??DrK*%rr:YP0ěЇ qU¤LL $d ޣP`ܜJ0אYtZbSPgiHLXP56uƂE4E, 9#6ebdʉgg6;k8{27ނT vQ"XQ)NY}ΧƢp+&bdAv'ۚ c# sl}k0 H [) F0G- qx!Vx30T $\W gxU=@+8"ڿ-lO[v-氇#/Ob:grH鎞sewK5$0DdAay!RCn[`⃂݇:X{ XwCG5$>ַ(?lP..B= u=زzS` UÔ+(8]BAiCiQ]9m mpC{a[Y f?y(¤̭(Mz,\"H1,PHg?ܭGr/s|V~S?n`UksL d*'a]-R)򑲦tS" ! Y+N'P|pD B2R ~m/H('LΈܽ 3%iZ@GzhזӾ9YĴPH5TM%i sKkxC)77"UXQҠJ'1-#= ?+RJ1IQ3a@܁88[)Il|~6eSCZY6??ErokxCulq$m ZtT-ly3 :GpjSa Ue )_j(짘6CB^$L qi6)>sUd_"J։,:$yj+J&CB* a=)>}"57fw}+Z >(3yG%UMJ Q6oNnqH/8Gy&]R$Ŏ,ՄȌC2T X1M|~KHCX6A>!(wۯl_}XO7Kq`f؋U֒㒄5$N@V%`ğ95FJDzLhg_] %q4t_?qsx_is],! ǩ , zLH=,$G|%LNáђP! JI˪m-!7]{ ԱtSfga bQa_-ᇭ[}0D9{+H|uXeع1l(ԉca.La͒JczE'7hc^'LH #mĩ ğ95qU01)'ӝ>saEXɳw{pD x裂b%aʞH=NRNLܔP k]ndX_ [ܘ#LJDՇ+kaD Ӄ8-A!h&|0"#GGBԐDPb̆ٯQ钅EU&qǛ˱MbF,~.%êt⒔@+^YrXX;ƭ}ĦC; ޣ|N&;Rzڋu4+DaS{$`l)y-$dACs+OXœM@WaQxq{+<: qaڦT>zVPH2 *wg Y7?GZ}zxE"<zvQW'[B1ur ^;h4" xNax b1 F n#XQNH=:;)wSE*f8 *':؄0Bq9Gi3 _?D,Y|+کK(wK\/ZbZ>6QX??Arekx CsMAxJM>IqSf&Dop*{`;h!`5"r*) Z\ڮ$$EqI)X ik$LfkNOA>?X!ɉ>( #>1>hEZP'OJ[ʃcZBiC1\j()QB͍$]ӕۍ -4AԦ 樖c S"7n!>Z{((t U05 5aj|m;Χ0yR iڢL۷H#-{}CDPEMǃ%C/ ??{PoӅ7ԇ5Ӆ3RF5G((j%\TUV0 2ǐ5ӆN\*NE1& sbJYgu$sjaġf`L!a+P4L6Kf\K^Z{^̓71~§ѫ8xzqƳaRY5IF0081|Bn蟢b^SBQͧ<45 mpSAGm4vv^P>mD}JU3f[(w>6ic"*S$a vL[4A# NJ{*WN,IxP.dc>ڬ! h׃fuT؅RS6fVrɺso`S.%6UCjHиU.&eX4t.|kJ}M)nJ+3kMgI$Qo PS- O qj*!ܔ2Y bQ Tl!3qژKp J8 s,ȑiC(!  95] s4 S >Gnoс̉Q\䈚Jl i󓔨0%խ!h6a9.KPßqըÝߢRhȠp>*߬K`_Ma_jh/`za[آ_3Ȳ IdgUzQH$ƉR1@1<mn3L/U Z\Sܯ UlcRErJ5 :6qM'`= ~kLDh雅D󧵊PS=bYBX1m^=-ୡJ6?? sKkxC}:kOVG~>v˓,??NqDu O@ŷڃCAEY(Þ.w;ht),(; +noщ !պ.v~xltXQ|h0T@< < &냧 95FwaC8|a$=gf5CQxJSC⪊1I)/5 ␰気O_42vLJ)XLkH5 uLJ,)ˌ%yfbYoi i0 u{i,* AnJ^Z{Ji?Cs*p}Q`NQÈV>?24[b$լ(x+6l)k[Ћ_TX7u=8by>,m@mM$~@$C+o]]K3޴0#%O16 X>CR)ф — vp~.A!|p ] GV^d̽vEH6{|B菳Icoo(UPe^?~yʢe76er>t\ݸG+1%J#Zn5l|xʔSlNNxT&RC6MO}P.l#vP;.+1`,!5-6PLʌҠ:qi#+ɥ気Zb CwSoT׽V6P#u<W{( b (%C@>$Gz-Qpd%MA2u= Vx+K(d_7o:E۔N˷h:J{ P,?'It[B~&31oYu89d:5$>N#ԫROr157| N޽al]|k"m~ҋ>b =+?  Tw}TNU+,J'o TQ i_P L..S5QdFIam9Tm%yĊQ8=4%DiZM18Ki339uȩݞ-ri-1, ִ-CIJTJ*8 ᐐqXBޣP)`o}2eb<d*Y;Lk>v!$! g%RCM J気Buv`}P z(Z'72]%hL`UQSV-RZN׿`"aCQ%S,J 3NV!fqyǖ'~lёp[Q / 955iU3!)7ͅQS.mIf1U]s~qs9kx\'ҙm Iܜ4L4ę\J }F:׻8"G{3FͳnDJȸen;S[s̩*%ĸH`Z4!hHCPѺ $2nYxC-A`x?JCKa lyhݢ@ @Uih'c*Xا4p`5atl`:)Hΰ&gX%#yNf p/A'I9"烳P41yTzN`D-fV8>m#쮀n4K9oUT=/!1 q>@MGcg>MO;x owϤ?#}ۮt>grp-* Ea Gi u7#O5-*'A$~ [<ى6]4%;4ƹXkJwxKe@r*kzGhHlF#1! D1pI.H JA{Zo9,)(DeALO M~O sf 2AŘHXņSY'L7 QtבQ*`>>?K vTz|DIV,[,z3,+i0olD麏ayms뮜Z(9,QsfŒN䠰 EP8Man1cѹg@1t19qDo 4)CSiۉޫznAsIpw~}'6hXaL,Ϋ.-w,!7Kxqcr&hQAEH)iq._.s. 4R;+P)+ЖxbC4 -,PO,F@d6JrmuV>HVQXQ{'kL  RyOT9U}i{9>fQS,%MJ8diA*-sQl I @ s8}BQXQ{wV/"PTV4c3-u8J)`mRHoĭtqVTg gM&K /DVf@h<='ٱ}%hRtBss*z_̕2KdAuhD6, ,idԍoT$̛j@9z*%KF:cԑt9w+K8B[x-A~qpӥ~. 8S.ILkЀHyNvLm;B4N֐n*G}m{sb&OW7LP\&)qKA$dl"[*%mGc#uB;5#s5:&!|$ Ikp.α9DžC{|_U=,q%Ċ%4Q7MxSmS*+`U5ǪDsbv(X;'e@Ez05 | t@sbDL%hs_ik` Hbs8uboU @һp xM_丄e0Gyj&myInƣI:8S 60m31 נ1V}iV]B 3C֑(9Vl4gb"%-ס: 7TCnp 'p-4{ޢ{1AlDD-3SO EAZаD- uP}b;և%5"^#zPj}_خF=ޙGⶆ,h6alI>H~Np^j/"Gf(㍗9n ~GY(RCkm2'q`$2Dx!S40"CTdLC:jH"f.I)) >˓2 K8J[xCŋ2,݋X0c^N$6c)000M^S H` /~d Ȩe n9^xĠũ~ـVh1؄ʖ. DZa4αP璆,(tC4v"%-סU0W5*_ Y/y$4l!KYx >4vVҰ"6obFCdWխ cIHq[<ֱp,۔bA4Aʉ" FԸD`tFLY+-Z$C:g1,V=cx/pu:pM@j{M@N<"Ѳ6fKr4.]8H#,m >o`8Xĥ x HCr 8GR&/F]/`ӾϏHŗ Pbr:jEZLFePjIM4 K4  =T۔!itW+ K8J[xCЭ܃c_{ekLh|r:BܬҜD094:{Z""Tj~ϸp%} ҉iK{19:.i'bFa Gi u0S[LLLS(X&G0% {鮺)@q!w,A{cR׌ $gW"oz/~VZN$ɂi s1Pp i*OZw/ꂜkӹU?BX0uNAOEҰwp'|&w65w1g--+ B \>@tqq4lbDP 1tIlps4'-ĿT `a(V(,9X 9򄮼CK.HN-T Z񺔆%-wWa_(х$A)IMl% DDZÛGQCc3'o s8ω #㰷Jf ª7ұĔ-$]3f$%-V**"E@u/iኧǮKItlV *)Qa IAScQg uۧK|(,(m:tWИtiJg%O*Hb-^0؀hζ7O$%Ԁ>v~/[-ڿ19;Sy>IU`l]Y5k7sJl{@x` gW9flWr/.`y2-@?$:]itM@s zbAZPt瓕e4 [I=QzX0q΅ٲDžF͞y-"rXl cnCq090cztj]UզmK FwkHAk= ^&'}*n K[Q{HI,kȂi98T6pұѨ=$dx-B`LR4!A EV?$k֠ù_$ [Džh!p뚘5[hNnТ>V Osut92(v@А3 rgpJaumRzyh, f@ ܗNhR UUVc?n %KŴRH,MJbIw8Epaqi<PMy[Agy6]%V:İĖ-$}/196ewRLJoNp^f.ϣ2bY&tF֐~MNcOO%-K+u&Əug9/HmӾ{|pp;is@,%doPlbE6Z4K%itXuf-Q`}t}#?9Hs8!%pYƢLy2$.MF,UkIWg'cJA/OH(B?fpbTc4ciQfz =&) [0]4,\h>>H~Nȸe HZg'9DwdZCBi-HF;,*Xui% D \pOh!*|Mc[k)HxaNc48C{ ~Mv<847q/6뼓kx@r#8n h,JIͥ2% Wu'995B [Y `:V55d|P2MNR ҡ , m 3p_rxF; L=.H;8?>CfT\s BdE9[;>Wg8s"UO p&";9UCJL/g>%s` gQ9( Kdܲ^G^z^e9z@VibzҺb=%8X0= +se}G(,q{|fkv=/D5*)Q *V6Y2]ч}m11q? Lh(F6owa,Bnll-2XҰ=%9Iߤ9\p^d `bre$WGǏ It[A{s;d,C KrA`hUhKed-I!V P0ݺ.fZq)K8FxB[z΢#"w*F p3oO"PTa4^^K`R|g :790Uf#LmIxTέqnX:לx$M @ WkҢjKոVF>fMj@LtAE'}^O` RSmr m$;JpQRbi $I)'0zBlp' Њ޲o)=F"oT"؀iδIFH $2F 5#@:Հ.UXhg$XyMӁz{cJ>HFY3bWUqRG2*9{/э }f'EWRb^֠ gZCb^j.q !J"x}}l) N)ąFߓH#%#MP8_}?|ƥEIhyHdܲ^.ٚ[gqV!|ȈAC`h gk[zgiqĥ;8t6Mc 8.hf}½h +ޘܕ[CZ6֐Ľw,"Jg !Z] 9< o$✊˔͡qb Iw?,<\ހ^c[j<\Qa YxC%͛7SYضn7}_%}'$xڑC{[i ۔p1/:1sh"okض`,] hĝ -Էti8χXx&e`,󴋡%-V+7EELuk\C^'g:G_C~ڱk??'K8@[x/AO*1.fI1CIY Kcn]. [h9>a2 K8J[xC="<LR"%p@DU.5!S";S=&p0oL);|V졋٥3i8-g}Ʀ偂u!c$=|~CV>BYr"co,A+"eDه{:e^K1 yEҺ(hEn=8h^do894Wǿ3vD-lBO c4,q{@->c|ƅ[J(4(jA;96e @vp^[嗑a9q}^mVv*$Qg$ QYH>pfR>z8 ` XW,לe89߿ 6@ۭ[ƃ(PGbIA"/2Ϳ/h X9hFYOI {$ja@4.t0 e!H"C?PGuDIN])ޯωュ2M7;5j$Б%8,~wNkCμUHZLHb ¡`F1[M%,W'`EKuNr=-25TₐF}+'a 0Ié6) q4֐c*bACd)i >@T CLO[0YҰq8mY/j!Z}̐DF) ֡6Wm(pQ%[vq%SIV!o%gɰq.HAX1{p=Fʻpr;FQp*hR>S"iƖǁϏ#Hl%y/Fr1N6Fsz D}BEbi/|[AZDa Gi u q[WvrX';'Z I?<8֐wC/-hUqe UQ8 K8J[x?H% _rWmnljH44_ XC vSGӉZ^,N58Pלy6`ҍz6W%nK$-މn caI>HZLQXQ{,SH&]4`j^6if+%4la^vp8uZT>Hdܲ^ K|K7aONVKKRDFZQ>tVTuىCr8w拏ѕw9֠ľJMi!i8ew ,f5$$ Y ]'\y0]rHga Y0MaNMYAs:T,SmɉFM[ȍ˫'3Fi0Y祉X%q'9\0g W֐&AZ0/fqu]y8 .` h a}bሃ6QJ8ڵ'0tBQA =ROݍ?gN@{ӟq WX-qZ%JrnUM B!Iߕ@TIWs BT@.D p6^]_$f g[4O|yb%W`zp4lGSqwb.Y&`_]+IUBm!k!CZ(iE' KdܴgJ}_2xY.lrx:(?=YHWq@>"%dx/B݅/S0 q;4R '|X0M\4d!>$2+T/_(an~͉gçpC;.Ipi$V*,:A&nG ] p6^2o]c ҷZRͺ4,8a!骔dຘ㲋K'K8H[xG3 +:ӁXgWi{m*X,..0ݚk[̉S@;/T,m <6+,&k4u7nҰDl; * [(KuWQ %-KA%j1$6-X`]4-CwcHZ~:?X]nuw^dN̅jTGDŽ46s8*a4fSY8O# 8ىnF*{p psmNΕ4Z6/$ DАC-FC@01=DbH hTؕOB`oqZ֐(`;뙜& [h<>gp^]0 `i3sFX6-L-JC zy,$,%l|?Pq%'߀)D7KjЂi@8{Om!Z]gԐ"O@v.,9 `X6Ln[$”J)Myɇ#C@H?kӣV2AȳÉ=\JY7{؁dXDyoB*-` $mEg5otl:4K֐氣XҰҡ(,(m B_iڨGm4a[{%ܰ,4(9ĉ-!wH"%eZxCn{86xB{$㟑$vqHD- u==ÞSYz#Υ),z#[uSCb[CL4֐{ jXIb7y,TN ksĉxc,T1BBbi4}6G=dHC4[;loޔ^K:*0O3Immzt)؀˛ùrR&iЅPP )Q_cByub;n$/6m֙ X0Mv( \Fb@,5p뱇*G.5nn4 zwkІ1pZo--rӘ9EpqӔ>.o2 K8JY~WQ }.?9qFhL]#Qg$?4C{!Ȁ h3 C> 2S p6^f]J G`[eIǹ钆$p؎CWY(8|ωRCI"lz6g8 =cCf\]b f2}Z8njlaY8",M&'1z!_7ch`&^)L]GM{Vvq51ӁϏ#K* ޣټ T 9vҪC"~ba!#s{(՛퀴%"+wte8E8➅͘E}HC8fbC&9-lc\t&1ǫɃvƌ~](Xj5<p: 8+@<,CCAL U kX>sZ8- [`Fǣi)P/Dt38qBY(vI$Q$1U-by1gRGa G) !:^[ ӱo?K*xbH EZ f0|I [ҐM`hUplTmW_Mm*,#/5aK 1vBU;ɹ H ҅|FH iEBEh P{ܜPY&_Y/[=x IM~H=akQUVu9-A= 5/LE 2`Z.ԶA5` R>Dc.Ii**^[H4e׽9PI i9Hx_r.۔> ,m ͺتAorP|E@xvnBPT*5$AsuWrs!9^,Z6 |NL_gȧNR2zF[ڍIKL͹8f翶}(,q{lUuc&q$1pk \[)Xztl fDY&*d.H~~N p6^ʊ.6_1oN9.T#U$y uӰy3 ">_ ǁ_'KdȲZvD׍+[f81D-yKĈ~p"@CH"Cͨ-l>V#nkb%WC*ǀi@ 84@Pp p6^Em Z~fxMES/c C{@YHox_~xAS VwLHĜ7(b2F|}^%n+K2 @=02IdgzftZ.KOxdI%w6Z)gu"Gy)$KHtzO9\! z`s i]"$ $I5&e']dȨe '|1U;ͣ?6EdD,$kRlR ]LM>AX1{:NxتH*{Րس}Yq$z)7E8J!ik⺧}8I*KdȊt.O+7iA $Kռ='<. [HjtrmB'.@cE(c8Z8͚<6?h$%?yZH>!N,- 9QXQ{94SX &l&ޅ9ꑈ֏"8>\\1 ZE\c_95R: z9/Zfhde%2.I3_P,\v)??2jxBxLMz:ST VE S7`qWmi Y g[<>δGq$-ס r8{XB ,Q|,z$G8viR@j|y(?K8G1V%FA0\ }vi,ԓ:$Μ p@'p^_e&T4-nQzs`q m9Զ0kBOϾ.Ήl. #ΜϢR$VWq,&T+eqr|FMQXQ{>i]} h1Vj0H#M)TTϏ3^WrJ Dm8cJ4ͭ[I#9 EKDA|X0MHQlԿ[g si$N(}3ɸ27 r+i$! [%OX6F'}Z]n E=ɗ^*X*!`XC 9Kv!DR@F-UzO+{p02C5<0 HU/]j%'D q[Q&! R1{YdBܨ"8v5rpAMo Q! ܨhDB@ q|y#QŸuhOv&d! YȀ!W2ZNZ=\;}h D 1B%y?)'9}y65b ik!4MLGh44vl5Ĝh˹RwMsnr{KslScqAq,El{w7[v4ueNR"i*cɚ]>ۻ28^yL;8'\af hFG Hi[ r!%!2h׼_v0 3u|cyӕޥjrY0M҅~9>}d)e= /TL̥IfD?6+ Wwcƾ 6zQg[dcisk49'G{ B` LtqNŘ+@\$.n9Sj>{}ҡ,ef9L/U!MQ2ӐaCI:RSXdi IW}qsa"-ʦӡ_%-oeU]ᥜփkcy3$w O2ĥYxہOb m Eh#uk 2lOKȰ B'mQL:-Tii$NܴgDl-NxF?RM߿Xdw$ %4l4}@Ü=7cbxAX"æ*ĄC2SftҬq2j]Oק֐ĺYb $ZA*U#NpZb&3}b* e5@CwMG+%;lvdrmBAZԈa8n[lL4+`/,nh֐ۃ߱kzXU3>wV/ܫCGGqhQy]ϭz I62`zhN烃gyRҏ[p-AP?QBEg$q-6m!KoNm㉭-t/ida6Zx @\hz*&fB+:HN%",$MgYCV>H[QXQŸu(Q$^%frpB>CkekU˼qOp8zr;oJKwĠo];ݣ 9 c66n Y0MhJ*.(~$-!c.MnNND ^4K؇5O- [0]3 8Fx[8 *8szAd]y _MIp1`zzBrP؎a1dp^gwXϞse;مK8HxBXhjž[ptV@9+L'7-tiEwp`hUhNb 1&A\\%aD㞇@Q+# >5C!6Q.H#,1k fȌG JE?-d0Mf~U֐-2K8f鿖Wlx*UH\1fؙ[1 }#]uB7Rnt &8;@kTU|ٜTzm V?*,uja4]y  A;@kdw`ϝc1͉}݊Fxz933bʪJK,i؂c1Ar,D- u )@l4jN4f\[͉¦QU9@!a XCItU 4,q{ CKS8~0{#qvm[A:D.*aiZ]҇`)m >Pb#aS&P_0tbsx% < $}Ќ̀c٣\J@`h5p._[C)9r5w\!1.&e4]@CȲ* K(Hx-B=*kIbv+ &glͲ;<z<LqTt,LipȸeU>gVT%g|< 8;(9R4nBYJ.ONMYAsDF) ֡Zc[/#3;gd'/![Ŗa =8gB}nDGa[PIlr6P~!諸+O'ܽa4,$~8tw,ąR{ ĉ9Ќu=+ɐC=˷$S`ZAD$li@tDDB$ W].KkMX#YIsp]k`xY;-5==tZҰrӌ,ב}<їx<9|0S0I2?Һ$o>fe4](>Os Fp6^pn ܚj@0+pݓ3ᔆ%"SL)R. pԉ,FsF՝H$gs`C_RP9E"H1<7OR}ڟ$ڿ#@,%ʣh×19W7[PDf6xHCQݎy1,.\H.[!X"VHEpa>%75u6 l~SIH ID9T}X0}UMoxAC>DHj Yx} D2ӫ89b5L㑌-ݑҐ$]8] ;CگYnڙ?kB#%B/Є} hT51%5d4}BD,D"!QL u0И9ɢ nL$ke!)W0h6`c2(ǁhlLBJ)Ha(Na1P:U+3M+9g(;WВQl=i40e"KK KƩD3:(#V$r+"?$^˂▰XaVVȸe~f6D/6618y;4&K4>?w@VCںɹ%N'|Np1v-{|i/n+v'ᔎduз ,ma6o~+O-pLb5|ͤ7q,qK0F.)Ppp2^ p}(ۈD8̜tJm!j#sf7-[(G1n9 K8J[xCW7: }rv|֧VW42ƍ4]R"jBO/\$G8Ó]PI G"9 @G/KH-KQXQ{<̕`0oVnty{ν 6d\krl`ޯBa(>Vp^JU;q9ZI#$qKDIKd\_)zz]\?bg6167/ʼn:Ջ$ab̨,'[`) #O CWCm7nh!n!??EJdܲ^pwWqReݽ9C%߈ݳ}c?$ !hI+ǂ‚irӭzOh Yia7!q{P0=@z"PcfOo28;i=%:˃σQ~n] vA9a ,%C`B#sIxjR@0 FQ=kܴ .fx,Q{<]A@s7@/)e@f-SwtZh谁"{ 91X1{MP?  Tywb=8?$5d2Mr&J֎ @c&:T vPbxJ/0\\Y:hHʾ`.>?3lSFaц}($ ee&VefhH?6ILT.V٭+[&1#Ja?,eէ75Y׌Ŀ9̊S&Qޘ&[0MbVhZ _K`vnp@v}rZ0@_96DF,QM'HkJD442 9&)UpeK-!5f~iAN|$!:r-+O2LbK@r@)H{ jD?;5Qg1 3cbn@",! eq:@K $ e `͸ѕ2C*@aIj)/4]@h=̓|38ދЃ+36v6ؔX:Y 8ʇo1tQs@r.i@0u@OP6ZijC5جL SJ=#2OяRҔPhZ m0~;KI=.DB<{k.xKFﻹqD$[Jش~\a@V*7΍\t =%PK8>[xaR\^5zZԼ_EuϫĭnB!92!hЃ`)q?ye œajQE{+dMAiWr YfCկ]k ]́JO*&n`*0Z4-I~xt#d iiȂƙڅHbzL.`]!WEwt}<HCb/Ȃi8 Gcaqd Da Gi u_ U0'J1[**T79iYC%hǂ =>P!>"%- b%LNG,1yH5ZYVsaw n J HVC:rԆ״D,˝MQv>r Gi up}R.e1/.)q)Mcұ)U  !.K?}'>rg9x8NK8Ԙc(Ph0jdT8Kvp`` h Eɍ*)ș׀(z_Am ~HJ6 thmVj=y,-m ~B ` y.YZALoM;N6:^bi߉A Y`8=$8弑!IKP3-hc %ȸe὎*ʸ.J'EE`r.XJ@i,$MBR>- qKdܲ^w<0~vyUޟ e$2 XDE۱ iBA:4$2JY^jUa, N"İ,_>a Y'dYEf)V+q)Ot]K4'F֐Ti—it[,YkimqMae"i5 @ CON-p.Hf 8Fxo88*9ʦec|(3iiA =B9o},7LFa Gi u61Xch[T0qQ Z9iP#%5duÁ<-}dY'}cWֿ:"95Ƴi̱%jqLw_G$6! 4$2H  "mF+j#r!L}I¶`>TnDeE7ۻ(D- uRSxb=h=XH7MAl1Ѭ'KJdQr]}L=Kg3c:>Cf)ᠥ^5ݼ`3"5c b9$.Ao[tqO\$e@Z~H8dx'鸨8ѐmm¿jt䗾=KA31[ovp&K:#mtX$E)[mBʰ'=$2:V@`ǀh@FG[υHC"3e*T'Y'N ً 9e5iF}(IaH N`Ɨ=H>wR>Z#FűO5m Y0 PX*] ;B4^z֐L Up*7`G.'IYl MY ǥF$2JY9//! [7v|Fˣ5$(m6%fy8QJtq4W(5SMҁ{3 (y 9!X{ xfFHiܬ׸ :RsDഐt3Rgr_R ҟ$2H MZ)_ 3yN1v9"8[Ciԍ,^Omi']44>93kq[H,mzGgsWנ[k7mP^fPC0I6 t=D[q@Z?_>x$m ϳtKN+x?D~|):9 HYCݥ`>>?E vAs*V7Y:ՉEB&7[ OO) K,ۂi@fՌ +KdԲZ)@msyze^E!Ɲ)8} ?GLӇ=6! EK;QX"㖅:GI^EeP|7iגaAtQ/Iۦ@46 O^LytfNh󌽀_ziqwYb^5X0GP(MUڌAgR/?*EKCrp),0t02fppH*H?ɋcv7I) Y]Ѣ~VoqP}k HDM}X0v8Ҡd?JCQ[^DڒYέ[{7DLi ۔@o~]E3g$V:US9ENw-iX•`>Йc4l}\QX"㖅:bm/*r 26FjhQ (>f^1VGK^IZq;r]mfx6>@9YF LAi) _i!i<Nj6o џ "%2jYx"qO݁ʺe\-p6"%c{Bѵ!9;caJ>K3 K8J[xCU,d3r 9QmNJgXH%D @ hs"1 Tcr4g\@-[3#_R%­؜LXB}tXeȸe1u6 1ؑyR + 4e~ȊI4NWu#sK88[x $F#xF$b9[H>$6eaL~4ʿ%2nYxc #&Gi9 /֢r2@oA7`@מW]ئ,ΤLE2jxBWܞL̀}0p 6ਗ68㷝K`yO2dڥ\OȨe 04FiQT~UmznBJt̻<L1u!h>"2jxώ<8w<G`ʄ0_J6W7vZ|&)&d_[9-1Xp8$!qQb [o,R%`x95V'~sEw$]S"tF9u'fl SV`Lg׳&[ ~>sR^޹E*Ò9dEY`H Q-! ƴ Q%KXK{a{i u nJ)UOl@:l"0oalK1m@BxАlc#/a/yT|CswJQFh,-1bOǫ戤 HP9+ڔōـRh4yXoS4 #`#'iG1TwL! kH|ۈRڗCv^PCN]fCFvvw=Kx-?[4V"D4?e;+_Kȿ0f v+߿7 k0P$^Eimփ@AK'.츙t%Bh-\qf#%P")cRAM~/'a' Bb.6Dl둔;5 @#:X~oᅰMDt?b!"9ejxCi>""ͨ &}Ͳ}f*F9OqL. 0} ׭};795VZLo>48Yysj{v ]A exY@1]KPI}1Iܲڐ"Nv‹;Ðӄ$@8H zt^&?O%^a<l1g3,)Q0o]Oj Rq2-+H|拎(46I}3G+xB!+Ĵp(2Mh*zY%N`VOY.vNC5p]M8.c~wio - -c/Hph }ꔔj#e#O sKkxC|;Mjt -K|`auϻ-Xp^M*81HmY85x`h Ed}Ahb E h0)~ΔH\_H@`I:o'ch\ô}T:1Z֙0 abOIJ?`.$Cki 39c̑pI8U x*q򨩲Qd5GCZ兺25K7[}Ĵ_玾̸#6l2/ȇ`Ab*c}$6F4ı]l#Z' s^cG#44!Asъ٢?cck)0dH(Yq!a%/@yQbx/{=yb+ ~}Ѫa_N0.iP( TM?Z#]hӘ7 ӤKj3"6~EqIiRj8mS6N s^ \Ë\A\>?JBܲ R`LgQY -B0t vQ5菫O6~F='uya^)0ߔ%U^Jq Oouf e'b%/lr8n ug[S R-[|ԭYU+͑JwAg!f>fs# l>_؞YBWiFQ$rH V>0:uԽ4ƄSG"t$67$rRq|zllF`h E4ݢ^‚k⩉qz>9iH\<Дj i(7|lh7ۯD.ſ' ۟[(8KiC>R'j ~>@wV:'0 z"aDQ  Sp)0>=dQ ᐄL>k)xB]$բ=TD(1Ù44+#MSDGD%t/Mip+r ^P恤 <8Dg .ؓ,h }sL#5UHUb17 \ sMSX'|GQ,KvGNIGD.8~}n,_Qkl& ~7}e*2{`UWb ! ila҇H 5J໔0=J4ceTmM@L71,qc5%ʅpOHB-w<8wbpT<[fv9C- }N k5RZeC$a/% P3+E;pwMiiW*$c "c*aQ~6Pj..Zöa/m^gOL⨝֐RRf ␰気^827JTExO/~r^(2='E:.yhN/̑~K{E[F2#!M]4V Gc_驝IeXAqo6/]l/J&KLNNp ;9I<cW))vǓ 㪐  89Θw4/-zy0@/HV)k`  JD q2! :4VN1{sB)3,!l*d'ƣM -AFB[a <..Z{[~.=4pFx3ĕ񘁄9&ߊR1mg jAе0G- uxD:Xyr3:â=זXoPW-ʰ4`>/B-M_'!UYh#s#ul 7NSS^e& b4e2!]vk {i1 %-8[^a >|lbžgJdDΊ\YHȂ0%1]??YRNJ=0u%m#Vx,#)A i784L#4M 0AhoPeESq,nL')1.˜-AZn*O(D؃a7$ƽ~2M^ dʬ IC c'>Z{9D+w1P"ÿcܚ8n H\5?b.Q~t!j|ص>No%> sTl,JBkxv~-XGy8η&+0>+)m j^6R'X@ ޫPW?o״tDa$7\ l5cbXՂcc& 5ʰ$2aY9Hq}TEk)hn(=~Q}i Hc<5D$$dCa὎|CMEKJlX7X_#ޮ XԻqFl:Bq|Jxf nQt*Ur6r]VK|6& kH\9TvpM6Jt265>~w[4zB E2 a7%;O0ƃHsh"T]t^9rQq|F,.wFt~\.q:ݔ?n퇶˾9+`xP7V2j{wՊ]KT5OxÝbRNh_IqH sKkxC?$|w.>e=aW$pz` w-ym&CN>Kk jY'}켱Q0S`Ln~8܋BΥaS~&eJ4!2I^Z{z hZ Lʧ²Eoxo|x'l YchJץئlwH/đ^S{\Wծ1FI)yLHǐ+1^q#C=l!1mtM ~> sKicgݬ>(A͗4T01`l)oXvBx6H K YkeVUk/J@k16"ע\]5rTp٢,!)!-ZDh W(i2Cac1(^teAa%Z(ߋy5[ءxbJ c`s IXCb<&XBIl?95סΔ)N3!r;wqqGw*Kw9ǚq IX1M(eZ) VN {АcF`rm7+ &|QL=ȸ5dx*0 HaTIB&: ̐^K{ [3Жw>2'EHXY.fc(sApq'?VDAnF sucǒa{16R`Lʴi'8m&hC1G+xBllA\5Q7aokJ)$-KCC)rxki b$ sKkxCG("'*D|JA!R`H ' Y 1rvދP9%{ fnDQq<^f&@7Ŵ1U?E7,eNQY}=2ݬ}MJQg pt!* 6yAY5x>l dٮcʿCUōwn,B(5Gz>Mibx)xh>'OaEٚαHdQ6ќV`ErL+#ԒI 䤠#iX Wdh/C[f6Yx+|X@gc %(gc!+4qjK eH BUPZ4*mC l/ki$]0]mJBau2LJ9(B>$a EG2LPO$WBu:VcpOVxE ?95w;cLRrs#'bhV?lV5,Lxr¥I6 DW={ˮEWȰE)!FM"KMaQXTq(tˀ|1vދ|x>G4֙΁3"+[tg躡[1-8ar05ӆ=ph) W 糼0:nCԳ(ש!E)C-Dnd>gw"1\cx>RwPl& ޫNwj'ej?zND۴]+>걖MhP!>v`o: RL8^5mUR`H !&/!a'̑^K{C ׺T:7SN 7~vH$I6jF81wе/_=»BEf­1QV1z=|봅Nu!|A V\0G,5pYz F[λh g0hU~6$G5.RۦlRz{a[PP3g+_N*V.}'Yp ><6Q- l10G- u耥sFρFg b ͙=\JeK1m IHCLu?!L za75czr;ѵ 9N u*:JJQ; W ckKn!v.2@{c/ n37#Eoo@nfnB_6(Ww/s`tZދYnѼ8IlD}w+<5~xQ>tv?O~&cav%maq=Fsw<9":h$Dۢf\wS|1eV*st yл`²֯̅ۊỡe@ >\He ]SA ~>sE5F(|dwQBz<~bkbcE/pNHL@@ax.dAI03'y-BwFVН|=%&G_ZPM79B`'#)U/VGz`{(%fy.Jʉ^ո|jMqƮ(`L')5n/,oaN3J"l\ UDixc2]`V2Q,! ²x6(Dm 1V<HunUpHA ߍ8V"[Ls2.eGO>׍9ōi`uNj@m?KA-1&aUusL>ƒ2#*r12JZ‡- |K§kL.\!2?2A|fR*)ߢt}ߛΜ(<4S>N9ő!q ܢ\8[.ۜ=aw[Y8Y)xA5P&MpX)c[<7)ѓ%'B6C^^Z{" ةE)&O楡-p)YpX$A+HL%rי@wa&qE3Ei;Fͷ8} f;GTAb ХϢ|UG( 8搋-Ar#{qu$QA?#∷M -Nt= /a!?b w5L"SօD[6 I%R۸0G- upm6czg? |.@KK6 ;ahU(]Dn8L`Fd:bQs!˝aյ1䚹)H|x֔Q5 lO9_KRWE _7z ⇍(!r4ӆ΁Ggif  {!RXG&QElmED 6[b%AokH|SS:򞖆& p95ױz }fO= kU af%"[ZO| )1I !*[TEI.Z{jVinlma"Fpٽ'y:,A ;)ecv sKic:IUiIawWV=j#_tO5$eX%gl/ ޫ٦1\@K VG169ڙuH(TćL9-! 0ҧ:R=~[͑I1V' 7$RrSj0(%4#׋rި=+g?(9ƤAZQX.L^h@q2ދ!'Cqk)3zP{FO0ur޾䘖aPLi $SƲ{ً^ٓ%&QNAJ>,<?1Ut~4ӆv4/M^気^ RMI)5 -&cxqܜwg5Y# XpH60,/a/ES'TIK\k8V$?l~ΫX%AX6ϢL뜺 RlC8$%a὎|aZSq)H7g\aqKiC/6XBjٔmr{a[d[;[yzzV]77 ƴ|P,he Vh:HXS;CyzGǹ<@+<=&h?8P~'*?׿׿x} ?E-9GD ?J-G Y[HEh3);WK hs-K ˔|ݸ6?7j&> /Font << /R314 428 0 R /R330 146 0 R >> >> /Subtype /Form /Type /XObject /Length 1211 >> stream x}[o6+:axeJ}ygkk:`/sVCʤҒa?Z#bk3ituc2Bv6:+An)D-L%!.a'.hT-xi%Q!XhTqHfk2W\$[הLYRSR'Z&E17'$d"A0-u D8ldԤ!#_&!Z6 :Ա~ $NR w\֎Xe%Zv/ɮ1j >qEnb6@/ęxzHQn2H;EcHS ;)/zT$>N,qRQ!+.a'mzTHqv ;q>PH㕶NU{c#v2e "DT%JKdPl4:v¦zB:焝P"PU.z@&n㊰Er F `.2u+%7ھ,ѭb\8sʤCA H[㞽KvspIcOy.v פXsQOعZ3# R Us!NG)~kjuSʣ4$QtʣFvT"ݨ:DQp\4*nFk3Qrý?> stream xWKs6Ԟ{ek1@DœNrGqDb#IͿ J5L }($Hu$O>Lu[ӟ

duvS4zpǠ:")((kԮٻo%ۢڹڹ/S(v^EU^@˽bPD)l /Ed|,~ C.L\ÏSF> stream xWM6s|騵~JT&9m.rX UȒW wHq؃%y3q!i+|MHN#N~"ԫE‚݊<Ƭ`N/;ܤd$Q_9',`BMKiuoV)J(@ :քR:oCu} dX@ ѫN{E!VE7.&^L9Ơ?~SUU?ˬdM5uZ<?~[1F~_󱩋Z:V* eSIyڧĚyIH$a4fFX#J [, \}U+ooyZv>]֏[Uهp|YhXb¦J¡d&[1}3Xk8B"v9rg0EX-_I3fKm=pXFIPsj`ǘv] h꼬sevAJx8#FvAj1A3wrpveV#VH@[faLQ=g'y2(kT!/w s&PI {`}xOjR+AI0<&Bm1tN#e!T aUטLNԆfcLXqp{uq[޻*iY[5a7jzW؇"@Wrʋ"qJXy!mn;PͅYq2\E,t=fhOxeDT$9 h\ |X]}qpbW %tQ ?UMcT{J~%'?5L{Vn3gCAŬ燢*[F%Z]ɁjsY5rib9mfڰ*'cjmd‹cp)a\zd+X\m-9ƻ'Uz>W&А*E1Sțk#حZ z=6rp;i I,5E!9+.rWlIn>Yt&جJW[A#ރ<5z"e`/NF6J"և9m.y4.rSJ/:mPB  y 5endstream endobj 501 0 obj << /BBox [ 2025.1 4609.68 4087.42 6672 ] /Filter /FlateDecode /FormType 1 /Group 426 0 R /Matrix [ 1 0 0 1 0 0 ] /Resources << /ExtGState << /R313 427 0 R >> /Font << /R314 428 0 R >> >> /Subtype /Form /Type /XObject /Length 994 >> stream xWKFWq0Eף_E! D! ) HSm{U=+Ehoz/>|>O)ɱAb&`\~ M F~bx> -(_̹@iz q,("?ѲVıزVIJ ՐVqV x$?<=@KHPjp@$iX~z{'fnYohXod}B7> ;W|9z% 5m"}4r!s̰1䲰F)fF̱HU^ rEwU4/րYV~z?hz ^_JRcsr +JnC.eZfY/EnJm,+piy5`+: ҝXk*r,x8w{v\w7Ja?*4b|xqqj WJ>o ;1-slϐRk?q FZiŶ̰nWZe.g.u):"PΨwSųu`_:Gw4:1RkԌ+aրVqR 'on6#Z!g5`vXI_'UeRr"gycGNH&#0ϑt1i]Yso"dIhS@dgC%JH419!mnlҝV/;bPPOYF(Al +Iڢܮs4o12Y==OII+ D}]6՗xr> = TZ[ԀVi|7`-k-[Pd8 v4[lK}|w侧s$l!!=E{P\lc_k+lc0%Cl2%fI -fqߗendstream endobj 502 0 obj << /Filter /FlateDecode /Length 4427 >> stream xZKo8T},rQ)>1Kf_Y6^4T*[ՙ[Rvo8%TD0/*m|S>t#mw8~wjuKox3ĭwk-eo7/>ҖK_=2e%Dqn/>w#^;Q4c7wCߜ=_43p۩-v{nÇJ*SWek/)281\oеN:Xws7ޫϛ{q B]enPp|e|1q*gRs=(md-O5ltj_Cr,`Z{zO&SKZ*c4Hhi2+)]A"G`9i3-a4aFQS3MaEwwᩗx\/eLˋ|qB &s5f_]E3wڵ-auE>{v ]_K֖|^Ro MWiU<, 9 k ޘ<3nᾎfc9-lPԏcKgeG_KDW/f:)LL:86ñ$lqwID4_yY6D^eK} ~_ȖC+A1tjmK`+Ivֲ^ZOc{+(TlN"K忷1U S63KQ0%E/ IE%n >.p_t٬eéEb2[ڶtmbڏ_jWCթg.Nzj)j/ AVӴw]AQYs u ' a 9ҝ}g;qByN5>p*r^f,O-tqxb6&I8pP龏Sl}]a0hU zC:)'g0Bu}mcჼMeg&ՍIЖyi\׵鏱 JO𓜎SHWChX: (=vLcmRn|:QJ")NcR@&0Lx]uX.yS|ma"( X< 9`N~Ţ%JLj"5dꦤft&J@cRnIwW:ŽF<8us;R{rKeB#,2. Md>Wܶ h~jxzh>f.ȴC5c*B٩3)fuF's/A__#Cٙz^}juqi6^PΪ=#R],MV^Qcɭ+;Dz:!ꬡ,nSI] W[ܥ~5_>M1n.!įy/ЗE=0sbhv؊SXK AZ* J*ccVu+q ɜ+S:<|!(~v|6t-O^!z@ Q :96&\=Gc ofZʙ<9S<ְ&1o&cIl* cHmCTq]]LեUOm{VOsx_r[ja˝Ob.WB\ӊ*agtSd>xQE)BR*JjP)cY,KQJA񰫹<-AMǖCl$-K^:7H *:Z`!J_Uf{ ;E>1Lk/cI@ }M-r#oRuG]tUBNԁ= [2 b% Xa2D>?-oÞ c^c<&WPAejaצr@Ei]20iF@dB/Frd[G B㯦oBZ:'U R@fWX6++##iGsF]h. [-7ܯUjpc:SChWJ56VK?+R{pk,jOw7  xendstream endobj 503 0 obj << /Filter /FlateDecode /Length 4135 >> stream xZ[qyg90 þwN[@qt rŋVүwUu6ٳZ0ad_WMM]9}s&?o~po_h7l R&ָ6RowǞjuu).9{s*[4; gm;+Z|oTƟ:h``xFKpط[aAY'qv諪}~ VTg˺8V&&vg$-{>8ʹhVmww'^kyedk597S4܂~KA!iHa0AًJʧPJljS* s ýʲ7 1 QAJnfŜY.xLyj͔WM .ݹKbwPvFͅzՊ2,^hO*&/!I89!Q[K" ׊D$k3,UFAHHvRJRtw+z G5Ye00֫ZQۋur+?BqBr'w`!ql.n;eձvr)ywR=o;:4.ܫ#^T!{?y+  d˕S_?I[' `Y+Nn~ޣmi n@݅ <*m p _  ݂"$%{HSS 4{X¿DZE3yl XQNpb'(l[SI*i՚Y-Φ$y3S!"#E XVkoarөEnI."]9 *ǭ ɲ Ċ£MH!_?#Bm~D1fs#) F7<'(!w'.5b?hr kmjZ _MZXY+GLkHe:FsI맠-R;GbJn)w`fEfts9 3\ +HI@{ 6 4O+tc=9D#$3(Z 'grvvŦv6eHHz{QK;^]Uqb). -721\{!.C)JsFQᄠ, X{1U\sfxS֭!Lnq(vwUMp@jPwesH_ׄ "6:<`^,vYX+Vå } KP06f86^;buB(wk%NnO*Ȳـ:S JlLE8َ+hyI ҚR9R*頤5`=šv=y-.oQ)KiCm;; kW7Ab ׊Hb.ҍ7Az]XpͪBX %+N FJ{\7%XLP T BV jP>CǕ02G8Nbͫ'Kfލ`Uy~@s1SdFsQ"fI啇=Cf8OY_HCǨ_w2a*oC \pү#̰+F̈́0M ]7Ku^|@P >H6ֱPz~ćs~(s|Vʬ96ر-~' V^75^;N\qdlwjX5|-'N_WyKWFDV[0$mFF}>0?em7kj&~H?O>i{a'F a5]1w:JD \(t ~;g~شzh@Sx7(y++&P 1B]0 Y[jpr\W%QqКq=rO 6΀קo ~).5$Ϫ(($p4c5_=2&T_] ʞ?dDz~'4!9% I1AmYuهFJH=-J,|q ?ޏ9 @4Nzv¢>D06yuK3 DL*35{ |,+u$ 8{7T ~tw]^cQg5`r%t,#* λj>r%e7]a2i5u6945Ȥå `ؾDƸt̺o.ΚQf`Ed~4-dAM)9s޶9Wms(wN^743GxxƳbVЉrή1hd!d:8REȫ@G˛W03FM\.hUtBByQ‘Az6c1~PBqB;ߊYz_l{3pobz3}k%:in-R /@N^sOq,xc1k/52*)С(bBN,!z)Q"VMB͈@=ےUo>n =yal'Q:Ga qYl 7-CE|4~*j}`+aNv)6!E1(&0 V,#̦o?(_$-M5G>=!;Ы!?|>E~Ɋ$OXbWSE/@YE-A\ <(FWӜӛ|߅}CM>T=)м .]TGc簍//b&J |_LK`5 SP!lOY>Pj̤4BFӳhA}K gGmo1Э A.kP_ZMSbbj,Z@:Tϡ !oZr" }ZraY9?6E! [4o{gD|9Ֆӝ&&眺T]ڡAGb;eEIfUZ5W`3՝S:6KGĮ "(MhJ. = "\>0F]).ɴȀ_/wRF&`ϧiLfu(WBh3f/E{kǻe!wbD;us\iOϖaK4vfVb _)/cHx/>ǧF;W*A 8 >~#,5E5s[pȒx1s& ~]ZkUKA B tB])yD>:lu@^A(k"#CBSOkI?20o%cendstream endobj 504 0 obj << /Filter /FlateDecode /Length 3473 >> stream xYM8rd渎>9P*6E[3^Kڮ#b8*֒,i4u2XuIH/_rg*?˯6L׫_?BWy% g 2O=,YYjQwaЬ}v)tְu7eyW*};N{9Wj)3SiIcl҇A uYWCeVBrK  =?=)^ ]:1nK񅭗΋Gh7ۅ8V}nÐ;=Y ΖeRIˬV2l Wp09# U3hګÚ^$^Jqط>{B0AJϼ/hv_^exH􂕝v {JMYT"5m#P=yv089+z[_^쏻x!HS(q kl jmӐz5=w/CzV:F2Cotl \"^y#y#+cJ'{>p ~9{g)UŐ-ƌ&gS.1/ F7X=IӚ :5g+>Nn~a Dbe*b%oR0RcՏmtH0#{<^ܐCD ΅MVuT: ˇ^'JO ẝGc@',{ľ4'ۃSxQ+ThgrE(rp$4>DqFhB$vFV ~[XP/u&4`H !^Tv|DHx>tkt};nĕ<x[U}SҲ@ cpQI j#wݸ%v%ãoæw0}u:̚#kv_ >N %Sj%>,P i3iSic,<J|Zw`mcd߮wHTN&.>嶴#r[_ZO5KCVJ^xC!T& VErw?h 3k"篑c a((9dG ")./1Rzl0f)œȉ _'6;L!~F#:Ȳ->xV|ZNyXxDr/JfiHMд6eħC&fn<7!NW%9ٗb'lr1$NY7T`ž_@%K4~}Gm焕%XK)ƓޅU߾!3DΕH0=E}?ͧEtR_4Ң. ACyÛ\ɀN!d ~LZ?p ˃d4 7^-DK䥳f:Ӕ \l4K[.wf0}f$tt"&E򡭫]vM&A<mIcuJ-dU?9Id:48ĕbW.P~'ƾ IC-PS~ejse§g| Jǻ7Cykso9sg -Ëp&dtv5j /h[=Xq̑hS8xԯ3U%?un>=ͮ{&82;b(\1c=` E7QnWCfx N 481MCFҭñ~kn?ܰ YAWMu>P'AP$2>jF:@wN牞MV8}B/Hb;CK WߤsMZ>'J 5eYMCwMO)9sFNͺ>}bYOR\U@Mbh/r @h#6lu)kq94*ص`=8`>A}lmׇTF,m*#4F HkaiL_ f@k 7 6&-)S.pȪAR&1{ux~YL8YLz J䋿oH oZHO p7N܅U:Q6!U ALبm{Ix8@U?$׾{Ԟu2?,^H&3U$jkң_]J_I"YY[m7V(|ot{ )Z)CUj^pov57չbw?\0pg "gCh> WOxa5L5Duo)+Sv5~yw.[RQդ~n154'Ϳorׂ1f,.VW`(}8LY=%IM€2J@Ih&Yi@8(.xYv|`> stream x]n0{=E. &פ H,(NQY3Ӻ\ӟR^뵝us/۲6ݹr&>lj u>yx;eP>oyٛNx1η΁ǞXP?F`g.0&R IpDRB,.&" ә$N6] ػdU1dQΑQΈ% % $I%&9':{@EeeCEUYt YlLVF+d9fĜLBSGƎ ݘ:2vdiVYetQfGs ȏQ`VEfTUUfU3"KLs xem z[ȶ|{]Zc)sYϦoZ6˨endstream endobj 506 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 4775 >> stream xXy|UNNu2Hu"P( ;n閭itK_fOM{RvjeUԫ̸;N=mei4}>~\Q.;6.>~M3sÏ ?–Zc`"&v*}0B]!nᗥ4K!e WA)4Wp3&HO" FXc6P FUR~l\fF۵ {~fdp"/Le&NV}EYoSOLd$|M[zC&,Ypjt])*6G!P&)3(tJ3OBn%V"BEzo\U~Zbd6?Wn6T|fZC `.d:3xviQ"憸LkC*jڷ-̃hD\4{e>{,TEtz@7N\W2PΠ}ٯ8µ>זb*5BS6:2 xkk^d{R@c+w2MBRkm(5V%I>h BfnlL}i T+9>MU2T|GSSsueg^*RU;3ӀZS fa?# o ]JNyTex1Gwu1jA&~UkDD#UJ K*eڠB2.G9$9:v zD 3gu"n3L(qm"K'B܁moY`LPTt:"^Fs࿉#K0%e[u.ViPަ%)Y:B$Ps#i%5-E 悡\̀Ψ%䣷h]nLrK =z!׳͠(^ƑΏ.$P2᪈:0UTdX f8N.狌<ȣJ\``CpTڌ,ngΨ`3%-KY}@ T]'_gS_GكhRoWACtgz NiS* ^V%w?}{)9PWW^ Icy K$T~=9dRYqZm8vD34t8NXxP/vo~ i>' P\e>>}(W1+W%3ia5]A?^K L_T)Rέk^ SI&P;~R:G U$ݐJC8\^ѵ[pbc;m!8AWJvKCU  ْm]־fG#*%} r[>Eo|Cs|{cݧWP[*˥K m=g|[eJZzyK0_:bv/7tUөޘ]baI{A26؂My$k)]Wړ֕Է}02\k`2c^%dRA}|(0x?R]!2>R ABk*#((\'2:CO/* zDh';[,޽_C˗A0'_l)g=z|qw:̦5PSu{j9> eq 5LXfB Vk'Gԃ wQv=$uF}xr/ˇ(Fc*:nn"gpLx JPZ*ZUTsrWJ0W|Ǯs3htY J։O |&w9J}b*=#抷s z!! AY0|#E"F^[m4z*%-1RQeɅYj;EPdR&ō 2E|7Yi"n! v50|^1zBěDrЮvHAua lHUh՘ڒ/~喟S1*/0 PofLǿƁj,l7P~8x6b,%x==(\>?k>41JW̻@D<;N“&,hDC܁YGWhi-bCodwҽknffF򂠡(Ǐnn z(1_">8+^"&%پg4<-"r$At4|{&-5 @]C?'ڈuz~eT=MS((ddVN9Q+4GHɨ6hZ|_ Lm`~Ƙ}$ z1$"h^b%U2X".׵ֵ'=-D_Ц=#//c4=*W&)̈́|jWf9s)evo.t?-~yi$,G#5556x ܔHVpRՈ䞼W?;eix-@D%un^.I7\8vP;5X<c8P/l9ڈ<kA"MЩt);d1#+MAcPT;Ncy(H'LjM7Ȳ*7ootWB @@d0?vOZ&aJ\}ڐIwY4q:Hߔ#[un9/=(ޚr<8s0Nz߶wkmzimM cmLߗԺ㡠4êJMߟQTԆ;ˆ7 {)ڥb -OcJу2N[&Dyfb?(vkmJ;3D1?6Me<|Q@jڦipɫDׁ:ZR-fpxɵ-hMS2R8 ix b6L@Lƒ xmVnsGhh<,%^( s[Z[0bKځ]|b7} @eu,x=ak08vK]O}ǷՒF(PY% ]Vk=3muy@Ľ {]{EGN[vU[mWZ͎fr8oy'GqԐ׿웋.M{gu6tʡ,eŎt 0yh|-HFU1Lv%P/>zR 9N6"NiWj@%K!MPb۸UWWx^SsDR]4;ɧ2z2P@}gk(@5TW,New [oÖzƓoݺ5x}%pVmu.! h,@߹qܒB[)P*u+!z۾fx`~n@/I~NhOqà)8=x .FWt{8 &\YGw0vfE 0q󿒤jendstream endobj 507 0 obj << /Filter /FlateDecode /Length 269 >> stream x]1n0E{7`j%fl(Jrc+5-rBo0xn.חk^6ݼ5~Ҧ%JY#nKVm_w!h-ܩz'}(%D!Hq>1ǫةP;v F t:y}czTo /F9'g:NNXY5PXP trsO1:8#dRj$zN|w{e-<Zendstream endobj 508 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 2577 >> stream xUkpSe>!Bm ]nS. Ҵ &MMӤ_\{ӦMCܑ .*..Ⱥ _©Þg939~<| ,m`0W6w YL U43Mz}"d2!3euQ|=j {$9Err,.%jnOR$Ow/i/YR&ϟ/=ƺ%e Z_VKkx>wuP Jbn/.Kx* OTaش+EUg84 l=aal 6{aaÊMҰlƵ U-i>ؓ^L|i#C54RAQ_zm)7k.WBdtѤ^;7G{MeljQ,^ &?tX`n';63 Q:9drY퇁*q|I*n,VV}ҿLϞ<5d)2 3TirufZCXoJ=HiJEQSS_>ybspfC(fQ\j*೩haO Z&W+\*MhҁT{I7ʎ'8#YsybuBpe5{N1~8g@%M)a, ͠_' yB)@)4dkA+`Zu$|-RQYiѓW쑫'Z~`zuzR}ak:HVҢ?@-י-5m`ŝFJ,&& MANS%d|%4+9ZP ^gh⠙({]5"<.γY`$hu{t}$y|mɼlրKաP$>g3{ӵqE]#`։ǾvmlB£'k^+rڏC?;û{XU7V‹qDH1J)6а~go;5u"R'RcYF^ }C6wϡo_|ĺ^lR6m$Y6U؉I 'D{Blؤ_o4L: n5U]5oDC!݆44d "{z^18ő9x0a C yO>G͚CԂ'u^ 8p[m#sPuF䢅Яψn'0\ǩ¤3VC#Zu+n"GYh  kջ[6^ͼGg<9ub؅SdSZ5kP8-sP:)Bx*{?ܟG>aj6,?bSyw/9w W )=⍪1$ZG:*m}ʶ[ +]k yc-&b_OԌM]O+Qw.2=􀍦+Js` 8E.ih[+uz/Y?0 B/hZz;z!p=5 'FP:FR4*q'?x"K&l& :b4A7}%X܎ 8Vendstream endobj 509 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 490 >> stream xcd`ab`dddwu041~H3a!-C5eҏvb;022V7N9(3=D! H*ZZ(X*8e&')&d&99 ə%`-6%%VzzEv St3K2RSRSJsS nՃP%E )Eyť KjS+JₜJ.nfFF/?:~mWsfVǻ]aJ\~e-b+r3˫ns+ǖuKvwN}Gl߫Z.XfEy>%}$q7 ' [;+@S疵i/'M>Aeꟴ?v}~V;`[KǭZVIl%]]] ?͐3.X*y80γendstream endobj 510 0 obj << /Filter /FlateDecode /Length 338 >> stream x]Mn0F`pl)MɢUƘE}CEόǞ|yLVV>Vԯ>?֔._ǩk:[\%ؐ-Wε:US^t9 y-5~z=5 sz@[j' Z\c"Pl# ZQh@{C98]u\H5g" Q(@YO@QF9A(*֠P\ Q*^1Kd@Tb>>Ny6F뚧MIۅ]2Ny?wO7endstream endobj 511 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 2965 >> stream xV{TSg?!s|Hz:zujAm-@T;ZDԩ )B $LPIA0 oZmrK؇VZѮ9Lgͺq:ku:}~-(@ ZiY,i7l!ٱ<,%OOii*& Yk8w ~1? `b?5r2>&:{S*N*V/2x*ӵeeJXhQFFo<7YywF*{4ULINRy-zru&i*{S2)T.9I *z46^&_=w>DƃBTNmTzDF(7jQJCMVR4K@qm Grǵ%O0NMZra Qɛ# 1َ/6!qeqJ>@=I4dܖ/p)ҼZ8'[,;&Z}56.d.7bGcAe8,$0.hmӾN`B"6YuuEMo$Ǜ7ޞŒhw^׮n+?s|!8QKȆ 7~^L|ڡߠˆt0(Az3I;6a3 u]Ւf*J64}fj* =#OV't ӨjY̳$`ՋDސցx=oN#!y,6&vW h.&?"iAauY(OW~1'T?dw n9wƫ~ _i<7].j+m`y*>Q窒c!@M=S[ ̝Vedc<i ɪ=ZV]RS۸'#pw4mJ7$duUjKSg&@#@V!gf%FA Vĉ5^mxCt;428 φs$9/¶w̗x pʨוuCwp5x;ۅoH g OlCvGۨ$) kfi\' (⺱WnxՓ3 m`>]osˋ? 24K՚ûx8G^gaE+aGrf3I<,Z~LUuuq:kζ֩+vܺIBt:SA1C)*DWo4k/"{ Nͫ _W|0]L(eX2 ֽ vL9{PE 0R"%Z>_+|}3qn'O$EFU4٭ƴ(I9~w6=oCͱ0Pnr B&űEf>Ӡ6204T1ďmHMJJMMJMmhmpU(& n qWkk@BZy/Hƿ셌j8%#%9;z#ߢt,{ͮK#"Uʲ,Nq4$o{^?ahWqdzTAT[,? )`Ƭ*2 1 p!`pTVQ[))67s\"pa2]>z2`02 #1/20؅&ipVũ/|B-3%?YD $-2ڗם)ZL щ¤%"gn5v:.A蘃X\)gp[.. &D $4Y^\.j&.A&j1݆N-.lhh)K:G\7e/) U{3ϱ<_O qkcqm1jjE#FMdB4T'yQJvNq`m'7}yAendstream endobj 512 0 obj << /Filter /FlateDecode /Length 237 >> stream x]n <od*_Km/SPhz;4!}`tNf켤XK`;eIơK~Lpi/clkϻWn}ۛeL6:3NQK;O萪:ځi:RNԓRWgqQz[$D{RmEc/Z( @J(:^x|Pz$cýNyILKV7bvDendstream endobj 513 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 1418 >> stream xu{LSWX[V>nk|(0Fg0KdcF*\ZЖ֖GK_[R"SqN'c1n3q[f\Xn3sSݮt.[&s|9$D$; v1.Ib 33ςy<[":;X@&ɺ`ߙO'zUT׋K[(;7wc(G,mbThD#c$n"+K+~2zSVN˔T3*g*42;Qi2JF%f&A[cx__s3i</7,K7! GHU;0NLEh6BUw_ vl3эnk;tJE|vlj) ڒQb7XsWaJ.[@VL~9|Z*{>t鷬ݭ}m]ǁ 4J w5ڝ6؞[?WkRXE/d؜X np6B f[yҜnCoN"lED<$dzPZVe BGvp @!w ^hAkGki9m:H8 C^&nSc2clH)\~@eDt’sld߱[ `Ƈ=t=-LE'y] ZS?*c+ AǮ#җM-`Z)޷Ic *KLK3 3')a@Efj4 TzւƱ@`FJݣhGQfBwgc5u5eԓ|{⽯B#e +:qen|rnx͸nv^8A聆LcF{\/lDD7Nhwx|(spȔb6]b1N Ms_fODIZ%2K\3ujs3-B)2sx+^uxFUrxF/F onv8%i4[f^¡;!\@0x9g޸x9fğQhendstream endobj 514 0 obj << /Filter /FlateDecode /Length 197 >> stream x]=0 "7hR~$, \ Miځc!ҋdp ~% t>t Ô,>dQDQ4'﯈lz,uOlCh,&(JsZ`ZкIMP;V4ȀP[+1JQ,K;a̡s(~%]ovbaendstream endobj 515 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 826 >> stream xUPoHg_c\ڵ:ӻ+R:10v2`Bjhs3 3y<$hLte0ei >lc@ܷ{`FϿèa޾O.VF6i t/j TW:IV^# Tq0GOW>hme;;{i6MRK,N T~Rlr/>lJ5P"U|dѓ-AZ q&<DqG> stream x[KoFsl`YR;*3v13;Ob$NW2Yvo<2*Z-⋈L^/=ʯ^J۫pW)BWZg+k\=^zSRY7gxQtԢ`|{%~ټؐ[n誧Z,űnpvxVi!J*N[)6 wvM;:Uke oVh@ v=K$ tw ]!' ؓ6mmXsM< EdAD<6҉f*w^ֵaόL9MI/D 옊l85,R|^7@8xf$V'N #tPoK = д8]-U| Ӫ C2d>`AfZr~F0ݨ@PNz ġn}ЮM(fadG3.ب$#c+bПY.:%"a}7U )- ? 0r׍}X0v(rȑȹ -.?ށpzD]>N>^ {74#ޭMe]+JVLw1($h،` zp6hc~!FlS֊|pmŏ(!E)"'A; T?M_Oz[c}?!g+Ҳ8-X}݉*{nFvewyK{An88lO$"% ZՑJ&DjQ8 hvt4$q] PR)ai8 ,&- =6D qgK(Rq: > 5(s&N%t*pLHpb[44q2u0U8 jўzLbi  5DxI?Lw}*A!z#.B&0ӛiEFdi6zl&$}+62Q_;8'Nll7QbvL5tjrIT9Aer! a?kUk7LKY6{ΉgNd(P?b9 S2$=0F_PIN 3@&җ]yvtdz5n Hax|'jr0(+)kƼÚ ܓ3PQ R"l 4^: Sn v뺺I!  bsA_N&"/&Lt/o{oha*Q -  HD@Sb;XҧD&h}uigabw/e+kB=2W@%Ej|yXcSϧ$v.)3E~t nz_A O:Acʣ9Rm)eqShvrXrzxgRӡ?q bāMw'}ӝ~>#"e 8,ⳟ&1PC%eNZÍ,Ϝ` ɱYڕu]a$ v+#L V 0zlnM hTę7@j(199( h|Ki'4n{MkggF*Nf*[,";Rߙ5rH|GC9@|8%-8nOĊ b7k{rz8R/R]4׋O Q瀼W.+ = iHζ_H ]DiG9x__[ aeQP4h}sm<|}gp ITJgV"$s+y@ Ys)j.jj+%.^ P IQldаKL#?kԄ ڏH%erdFBMΫ;*4 ,uf.9J%٩zח>^{@>) EP?jݪ7Ȁc& 7f *h:E Zংt,"OPQ܋wQfjTRRwXwm"\[<^,T5K /#1Bg^YMQ,CBLX},WfSނh\['ks;.؁)B${K^^jcIia0~:ntq"TRT{Gu~pe(qbx;;Jmb-!~m'Пs_ Mqeqq礣Ȋ\?H>g85x@W>]z@p;T |Olb߷ |Q\,s\Iv‡rRX4l1E\7J^\F1 VFs#S}>&kTtk*Ӑkܲ㎽kIcI\D[/[Lnlt_ g%|]EvI;yQuzc{W)_pFO),V![t, xI-`úT:4'4K)l'[KvrR6Sg Mjt?`vUel'|Bw:tE%cGlS62I_H~0AzZB)g=U6)ͪAczX7dVOJ+.'kׂ&75wKL]S29Ę7~21&x2+5-qX.^ {|?7E9%T"sy0\~Ė2ɟ"]"upA9v{[\i6!YȊHbUzժf5e N7PbTqs1N[7- qΉtqvueq3R6˦R$@݁{}Yr vUBh镗4<-ʐو2XefK?ѡBOn`")AY',ҬRPY=OВsyX̩ž`y7_YtQN5/Ȩ hpFm胦T\0jP&edgPܠi)'zBR~7akZ&|gfb 1x@\`Ae^Z[{Ix~XH>84(+@y.sIbJκ,1qU X]W"ɧ}DK(߁Is3l.\"m]K'lcE?"P_ל8v^%cr~Hߊx]x`o/7 /pkK-ͥ ֨ɭ: H>v^Lv׃AY`2pڌb/0T.ts\.#4W0d.9DؠWjRm?嵊ϩ`uXԞbI̬b; DKf`endstream endobj 517 0 obj << /Filter /FlateDecode /Length 220 >> stream x]Mn! F ?M"oM4`D, 2Y=.xHl0p/,zsERSRu:Nݾj*Gxpݽeצ8'|zGu49šYvLy+Hκ=;` ##DUWy eu :VgM &{al;ED8R/67҄piendstream endobj 518 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 1203 >> stream x{LSw&s{/[(ITd3MtA6.xm(;yZ 8EQ݋ad)(:TnNqh₏sO?KN~'9{~TiZkN&j!ب)Zz"z&2-񔖦Uv٢ r"!IL7/ml0$5ǔ'fd3)$W|CαJ[-?9ds̖1ӪXl!ٷHr".7$q쥳ǂQ(]̒7J6lZi(*&9e/b45h*&PJ"T5uuMYSΌ8u7XvQPY޻ F88D&9|SW6̩n_8?5kf %yj`#FOtWFQ~~]-xU9)!? !/Wmuw9C;K;z]4CX>`/l]ɂTSr͒Y{sp!zw[q]E}٧?AZh;ʖA&C>aJ'[k0rJ x\ƅp Ҙ2WGso|g%ճ=wc~o@g ѡ^}8>&*%6f]endstream endobj 519 0 obj << /Filter /FlateDecode /Length 3700 >> stream xZMs6[:b< jRlH^$M@h #v7#Sɖ~xN/'Gqz{NoN+s2o:pz.ef;1fBꓛ2[U3 fZ nxorٻ3`2gg]q[@_Ŋ *ط㠞]zW4?7ʸ/`o ˻lK(zV&7þ[i|%+Ȱ+-5,<7} P \C|-{MaZ,N*Vֹ`\yyꮊ60 ZKg?V6^uLvsyv<(|2XJFpXT8ʱw۪.q[[x RIfkװFΥ=X;,F,$ a3黢+?D֟ f74 零kGB`\ﲪ˪7\g+c m:vsȊ9,Dš9{,62aM{%:f]jq҈[n؇l ^i`rضh󰸻>jңc_3eurk+@ `U\ߋ *~ g05?9/:` #lG0FV3W7'c#\<#H'Y W Dh7YzT#厳|K~''!D[:;K}${\FkϺr/4wW~J(`@I޴ ``\Sj2oô\Z2Ÿ]Y,!4ڬwlQ-> vˠs.l* 3hi83ЄI PXhC/Yѵ"K# @r>:J ,(!=13\37BK' b9Mr6u/+r6{h1x~sXݴdb B en}?]ct?#r.Pac+XcKR7d_)iۢ/"$ C)z$=GPL~ZO?,XR*i+}Uȅqa4ЖOY與ߴ/(%֎rYPXݾ?ibiOm'j כqqaɗF"?f͊6 Th@ixwve #"KJ sWO}էOc^&ob,y^-PKt :(9I.X۶oں%JGH/t!wN.[*"gnd'M|]] >N(+9{n-: pc宏bf`jG$|c6Ev=:2 xdʮ4g7zytN;rUS} h=D(sg_f>Fxv3Eɜj}@h>:@,qhM> Lw1QBwT`Y 8 v~ B&0%Fr"X3cQǗ2wܪt:0xdf;!uސzRw3$\Na~Y7@?_]\d(#RqRkj4mX9K?֖i|ӍU|11UE'dH؅]V/-̱VjRY٦+2h3c,1 V?M " uz|eծ* yfw4*5FBM{UlUSd Q<;av Aɑb+CLNa]`vJźPSFͻtvx.g$ɨQB&n݁/cAy6#2-&u 4f׫52-PZNf<@;mttb B *_/|I ^'#$:9]g1!0pP.T\.6?>hJeAY/:ʶH2ٌ?4W4nNhY uQm;P~AL/~M9UٿcU.RS@6A,_Q|n#Pȭs H`F#Hw3zYTRsT4}-iCf eQį93DtP<-Ö~9=Bgy?ysQCk\aX1c3A=7;ʫ :uu KaxP6wtY"Iu9XgAo~CZ2ly%N:pʹ#'nB01,p6A,!rX iU1l˔˸EW⅊ Ep, Ʊ0ԃk|)']BR^)EIcWeUY$Z,Dc1Vl0bJmxJx-AH9IqA K GG ?LA$%ei^Q1.'qPo6C\b6G}tQD SNZtUJTPt6՟WPGNBR!U#]R ¾W~(-ەyH|>mR$c[) |"`gUٰ/5]|e,IHG z]K=1 iR/"HzI^P̝Es6#?(ؕ]]܏K=B4q2\Uu.r"xID~~IDνRcV~ 57c|bmaO'Gᾏ8>pDk 2v7ݿ/澮(dکm<+Y1cl{ؑ!ŬiA[@px%LϮ?˅[n)+n¾7zȢ6^i>悟 Un!}AP G3J;9bˋNo/endstream endobj 520 0 obj << /Filter /FlateDecode /Length 656 >> stream xmTn0 oj)vDɒliZ41M\ىߗ7X'{Jt> stream x]O10  ЅUqP:QC_Ct;,2K X&׈͎E8L+:9\ux}f ~|4MSVBoh )jItUwIG`mU)Uyp8I.~r 6/6SCendstream endobj 522 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 175 >> stream xcd`ab`dd v 5400q~H3a#ewo_ 0012:)槤)& 2000v00t;gu`Í ʅ|(YQϡ;çg /xk76i>9.i< `D> stream x]A D@k41iԍ Q/@ahX o/օg^&ѫ5/Q#MKUisuI O<YKkA*DFs#[ka0[4**[,?%FT+XۂERWqendstream endobj 524 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 640 >> stream x[HSq3N"BR0(3B(!24ngmn3wKtΎl]UX 5SKAͽ||?E%UwF @sǝx2EbpY6wx{K"OAƱ?u] N 89E 56 .0.`ki_N}* aѐ< %! _/4ėf2l>zp ļ8\Xe,9usGG'?T[,NLI?jendstream endobj 525 0 obj << /Filter /FlateDecode /Length 4171 >> stream xZKo봾;;4E6b#a !eZקMrn_U_}Uݿ]NwJ~ow9u-eRXoxZ'y3'J{J9m$59aVV<үP}*IejXu5l}7\2."Sry}iD*⇾nwcZs1c鎏n6[Xڊ}= xS,4hQCf5MWձj7OfBm*U!;Nrl̑7k70s.8V0<Lt &E`%k$ >Ul~ϬI]H}>28r&__+Xf%/nmbmj|7V곘JRRΦ}^)X8mKXИ~nKou.!Q/1,_ \FAnӡޑ\#s/P< d/2#;h, i =h;h7`n,i{8 !'I-]tꟈ^[o#F\3`lrp#JT K !CG' OXz<Ϣ*,öküm9)@GÏ 4<Fg:Oӂ8b)PNȺk+PrY@2KիKȜ@CRFODԭ}x7E*]V,K/9ct9իyC øB3rǎJ$?-Dl!)|o#U]عg9HZ\~!`KgϥSi[:Њ3}XӾ.:ؔ~ĀmNaZlM)w? s"hyY4pi9ձİ[ )Ihg"N!JfT !@"s,5YjY5p2K4}d(F6Mk#Y *V|zCܯDJN #ҁ x"ge$C12I,% /qC5M9tŊ\сC~<74GV}wv &~|>Sച_Kt c;Fp@;$"a?ʥW)W24UhGv#t=ê( P9-K{'l&e7"z,ioƱZi PۺcO3b:-D<pCf vlѽIpprP sd@ 1uց6ՀZB]">M-$3~E-1ߏc9 aپU;_Q!Rff=2njQ\dL=VWn)ؔǒPU/0 !ٔ$VqiԚh)9y|#RT곖 0K99?y>c^LaYpYzx<1V57.%,nØe-s50*owːW1Ժ@Kg5.nϾ,u#5>Uwq!ԍ\,mamrW_]T@3ZU -k:KS}һkS3eCQJ9GiYJ%.AWΑy9gn%q%c+a11@{,Jːj7Fc_!TTszoW@|/_r{p@b")TF3"G3B2J9HsǤ\cbEֹtϼFǹNeAXT%y~^aJp&e+F$Wh%"+qzard2'YӸ]^o x%:¹[ag8I\?ȾdOgG]K05MFtnBHbJ%,P̵FC\Q~ ر\]G279G>Jh!uKw4FxWޝf%)C-*4 M;_h ` ePBa̸'z[v +@=9+XJ6Uj W !)/bu&]W жX?5h [`\^{Ѓ܃_- YT aYgs$4GQ}Rq<_( 7oŊty >Cg]ۜ=ւn[pF'9P]C'4KHxqpl2<ECUЀE,,sӵa&U**+΋FP5KqI[,@\ QP%5}U][5F[kHIr\tendstream endobj 526 0 obj << /Filter /FlateDecode /Length 4996 >> stream x[͏uɾ 6fNıe+Bn4nrD>$3+a;MǫW'[eXe/=V{%*=~w_Ү޾b%JWָT*;zt}[TL66tϵJv(OV8.)v&Dz;\".uЩZ6B_0+er}4_cH@6I-۲ ɰ1">zmd|U9LPɱ"f"6-oV; ݣP/PwjzDb-2Q¿4HnÁug8vJbʮxlׯቐ :y8:5@De#D#=AN1RUYWnq3 Yъxӑ'ڄg%9\po4 `@rn>y Հz.r$>K;R 1nzoOh+蒏YH5h'hp*U To*.|_d;3eaq70|u8՛y5Ֆmew SSqDKj=s ~g*@ )0Ҡ$Y||v 87i=zEPVd%?A Aw|1x|p͒M9|?Z2 +7t솓Ct0>q'ZgptlPϑ*|ujIG* o2Bq{$5؜Fbʴnd(hDiaPlqÁ[G3i?,-K|P0]9KNw2cȱZX E}*d<{.NU_<Ƶ_d b`׾^SڰG_ϲSa hPlP*@IV$J.+NjL*-B=W !K|do*͝eBԲ!"2$x4$#pVVh`4=9 |sr`W).sI=xK׀}tS7AAV Iٳ !ܖGAp>D:xHˢ;e&Y |! HRЇPDr=-RiWw8{ -ix2,&2zXNTpu,QxO#撻/@qAxHi ޹˰ ~O_2O5dĪzWaoE`hɔiSBrӡV=hLir;8( o(ͩ7.X4 1 8rs(`Ӏh]wG_1@+rrpA US@9"2picb-l?=& @ ~6z@tY#Y s.b T-6Q1v, ӄِ\0LsX!reoKSqw:3N]xC%)A)1 3V`*!$6zb]s"8PqK5{" 9#S?t0D,*:25rIdhX`h Cϟ.QQo#s} w`?{(;G@:,WX~HʠU'~Y/Af( rȵap%Vvd%l\ztɧWQuq2/|361 ',1I! Wpd=)vP}II9ygv׶9]pY):y,%l r6?.~/佅_v]YŻmsz.KKC ﺐޢu3'DM j<@?3? A!`z? b );J+P~ ^gڕE[,s!GLTvFOT"q!|U5lKL 0-pe l>9.ZС>J"cb+`t{՟9'(ZHM<8snh(@ÕkMrW §YzI ^rH'1`y*~  Ҕ-E)xjhm6nPOk8uD?CO(M,w.HWDspJOYQ))1T` H, %reb8~s5AvPf4OXtp %4->O,pYO{Cs 0X[*wݿ<+'"OnD`9(O혷:IǣOBhJ7SNS{IL)US*d4[OQңظy 61 Q/MsΌaaG^0\%<]幛s Ly@: nWeSyݺ:ʞli eEۮ 0 <ΩҏS%&jSUTnOI2+ń9.%9#H:Ƨ1Lj]ǚ\MHWM@ԙ}<}yϵ @$T1ֿ,bA KGs60&'& iz0ߧpzDIz^+S; 'PNnIHF6QY ֜s9g~BpxIyM~#kK2p۩:yoggr1ce-$^ !Fy&w/$<>Lr V3Lp`Zp_> /EqCeE^Nq\ɼ?1 s-,x%3u Gc;CΏN0y cGYmUϔ MRz>]qHaړ./C t/ݸNX7LJ/!_SڸϝQZ?^C'1\O5uj{z[S\LW@>ќ/:U<B[x,IN9SFJ@UOMXeg` M=U\5[i,':-97|,ƉcUU?T[>qyo ]"ܐX{,K-* b8fi ;LrNe\'\H+zfnmfJ 9K͍wOed.4- ;q_Fd &O>:r&S ΤfaG焒ÅiNgQz g8mAjrDk(vQ=BAy}R%N9-SҙEv-CQU^մC} =# QݚڸkK_偟c^\iL&ܨyKuͤMwۡjN zt7J; v|-d ^*@u]&Ab]v'mmU#s"cͼs|tq$nc-EZӪ.׆ݚ#IJ1fGEU5Oj á<.5,pO_ dmvCל'ūSNv\u/C5*?|0AV$:9ǶxڇҼwɰH%P7#\huґçu+&u5~UfMͷ>e=<6-~Ib'F߫dG_Y]4x`+rxJY$Rʰ6X~樬ݽ/vendstream endobj 527 0 obj << /Filter /FlateDecode /Length 4329 >> stream xZYq~Z=IQobHVY+nf]bG&fvE28oWM-V MzFU9~sw󋯍^:XiWw74BڪõI:FWӲR_\k p!2oz}k/1Coi]=M`WK@G >"zudЮ`-SoC1t _(-+K#1/i|ny y#opz EiLAyOHC >Z{&N=OD6p~ML,f y[}GY'pca\*\z{<݇.JBflõ_|mf>VEW}G([fM&T[ hf$F}׍6ЭK[Nʻr-nb@&(*(8K%h6StpBGk Vn9`Y@BhMH3U )\ := ȅi0[&:אdF6'{1l2P5"4%  >yS*bקS#wk|>Q&^F1(?"@zI{%3=^F_3Y%rl#̄xoHs` Cw U%͢%>@Γ {^'˘ @6"@2bKf;gXU> T9뒞^Q/ƃZ=ҌX;f;V-.XǂaJs- |6" 5臨?/-Op^ł`T흶b4`v1!j] _澪OL~ J]! [C`a.06)?o0rt=+X!%)ooS2)B:QCwKje-NҎt5Y0PA` c ϥQŀ U1Wh` `Q]Z f|Qer,TY F$ J4iPF+ (<gb52Q1%P ZpX -46a4~GVsl@KXQ<8kQ>|ql|!1 jLfER  qT"hE,WAڊ 48q(:!;b]lwUQ=Ľn^t[o\LoiWk?zxŐg@J/=tG)` b˜F9 _wܞOxcECD2FO i"t"GD h@:)sKvf C@:S2"4giT@Ќl;3Kޘ1 ى'<1-2Y(WE( Cyu# x%M=..Jq9oKBB˔pxp2Qz!# ?~6r/J6|F-æ&/Ȓ 0\%(9'PUW?KBDAp1!WXT \XCP rْ&S%F%zab!YqV. coNz`<(%]s!^D5A _MM~H*%BVc_!EjϫyGz;%uUJIyuΖ&̑2FRep<8%Ujm<?@4:\3|p}>=`_OW\XZ,H/Lc Df2R1C&LCn_B"]ZM#Kz0LXG=ϼ!c.{yFCz? *S\໣cb-.pڞ NSOp w]'a< kr頸)[vmNcM֝{K%}Fwzi,AJ?{n6AzZB"ޭcWXMafmGrͷ-}$ӒIv?DtfsݦZaBS3MNRąNRlth|QB>B@$%!IjJDk21q9]c.bФ|aJ=ͱ sРb΋܃x()"͌c_Q neGiA&[f^*۞ ~DWG)> h-v|R'0zzHz~?nǚ ;9=0@npgTc @;SrW鷇9&-hs#Д~̖&jϩ3¼FIED6S~Q@ّeir!č) B|"?*WV)}PmdM?%9|hyvfQ@IhB@Tѱe>"Ŀ *,$eA2ԛϦ[Su}t{uy}!26S;߫"kZhʊϻey{+By:4Qba6D(U)*d _O,ğ[zl ZgjŠa`]S"XYGrȭ!~ě̱;!@ijOLZe[T`# `TjN@!YZkbcNcLx>i6Kz@tׂ gIv~Y+AlgT%bv4T~[Y/$)[ iEl5'3JmRMq&<ŒKe}CrȏdsRS1ORjVjR\\BfQק>䚟L=%G=Km`IC-` m0 }b dQEHay!3̚lDxZ/k8W>jn]@[FrjOF[\1,dnjm1n,ȋ"~.t%)AMȗuy{OX99&)o29!]8V_]8"T-xZ2덀Jew]<:VjX?(7n)CxY9M}*4_υtt˖*<©>]WSk O 8S@P"xV`Y]9l7{n Oendstream endobj 528 0 obj << /Filter /FlateDecode /Length 555 >> stream x]n@D{}@vkE PPa""[Ľ7wruun|Y_?uzYvc7]gs|۰?h=ןal۰ݩi- q?z> stream xzgxWv ciڴ868@&F`dPBBRsv9)B"a؆3{vlx=s-f%5>>D{9}R6x7dUL<˃٢A87a``GG tsv-QZP;nLP˪o6+0 1vǜy y E7,o]*)-۶Ul_[3SxaC/2NJiOn~j -V`a+װGUl5([Þcl=cl *{[MŞŖ`aKeqAX!`c1!Pelv76 fb#{L/eyG>|Wuw,!AC!}Cyw-^pS1GF^Wv(g,U'}{ EXo{{\I&S渊qx_6^:~  =S|f4ma`A.°:E wQ:ǔA>>YElYyie%aNWH+AHmK j #0ZMP"V$D}\%s˸@b$%!yN )KY n~Ƀl!:SxEX֪@nlH|=)X J(JSp&cUHHnO ]~@񶄢Jh.2<3p.+′⩈7Fܖt&u:@b?,޳ o5dga۹|Q?{;J`9."<GcJZ"=MƵiCVmn4U{Kŀ {O= ڵ%C_YqÖYvXm&3psf$.xS^)b]'(Hvmue-NH?ZV7pIW\kPTI7 :wC~hLj!vY1;@)ZXe֚'0Ј:>aO&g_a\7RZdDcL';CϬN.w $vKHn\5_i2MKYd'(Ep. G,5& vq괈]6Wo?6.K6'ͤut~Ѣ7޸Xʃkr܈ rįS)# XU0RPphmqwr?X#1"@IhE p||Yf$7}Q8Yӣ9?!IiU~xg-wNX޹״h1#ޭvڛ&'9fX8E&1P&\jz%vMbb6Ѓ&ɆTnQ$?شaK⨒dooYE7/s- IDN{1_n>8%{z QKҚ:da[0¾CΪC/%O !vr$Ȁ W1adE}0|vdQS))=BwpuB$ G`e(f+瘕&EIXNl;Ր).qJf)7qqla*iSNc)Gk\P3eO;VDHV3&Ŭ|T2E 0qD86\~UAWieǏ %*FW >@}8n(ڼvqY@gb\"7H8uWԮTԗjkken7aؚ jRdXv.%,}aPO}Ʉ@MI֪+j)XcB&x6Mhj3  x2u_pn‡HD'dCpMy25_ jm&Df!06\]Y(5ԕ+!ZZaA_9*/s +)w=dCۣJD0/S $Aygn ;ǨQ(`cGHXcxk̺7JyZ OI:Kwk/]rx- ~TH-%@ծȚMfMHV++|̗M؍GQ!G2xGRg',HmʙD$D\$QMƽirhn>>F@hE*eUg<qPFx_xԋTjxN1ZE_}SW.F.{sFh65m*(OROYR"Asto.\1(Ux@kH4^R)G\cYyGL%r/b2->njV* _u ϵfA-Xbuf~ql{Cɮw]>@~G8|"r)KFCըg/W#%!MlQ%py~S!2~Cz.?;#;K/&nnIN>k+DNRуit!v\ǩ.qxQKܒeG%;;`zڏrk%k_VڗHy2\,xh OH)/>nQaNb@ D~젓+QEN\] e8f3Lw`| \́ݸ{w;v|K߅>t`[^*HRX!*9~KS:'\c\].8gx)}h`a:k{rj.HA ,Pu8u=!ʊE]dU)Pɉܼ&vaTD:L+ TlSKvPFd_8!*'_xT%hCr^Ũ"Ӄ9ճWyvo#m"@GSŐW\ ΃]N=yvUl1vA B5x 27Y/+;|~h>8/wjAEL_CM?(C@JBO"TPE[!a!4pLB0dtg[}dA'PI>dwsޮ:*? @HQ:h{܁85cnk3I82粢2!Ғ68M̧ԻkuQ "7p.\D) M"D!L? դQmRQf&j]KO=#ѝn:9U1wRo5ȯJ}8'N.6eы6[j)Sn[0 Yi,WU?YW! [b9 Lv,9b+x;q]A8@~ĚkX;vw;wC^ByG>!0 bJ huWp/߀"^͖'ÁϫkFĜ{%H@3]9C3='مwڭ;\ÐrSϩ6ւJ~7w:ZZ;UR;OMkBm(?B 3RB }Ȇ$qhnGs};ϒ*q#=|x:\:UB/q\ .Η'w&vcSgаd1[A_mԍAubcqaqEkffbl9R6cEQ> g޷\nl,1lF71}WdLCu7bhiGPQ> Ҳ-fݡBJApL QDHRoZ BS5#\.է2>u)FJ%}k{f:I{*º /&UoyZ6y.3->%.XsDwE_"gwm _<q BZD=_ Qn)""HrNE.[:w™o*urYZhdf竿\*." GbG;Ȃ/ !~ND[>ő 'YDʔ'TXب\%,D BE]*4zaU?\}籲a#ITRkm_}UDӠ4$<`{}NH|C>Cn[%D^x7/1[SBIEQMHCtnMUDV\"@d}cBl}yĉ1CO|:)@8^ꕏ*"O~ #~b" 1ŔL! zܬQ5~ݓ@?4=vL aã y/8t`dMk6Y9y; eƛxٟ0,pJ~̉c}6׬h5$Q)w3X\kPz2.j0}0/fIfʟZrαV[E FYH1<7O% n ;"+z=DAfYiQŷ6jKEZn8ooLxd]ڍ{8t,g223W0eȭ`4ssgC9&{@DKAr/@ G6 .S$D6aC8{Nc陷eP].H s4GkƭnNYkuGH @bb aTE0I%7hR`zP*rQ{8 p+ܼ2VzCދG7Фsy8b h"ބ_-P"e^BԌ2݋ !2٣GI\QFTIĈi,MG$#`1{Y7(]HB{.ҳESi"O?٤A9Wz4hb =塭HC1i pgޓp!dG)u =xmp@F~N~m̿\c8!]R7t "*,JH3:G>\n/ ZLCl.X!H XVYPapk1l)"ҳendstream endobj 530 0 obj << /Filter /FlateDecode /Length 3984 >> stream xZK>XL+"Ň ;p tkem=vǝ ln}!:vTјFIs!̝\꤃'n/J;/SJЌVS)rRFiܙϰ!haaf)%7DrhaxLlCI~,"s oz$m3|7U|jg(<>S[V6;7kCq2P}৺ V%2s"wp*i:f2_[kT9#A5ߺA/H_24tl{g`[^3H)s) ={0da8{3PpBe\uӟ^wB(2YuS{i` rבH V&2La5ށU]wiɆJi5ϗT-e%<39.܁FÚ~nl`XDtE7B4\Gc!Xa<_)K{pqTiSݥk$F3.!D(mˑ[sJ82 >Vmd`Sw@ߌ'"5pa!'0r( rAh^~jB &M~aն]d9tf)٤ ^X  L@\y5-+1ٶHL=㷎_J4[Kfp^vHK^|;%K:%bK`i^cd$칇ʑ%;Ļ$*%軯&\׆#D 8V !ef/2o:.K.rdplV QD t!~#LrοA<)[NM^' 9n)7b>8Љu?U'OySh]06ۆNA3HixB/yPG?YGA ]|>ei)WqaK&,w/09'@̕bd+'T7mB$ܢմku&" g}_y:pA}$D68w1n呆 "ɶ6&88ڌEɖNCd81Zq|͵u5[ɭM7A7y}X|Acx-mRrMX8<\D3<Pܯ`la%Uqe2,SdB> CDRh8N]dFC#Y+ ̞\sBu\ž i~$N,l>hYk3 zm_oҩ2 Jnv0Ģx)55a"A i.4ǩjcΏ/0uQ0;K XmӅES4 +@7lE|4TKsA1#*B9#Ӵ_Aq1d!&q(G֙qlpkf,_[Ҍ"r57b r:A7dX&7S$ oʷXc L{CY=Hr89*$? C*1lLx{4bslbGCMD@3EcfOk`~ nk|`R9CZfv:ߙɘb/l䗮ߡF\R%'4!F`1mI vÜc5,7pyLRy/k(ޔ9QWTtӡ }!@L?:pޗq] Xl3>Sʖ8@ahۢXGH* O`/v~KP%ɴ{әT+#:͹JBc5V^d4ߦf {Q*@!tx,SoR;N97< =5-`/ų9$a|@e Rhևu%4u qq~f*BD#C)PؙفvKG+Y#rsfl^|C =H-j}t:}-!.eU @; trM'ӧyF0Kakާ ){#ֳR,UAelFҖMr8V]9CeƦz鑉M!t[KjY]Ӱר\+?(RnY6Y0+nUG/78ԲV @ Ç fpSCloᣧ5cN͵ǫd\bWaD[ݎrcKN{q`?.l"Ni:uieep<ĬF]DqP^:3W0E@`rmʀ惋]@ <3OO#fI1qe!HSbR]ݷ9r(oXFnV^*׷deA8L2$[o QTi,XIev)tdx O}UpĠZq|~@]:JjM\GբrH}mԑsBTU8$]6;,8*܌)MGP]cs̃*,aw Ɵo\BMU[D#jM.,;;UZ//Ss o\ ٮ~U&mE A!U7sm}R\Va\0.ݮ6p} 4wanT=-卷䮑h"7¤X1\C{jsDEpňJ,"[hʚ?YwWXKĚ@Wۦ#%չWS7L( l]qX4Sp–I[Wc^&'y1 %QC%- |)caO=lU{E&Rk;[0ri ui}y*di|pivTP8ecߞQ8K)[[u}Z;oUa\C-*J A! p(Twtq*儑Qr0"$9^ƲESb\GMLrCpRשXHc&\NPF*p\ٚK@XH` HPs.q>}ZC")eQteb+: 2w߅G^} %L ugc|1I#:R~=06V8}!Sr9.&L :R}8KA+M+PPatg-F#)1N*P:ޫIA`oJK!K1=2ENfHFhPPC#8+r@BO.R Yn h/b%@K^pGKx!?X\Ua,%. o!L1?2ǻIendstream endobj 531 0 obj << /Filter /FlateDecode /Length 4530 >> stream xZKƑ=[1j4I%ڢeK#vAca|v>B9D +*+W|&&?u~_J_u`MNW%T(`&+-xssنlL8Î3q (GVNL:pb]Hp\'6f/eRX+yUٶa7.vД2\O~&B Sg0T*$:U0^[-0Ԉ4V|V69xX|^4IpX#y|\2O\{K,sE|YgUْk8"-BL,e:Gj¥ōNt1ǘ"2WHsߌ],j{$1ߋ:9&h- ?@˧Мihx$ďSfUB?dR~ t΂f-ZTI6>WhOm};X a!rjns\ݫߢW|@~Z߀!kYšU9<]MA=R\ x,x4ʉ(t n6e֒LTTedg,;i@ȌhF]d_M$Ɓ-`и#1 8iY㉍h19]VKL׋V;HH-wwA ?kR+[jؙMO6_OIoW0t:;VhW"É\6Ӯ&V8Cޯdݸwb@3Y1F|1/ }3l'-'2,CĊ=d{>#Td.#=EC%4J0;@b ~=_qNܐ3U G1_`#C=&PKA :M?MC.~ {Xo Jx5;#N&/_U ^eЌ#D -sga ŚO-aKy[F^qo;U pubot6P-Ⱦ @qoG'#ƒyсrLU?mIh9\p/#i)z"<5{2'3HM{{,muֲ H5lutEՏM, 6}(s,i#oQE&Ȧ3)&hOdudb_{*Ej|` U-(*Bz,@<Ӹ KDVbg+"ߋΤf&ce}Ӂșr^QH)S9M4+b.T]NzjCi;~3Ru=9Y}(T`vb~[7xl'WRHj fI.L`0LXF%JިJ̌0W_NT6{$V}DR)HmGN9?NUa9=Mm 粭+iqb́^) ɭz7AFؚюwx2Q3ͼ b3bn_ !6Ħ썝_Zeui%ql~5V+`T5f%Q:2 ;.gp} v\pK vriT1 co)wuif?#j3zK ax[IBbn|a, O"Yiu?mQԻ@ #B&^,r`eXY@ c}9T[6 s#XÅK h*H+)8PҌ^CE0H9m3[͛'\5yLe#y VB^-fRLDh)FaW/ܰ]$Y^U"^@ 05b[Yl/B5J ˘O~W.){Bjj-}T y*R 7+S|Mo K=: O2#)ש|uIP7hij1#fav)-Qh7\[ζ XxNnK>~-_2V@{l8էEuE2T`@RfWY!HyBy<~^z(CMe3}UqdI8.hcXo٬IH EpzfuPO e6Տ%D€#hZ8e\ NR\uSU_ P(!L@\+Qr穂 C¨C#Dd%JP٭z]Ew\[`sҰɻtm4Kt;nM9Ȣ^ E][׺ٔ*_)( mA{!]S,grw4-Dr 0ng-LKFwFT3HKy~?>x?>݆b,Ttp;k)f߮UӛƯ'0+4T)ڬX\Tr/&1_#%Q }+X(Rߑ6u:j"#OETd\wYB3eEQ$a?W 4c=}\~cհۇ)+_tY+m<1c3e0D&1.+˂m^Bd|eCSxl:2ut[Bm:hII䢆KwiKjZ"Ol6R.ɍIѪ2P%%G{/`#&!$;˾;\º;aDhAyQQ3=3gu~瑠E+4K!d _Fܕr6w@v6Y[>Xq\Vq::<`66W/ՑmL,{iÖrX8g?!6b ][=h p{- Q$M 0) !0JnT|}GqT(dhr㘇Z,uyy<3D̨թ 7H[a*З P=ut84bBea=I !'Hj/(֩8EWN@Xw9y)=1z3jrFcpCQbعmIgT{>Lendstream endobj 532 0 obj << /Filter /FlateDecode /Length 4663 >> stream x\K$7r6`쥱g_CYTnMZEkyX jȮN=UzG|UKӣTW`<"YY||ӷr37j3o?݅|ƥ̬q3m&ogٗu}n ,m qVjSeya., l_p <V̅1/ 0ڳ.nH P1|^ gv3&/ _Nfyۭah9nWvךYEyaaKq| e#k@ܠs})/*+oQ;e̹JV:m3'p3nr-Α9-s&w\b0".ʺ&y pl7U,0;b3\mʹaت* Q&`z~?Nj/Ўvjjх2WgY4e AN'9xCeKOWO}fJ9 P08|B5 /^Ն 6/dZB;0n<[k?"4%Mg>u (5Mnh/ @!+%i(Itbg6{ N30e0NƘDT> I: wN1 /Ƃ'h2Ǐo/G'~$`We'j˾p9V=󢵃 Tg&~B=al!ӕ}|L^s'U8g& 7}B)F~s>Qܨ^ 0ؘ D & q+((@DF/_yܤ𱼅^)= 04V`TiKYy(%5S2j en۔.QAA ҥrz?[Ȼ/8+Ŷyg!H93auReWLk$Im^{KE_,PW8+H"[as[ ABߙK[-e'\15mǿ j4Gz. Veit؂Rr\"ʤm?0Hǡ'\ڿؗf{mfz?BXt[ſ|>J9ǷGs>p8cVi7h}[/q٣F:0: QDp=bt, Yc ą~_%[5eO$rcrRR̞T!٢g$P1CXӇD9JcHU! !|mJD &Gd`08c9#4eLJsVχM켟+#D%9va g\"F!nY? gUBoeRbKbISғƚ}eJȲzMoIݶLِh!=B:rC/mǰ hդU6C jZ@ž!maN [S L%gh\#5kǮ))~ &ArۍbV'5;4eX_lIx!AG Rڔ9x2c1FJc@)ʬT)ZT*F ?ȦMli.`P"њRUojza?#FbRn;܄ix.yH& uhq(:0:hvB@_T}@4 ¡$FߡibUCq(6[&UC9eOTH1lJNE=T-I,qO`=h<laA7x|_E9pCuKM eٛܚj ,R^ Y/בKd kFdβk쓪'/ }u6GHK?PSZςtCeO 6 |dsڵÃȀD-ahY(BCHTu6"U. 0% n]TbsN0n $`o/DG}/S<42t!c[g PW˘©߷/b>Ap!#o 0{ 9{ץe,Lρ::TL; L/P?8X?X,/j^9&ގK;kvO;`ĺ Y}_8(ʸn@XuT.!{Lia fkL-ݟ 2&ObCp[7/yBI d5Р m.\Ҫîό\Pk* l0'vUtU$Aҏmm sӗ,pYځPbLE@] rSCK3b!c- 1{*)$@?ނb |'p]OiMNHD! Fַ(V՝\G˸m}?Hi1 $VR{N*d쁟YI${ mUl@ۗVV9ůMoPʠco=Jږ#[Rb:^Z-Lx@pD(sn'^C.$+0(=ubz[7C/t2v ?mdNOCZ&0@<Ψ"Vw8)i'2i)[1u cl~l wL?2ՄY0_-L,wfjvTО0|/ƹd6sZpEoN;}[MkpA @+XoS*na(vI)ĵeHݐb-`pNO.$?_p{}g}g()= .KRj\,nQFTia_0ZT)GN#! 2] 9 ʌI@xtn eڇaރ+?FDum;QwnjDCᴟGL`[TXH tXFN~Zt_.SRbi|td^L8i!{)%i ؀ $w@ x2:M/~u?H5uJs`G M7 ǞNJO)Ħ'cOSizpe'9S1ya*bCՇF !CǶzNtBg9oS d^zrא٬ At)!I%,Q~HZ?͈!前_MHn.zcApRn+ ,'K\r ST.-^W-^\N}~Rew΄{gZZ?.1cfj:˯Nlv9!KsobCAroiz7tƠJt f/sīQ"w +>X]d=D.%Qaj$gQji/7f] sd1xZ?qtOY s}^}o>*]r0Usڙ>T }~OX:ܡx/&&ō'&2H{Q'f @ yNTj"뷖'o:AB@M#._[{ lp1uMiKЄq $K6Q`'>'m&)M--;[%O WيϓU"I00 rP p|LAu;u^pCXe߅x)۸]oߵ2\E5X+2dP&yD.8Kw_;7Qx9:"͇7W\ Em@>*z xD&\r23MUBxX-̉ I8D.t}wIx.f-.Ǘ+S ф $;kRQ};tЉtfDNZ>WW˨srȧ݁W^zi mئڦ'{xyV;*t0~VTF!vZ:0C\{/9]k[uK\ɞ||v1iK  9U0 4 :V\Ui]pqC;ûTp/0.D aĕs Q:F> stream x]O tX%Cc""dߗGӡY>w:Wt{ O Ti#=nvtER7Tj_+‘j z;U7b8< @/̂d`JqF"q Nu35d"(ޣ 3:뒊F8vYendstream endobj 534 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 746 >> stream xP{HSqmk-{1RfBier-WU[wi933Eeك’0*z=~M9!*aFڹinb hQF_eX,%*npw*$7!OHNNZ''j.0YF$XRs UͿl5IRiJ\\eeerο];p-~<~N#\ ~=ji\}hs4h1yuqzn^n %)h6C4d\) C׃1tQ:]ʅ7+}7Πr|UT`\}PKNU;ۜ--A^d4^~kp@9:;Fc$߬1j_Fo|7p3}rŧ޺]vĶ831 8_|]>YE|S*c福qtEpt-ф!V/N;dhVu_iV-ڪVP+׹ε`13hfnVGNN_]K~cD p: -Tф O\endstream endobj 535 0 obj << /Filter /FlateDecode /Length 161 >> stream x]O10 b C+qP(/ СÝdߝ|]%Cֱ5"HcQ]8LTg:?`3燞I>u]VBoh )jH4J 6ᬫ@Tr48o1Ҵ4CN+S%endstream endobj 536 0 obj << /Filter /FlateDecode /Subtype /Type1C /Length 301 >> stream xcd`ab`dddu 21T~H3a!cO=nnIw }O=V9(3=D! H*ZZ(X*8e&')&d&99 ə%`-6%%VzzEv St3K2RSRSJsS@%E )Ey B L,A?:~to1mߏ=gS@teGwon9l]=AQ.2P@P^Vqd`hendstream endobj 537 0 obj << /Filter /FlateDecode /Length 5222 >> stream x\Kȑea00\6mqI>kcLg˞[b8֣F>o߈LI$<ч|Fb=扠7'FߔEi}$7BYc+27'氚~ ]tp)v|l Ϲ%E>ooХ:܈K~eY*x~ϚsM)[.M6Ǔg+0ҷظ3RWŵ;])/\ ,'&0rZ|M4Y$WYh,y'fsQ|EpX ]Py`p +,O- Ľm"ޟӄә^[6OYXT. cSW#BKW͕lQmAK}s/ 8DzjGN$R[ƃa`U;{̽+(˛ W6zh%9WBJ"+`_)gmMi`e47H൬ˡ!EBo+p.4dRB5\o ,gOns #Mdq$ 5 %1:II4r'P.pd%Fc «HpeP@P ?&uP^JQ I HM2t[bgܔР#[&^8B4(E':Q^ )s+iI#⌊՘ֹނ3~1tҧ 2 Dz.zq؄؂:p%%H g/%7ԙuZ24t*Xn>AQi.M9'PxW%JuT]%Z|ׯ#M/%Ϻ!WX}lp_ YNյH]rI^](p|(1%,CË =A 0˵c%W<_(3%օ0P4_'z4x]ß4=W|b^ ]5laqرm٢NFag/?b1Y|] "%פp8mCT`o ƑC~Ͽ2 kauqWy* !`ZvG P< !d_~? [ [Hmج?|g7y߅|jL#kjH%V P4 ^hhʭJ+]CuUb.(+czIvl!w Zj_:v[vz6mh^?`X6E=NV]eؐwլ/E؇ KlO<. =A!nkYW@-R@B T? :6-uSO_ -D B}|Fqf?ա^J m475L ZWV` tx:pjPYT_ARqm7U=璆 'h"R7A#/yaH*Ǝ Wv_N +y0NNY.fSe:ԦDTmM`\֌iUG0 Ђ-J t 61VuVnH5*f8c ͒ 8utIvT>,R9Ƃ@JϾfDY O2jJEtͫY{szTMO$2e=k Hp;\)Yؽr .xeH Ѯ;b t"p8l4 ttGw3q<+0,ua EtgxBɫx/0e À؛6gS@KY#+UEY7ǿ6܂{quyah@ S.e.p4!2@}L·,m@.$)^WY(T wk!@!>V@va29q0%zX5-@#AS1xЧ&;/XQvaZ[p> tSwV(xmܼyVy;.t۴y£P~)S_=Npfui<:*Jc> \Xƒ+S6ܟr^JRG'I2e 6c(@Va2{0/Ը f$TR2ʇi"ݧͅӪ$5༁(:Ѕ"(5Š^ƟAn輏/'ݮ0^&FyG<'mpd4 9Z8>JJ^=~cfǎ0~2vss= *u\i" 䣖eȉs3OyDt.JPaWG D]G } ;*#T=>,}f:L`a=ccWaiHC-BX,fJ"05}΃3GNiQ34Qȁ.bQx8IYod.L[6c]6#TA:!im'9F Gg{`R:4&*S?G {_MdE7uP@|v ixPZIx[xe#I5ڳ؄io1 'd~Iֈǘ )hB| Q}%,%qJ,bw)6 ]j;W)2$7¿T VӫplNb  N0W'x0|GDeSO 㴮@2C*.刟NEy $HykLh㘘'm#WZ͎ʐ%?N8:x73v}FUR |:2N˪CM8fCTO8U1\f[m~m [2 ih`c5XjZq7}*FR@lQ䑇kuNPvlIcs9>a_הIU"s-=rҒTUL+HY-v H?mh߮X䋼gE̹5T_T6Me!"&OroT1odW(iJR9d敃ӱi;Y٘}΋kLu(U[>=XҾoi}I0/UNÖ =Vi6paLe}~q1$NʚOo˚~hxM5I%z</+ %։srTց]n֫n<|bxs$]13i&a+cFte|K (|1}4BV.P7UH.͙Ajr9Z¿U6g(|ѯu*И\4.=)=e.9ĄW1?=@)i14@2üe@2=ٵOP*"~X*;XMa ,^}.Z WN n$9'(E_wLT C-?M&.}:a?EL2P25ğ+ B}Z2M\4l@b7l:)`/зTBXAQS p=`n]Hpdj#LN~ | =SۣT@v%7*@ Ȝg/qPPvZN>0VG. |QPF/1M .p;\G79ogBFp>dĀͺ3>2Nuul%hG`O)? /חUnDߍ=\9csα {;endstream endobj 538 0 obj << /Filter /FlateDecode /Length 4850 >> stream x[KFrŗ9Iw{,c|gba;Zi ˻ _ SũTɚׯw<2jH32/"+ yW;w?͚XDCI Pݟ^C(_RmT(RyZʊPoqx7ޖ2'Àf Hf6/6[ neQZi;>VEvi<)Ǫo.g\2FT{)K1y_<MVmڮ𼫎`wv[))RZ9`7? DXュU4F%:/L{lO5/j8-+rЮ{$k<.Nmo`tʠ$Ӹ c{Hzm4-^Uoa[L|/MO{vËفdŵ7O 6ڗjA(aout9T|N%9Q8=]{,O! MVW4Kzn,1z5Ft  R>jJ8d#jWHЖK]~.Sr2ѤEu2rCÁEg[8R>P:Eb ntD#͍i4 H_']?o^ ..}uyzĻ4@:@%7+kP>z>:UŒZ)N%t8VkZ} pj5 `@UMj-.O}s"l]wG@_Y4i5E,4fh gT/c<@HJf]Tq`yZ|[G­~g{f`,B MGUPJª0B)BM̍YH\B u!OfJ' m)mr1#| APMNkxVxV.Kq\bSv&QT!2&v)VFh:ހH+k!@\RWViˍ meG"JZO-VG&E]n?δ{t]sjUs_x/+A~m]o$sO㮑&](plEʡph%# W2)qSi1JF kΪVrfu7= nzGCl;_UNHSN1ݝcfB`E/L1Az휁ʐM=- 5u̫F}VqHϞ1=sprLǑ8Qr5#&/ m:1CTS 0#ʉ-5(+u{/`k$V_H8Q}Mig{"HC}5Qa* !Gh6`3O2A"^f Ӕ+1_>&v𴸍\ I 6}E>=wx4}ZLORcC ^&f"%7A|߂{'qn:HiI@S%26oX0`0(2#ZSB1I",ڄ!.{0Eg[ 4}@v3e9׫KpFz;S"Sf wd ]sy6` j!ɽQdʑyxCt`G15;`c3y7 \PK'"h <Ϝ@|A`P)]I~lsF$,=ؕ/$WR`٬M3K.ed6-0ѦpC]N!mF{9lu3U@@rm="( _8l=3l|ZN~R|:n(@>7:epf1c W!)(5=Bc+,P]ْ"kFuhܚV un80k50?ad~M.ҠXIyoj [-r 18T b78{JP]*c$-Ҳ`DIfCe~>ߘQ9)>Q0$G* TtݚNxƟN;x4,'eeQɒSS1zvJ8#)Ap״*|1\Tٸi. Ɇ|1n77뛑2Q  i  ɞ(% Z r05${\㌛I #H bfT.33{vh *hQ$`%ĺ4DI a&Yy9v \rE*Mk;F ,͡PZu(OihYޞaM5u06Sӊ뒺ЀYYNz])moI|A5lD&O;( ]<2nrC2s?3Иo~ѶIiR:Ʒ!3F|,-/y\S.c.߭Ja>`gUSê4ׅhР <ax)%Z1^-|2NuhӇ4feU$xj5yrŅr 0W,,67h UJq`!cVN#ޤ-p 5ïNuS/,ÇqԷcm@)<0K-83GGj"a`1:,A&d7#|bYBKcDC*-7L_7I|V6ixYy\#ߘim"o>b$K[U4KbiS끳_p=n 8OysF- I92Blڱ;^Q8?N(LH\/2:C8A?-HEҜ؃t!qҟrL#tzMtzrǕ9W+Q_;Rv긌}j\hw2Tڧ283>'ȫ7:iL?M<]ztԖ]џ>dN`J滵93;;PQL%x_N෺ 9)5T@D> _`YVG L$&K hľ8ηWMشOpe|5̢W!Г<-#)ZYVJqC|>f-RqĊj ؘl䙛z;*i/ddn/pEr>_ 5x:nPao3RΊ &W}=ǐ1̀M7cN55C>iHBTi;<n@H4YLХFrvגvMojfSu/ZK6M@ktCi5e Mu3v)oQ]Ĥ.¯/#Fdqꤺٌo,|}Ayr\%%\e9*NO ;c)tƢIP3ӗnv׶{2)8pZK:T&L;SIo0o3m\0I1;[T*Hݫɱ:A#&jTzxg\l> QAأ,4-Z|'sЎګ絾QCkQU>3JyQ 0%a fd a /KEiK HwA`ye [e6o6tdgG ̭E ]Sjl&9ҥI85Kp7I6ҦQ7Tðxf *> stream xWK6 R6#D2@a708v+INO}q mŶ_oΛYJ*=VJ.co>A j|)'&H65p羒vJV*ˍ2[_>蹪 4z@h.glY],n(k]Rϣr\J⑝%$b0L}'GqI%9mjHr 1}7Ǖs* 8m}.(IHsL.XRSx27U ^mU=Pms>PA7 5t ,Oo=dV"ԧN=.H8v_Oo5>CWLwU/rF-;C%OWԁ`S67r2<0v2dPqy]pivG!6 RI˒ +b]A*R0eK3'c,w)<s~5Å `&f$#w#U)u8 9Fڏ`"UE*tKTn,jV.+;T8qCc{*az2}qA{s 0,'ImAl/ x^<ô&@[$uX Ro$XaG& 4 x%X@K[2@#QTCO 1m=vc;%2Y8ҁH"QJ-d s{a 0Vf%SDhh]-ؽjн C7o5uJ}\tmbx摘gJV._̡jvaWMt$"*x,MgsXw y`6gle:  YXvRР܋/// J4WN>oW,nz]bVD܉А2a&fOX bv1)]fM5k^%"y K㲯tNr&;34_];^E&S0.D~jɟlʛm^yhD~0hO8SVYM4 4 p}ᑖea hd8wԯj:Bh0`]dZ]d:}e:gohvHq.r#պVbp7 CCM*7~=u|ȥ;QdR3tF=){ ؂1nQx"g> ko]O> stream xWK_HC3an!#$+'m p9\ m"9+~I'^]2^/݇^[ 2ɭ,\&O/\jؽ= ſDFgVp JD&*vV&;woJ{Է^kSpqhx c²^{VMS۷+Ze!r8_=/,+MQ TyoYO9%lJ {9#M`xjRs lqY5@#`t.K65sepKR5ٹøqxۻk<;*2T~}4(S<"DOot.ùs'C,a %<؀ŏd.\"18҃)=P$7 ^tINs[J4dSj۹_xI*ع&7B[2MhMK_L{ei 떽\7RQe%wu9=N,oa4MHL/D} (P8ԁ6O%- ]"D/OZ@IK(!$ ' S%J`r]*IB6<RPsn8+s'p"DID[n 2P.:#WqQ̱ҵc)0UXO q%ou,S=t|§5K1= pxqJ{ -P-ixb4:V1"XaYGH1-X{Ft˴d^DFo|Gs ,$bɪhҦ5㔚x!Z[;Í.!&(Rْ@sv P c)i5;U>ZJR^O7 -@+;LXNx)]dXAX ǥD y5Ürɣ2 E$ aqZYB lhkbs=6K(:ǹO6-RR@  G. p`+m8]@ 9LPΩ RD" RJRĩD0R m 9էģ *EJ:nDžAi֚tx&D@:i"[eSQcK=e. 86A_GG*!-NƁdM)ÅGwT;N=@jGsxAtSuA2lS35_`g~CN< 4c Z6%,XkkyX̐ֈJ/e4M9oK]CĺU> lQscx CP@$o|~> stream x[ُy76/ҋ^b:fX'l8VQ9D@m pӴ#{ף>Qk8=90$;~Q?^噼/^W/~|!U9\~*d)wuWRp"S^]^|'J,uZkDVrDjg)q߭ɂj_5+Udk)N!xq:8M}^xie;Xaul1^<䥲;v/<קjao ~?7S{) '}h{`QQշ;: `&i j{cVf&taiBZ8֔vH f?o+zP#*4 9=lʮ{Um5t8}6ɥׇR;#]SݮKiCPx;;mv,C‹{| c8Xk  m@ l{Iw*mn?n2H ޞZq=A iJ*]mN"IYofSu/qkQvq8%u9whZt'uׯ_\w8)&EmRkCSLg*}Wطz*H a)hi@)Hxb+$[g*9t8- {8H\-@6݉ kH1YWpjdT'#My:4jwmN(`IH9U0a,Ç괋m%몾M#Kxa$M?]JԖ x;eTHSORl(Ew|{j[WhYaz>jDכ:2O̡ҵ/o8t88BjM?*TL߬'%dEg_/ϛ=j٤WOٗ-t;⡗QŢD 'I&c3-zwSI҅o-nwd0iWT,x 1.ӈ2xƏphCPa%!ʜ6P j4֣Kc NfĶ.x#FvO .@ez ȡ,~Ϣh3mr,c* ƀj, C0niPD}q}ymӃ~/Rc1k(yY$@& yP5SSp(DlOP:CA2j UzߨV'oI}rVE0kH^ pCޜye.傃 -})8ɔ)z.#aȠW Q-O&<ɝCНBF.6U! L,H{#dGL'1 F5'Ey<!8O=OPEpZ.1URc[x)GԈ qmբmz;r2&!.UUyr+M1bnpHL:wǮjyJ" `a&~ll,h1Ef\\>$z9r'ڸ1GpHwmm:Yv]w+ NC+B9wn4,! :|=x3 oNAR&w`p@B_lwNx Q JPW;VHڱS2@X;.0uYC)q(ngi_d˜sا8?9A˃3bPx#sVѓ,Db 6t ,ayU)\G:.LSdXB-K*d&=৫%뢞~_6aKdD{%) D:1$ \3+Q(BTNj{?C{l!~f6R%/)/E ~/F Vv}_DZhTi^\a.)b#Ëo m|C,'_p[C8A"ehǼ.9 $'CLs*%}*,7eA JmC @Y"C>Q?:-UJG{<벽=w^>RHZk* qnGPicu3Iw {q(e}ڃכڝ ~v9,} }?UmgŒ&e#H❰BIsAaT;.#LMl$єN_5}?w+S.5C}y=}'67%LezwfzF=Bou.bRa2ga6e2 [!+J)ūH<ޱ,sΑm6Q˞x+sCޢ|±ł$X> stream xWKoFB(eanH]&r &-2I5ֿzXA>~3|$qA1%֩TŒοC3F_Ż6XG) TK*Dž;D4לh.>z8+D_ **Cg*͐ޔ( McnvESUlznj4 ?WzSR"D*qS1$Rr- I C!4a3hNTQBH Ώɘ NH3iuau_U[. N6K! *PpvNtzX}'(chyۛ:gόM?CF-&IUkdܭu|jK#D8}9 %A$(,=Ly% S:NlIݰ n3jeOШ4C52xbyFu*͚5FațQC9-Rƾ[<g;Sriq^7b &9azDhZަ@g %74tbP)m|3mma2U*_K)3 X@v12vhHL$jKo5U7V辩.v#jyWsј"90bS uq ,sq  հ6`ɵ+ˀđZ3:f0#)3d12-6p#4!"23P zxdᒜ9&Z=&B C;%Fō鍱f408HKs.1,A,+皋|ԓ7h=K+[ǯn҇>]>(&MS.'jZ.ϭ:зMݺLpН$)<H tTO * "솁[qPMD[3n\=!O2T6NeAe̅\RL![/TfEq1e2C'"}>zU4>h,tXb6 q !:Ww\P' HO-RlUbFΡ[ҌӰ+&`y=Uw+{'Zp43T{Gp-!ch1l4܇Yߍ "sneLpm=4)QՔa@e?o-}ALTep*I7"2^Y'` Vendstream endobj 543 0 obj << /Filter /FlateDecode /Length 1585 >> stream xuWێ6}Wy ծT*7M  $nlkJl+I_ߙ!u>,Ez }e)2 WLdB÷2KDfD*)w~ky={5ˢ \( V9ia:BDJ4|7{6Ǧ)Ed8QJwEY]8 .JDžaǢ)[< kٳzߖ%̈́pWo{ uweͰgEV{KsɪOډT 1g7Y&=Ui{dW&],,[n˔l%BpED-تރ\e/BYI:k2˚xXvap5a-2J9h`kvhU.pKDqdlV%:źbS(dT(eZj]G%rV؀Kmiͣq*Pr{)цör¤NJ e}1Y7i-`I@Xq}Tbnqlj5>u[%4b"0]0wW3 WF@ ʶ%G =g5SauB rh(3QE~CXsKyLsU+%d-6\γ& ;MWuUXNxe]So{KU-zϟɫ1_fYE_/9tN77\A~Hh,_8!rl_^^`?T3x3Bo reCz v{ܯ(4O9:Xo|ksqt 8 a2cuKG0bgQ94dFybP͞{t3U yv3 =0zXu hܘ~@%b{~_шendstream endobj 544 0 obj << /BBox [ 2025.1 4609.68 4087.42 6672 ] /Filter /FlateDecode /FormType 1 /Group 426 0 R /Matrix [ 1 0 0 1 0 0 ] /Resources << /ExtGState << /R313 427 0 R >> /Font << /R314 428 0 R >> >> /Subtype /Form /Type /XObject /Length 64748 >> stream x|IL4Ui! XBB*$`8M]č.]6s+|__=g} $($C1m@Bi iKb>l08>_ju^75MwZ2.uEğ%q/9a jHLK")sI[ÔM ~yLiǺ>rαny-,! ² Q{^ 8-a5ǁsi=ۦj 6+2U|pzm a#$L){[ñ$&>wh 0~7r?fyR֥^ZziG>$^V{zp4 XkBB׭ <0[浴dS0^Z>Ii8$954S pڦl?0G- qTܘc^12%uM^uOċK8kO9$X6 a%!&KŸoj5?WzDK64Ffkqr0YBh ƴ(Sְ>z #./a/=Z'fΤ&C^qXYcy9%6 !ʚYA ga= b}) ;)QeŚNMʺ{:WG]K-qK/el it`S6qY~^1U]S14%P1[/wWaT%`c(c<>氏VE^5o+:Sj wl080$؅֝ku'i1uӅ'{3px_u*vI[<c?^CY~; ur60~I[u,-W0yXcV`H /BM-6"sC*5`&X<(tW,j0 5!NcJ 1#=MAɐ^K{aZV8vR]—vq:Z~ ?mj1$AX6 a%Ă6CB^$G- q=M9gLJtlߌ2!fѸB o'KYPnRjt%W&}J?FPAc^rp˛]7{n@m¸~|Au> l2-|c ]*62xԼPi5YHo*Ht ޣK~7^3n՟e=w[޸2p<_r-d0 1M@5mİ_:-. l[ MdĘõ8 U֐XkMi8$lkICNZkbg_%fj?Oʅ>br[5\/ nvT|=Mh9s,[ `{h1py2':f?ʚ\kUG`8?ͦxb!)%:[AA[ \0G:- AyåvIY3.؟XX8aZ@ i5ٶ B̦- hQx&!^G8΄8ky' qlStFm 8iig`L,`l+"2wXw1N8%a +o$߁S{ sIkxbo]F|LRP&%v^qHB#5$HJAIjh)ı1g/̑~Sïq5mq(ÓD,}~Fk{c#ql,J$Glac؀(%|x"mY0Gz) 7wmr#)Oc@(vl=j~-1,AQ/@+8(` 02$\na8blCN$}( vqػM+7S3v q&^6AfNԐRd.:I ᐐa'5כ{ez)W"1v˅m|j9 /\K =]Iʉ­aH6{ /a/=X#IM=b;v㴄4$DY_uOrIB6C[^okx!0MDGBt3~CY `X#f ,?aH=ONNzq#kx;R޳z0L H1M`al_r1psw..<(qn80q{ ı_*cq[`LIXD`Ӷ1L^#_xLf2.̘ip4o*b19x߇$1 @@^`}֠sEi5<{8X";%hXs"epg) k0Hʁ9HjX6C^#82#K<)K^0?_hF1j0G8s.SuJCDχ ␰気V"3z5[S2ƽ$wa+nihm҆pؐa/=n[Ɩ돡%a0GMP۾!A|`XyOX1,_~71^Grv ^c`b*K8c];b'osCO4n#3ZAI  ;a,!hP<͍M945<() !FW4ǡggUGۛrc_ 䉠{0.xvXۍ㴡 jظh;)N`p.sx%sEGطXQ6ד[[C5}&OVD*GD*(v}S/]mɷC sGkx`=j *ٔ>"d c0M8l& kH\Urk)ڻ95vp-&48WLJ|cbxmŁp1mÎ1Ե$D9pmI0t ޣ{{l-=̗۔؍5(9=0TkH|(_.)#N0. Ƅ[NNJ{V:BN}S< ` )a6H@ v C`tlӉϓsd|lqZ,zb$4Kv5$VbT딆]qH sKkxz>pL\tĨM$ɫFFKc|h0 H! V-08v$m 5¯-_ﭜ̤/G/i i0) $aM^^Z{_r"WI*Bc> %(̿ul0XJPb41Kɾ9#E( 8d Yd\TvlEL`+#Ϭya*N:Rav ep"=+ +-\Hh ARdzM=L:#'F<$1$ %eX¡E81HH[.Z=U6"[2fqcXw^%a~& ƴ S5o^ ;at ޣ;o39,'$#mK1i8_/{䣛㤀 I(m902Hi( o4) ꝌQR DݝgP\lYBSz(ܒ> R?ڃY{ s@e}K,{;C˚H݃>,jF q y`L;D/bbsEGHGcaLكr1$ʼn%3> qaצmYCD$L">$}(-RwwpCfNKﳚ㖄5$VϦD5D .{S2;m7%VqkN5#uh4 DH*0αNʉ٭!s KK>>Z{;+c04RџYO"p_82 XmsLKH1m@BF,m g}NZov΂oCeuMX, I(芓!"ϭ`-D}mlI|ىdVTTlJ9b;F;,F癛U1R ,!aJd>l6CN$(vBv6n5"(-"P"D{GYeD C! ! 'nK{]%)ෆp?l0 8S䫻`qLE^Y=X0{W6>XiSuJCL108x͊gI?Ja-Ve蒰c)I Ǎb%#80 lӺYp ba±#pK@ ay=D[ !-HKwcRMEY(>Y77g8Kʼn]4!rkj5 ^ { ci#А_$)Q"BP$ağ95"5&>cbY)|j"D>oćs>MH $lZ`h A< cDٕ@(퍪*0}Џkܦ nSz娈akqs9{RZD<8W1`Ѿ0Uciscwo@ݚψiN 3 8Lq5H# T$NǭwI)'OŽɯm;&Fշ.SgqIR*"dai7'Gz,[Fc:ӣ7ẲO6ן%lS65Ƀ95ǡ 5w@&a#GNߛ@0;ĕIV)udd!:;a;i QAu2Ƃ9ptk`.9`^ ;Kp`ţjy Ʊ\0':&IJ"T?B_(@b qԐ6 !ʰ4 DpmN qkcԹ 7e`Nwn+ib?8.c(m^W4k9x^ʥ1Ii0"5#LIaԇAS$ H_fH)G;`&e"S9}-x3Rpsn ƴڔ! k(mcW^^Z{ oe0(`AAhܺRRP^G4 ۔ƓP%aU{ qkL-fgakaΈV<qY85$>0)qICF:mǨ91şG:``ZM02SʭF:%ızc@F(]16?_ sKkxC@m{6HKJ96qss(,@0Y=oL(i@dj =C:8%omy%e4([DaܰJiʦRc'vߔLɇ [l$QZlf1N惡J W=DIi( l88G)5 Z$U^Mg ŕ8d̎8$6>_$BöA$̑~K{۩86 Qgma2@..B=׮};~:nAE]H?B dPT`LX{{U -BUu9k)x#Y!!2+}-`c{%OWIÖs+ؼTރp]ڔ{yBه t6\QV߼uXR:p<~cJY#}-~;Ι,WP惃][i%!tZA͉ȱ`GY)6y/u&\Y.kZcZBiQI1,!h@nG`ԙ:)<M!oz25DZPt e2kmav8&#4ugJR`|۔a *h-cacqG+xBvegR&b0rV)(G.@&ƶ]eas+\~b؀(P&q|8Ki5.G>6x& 4,=+Y;TTݍZiH\|&Y[aA%, j^Jïq0uZ*c_|VoJb L0/p\la"LhV) M495FػmS"Y3&49P٧E!.&4(9 %!Xzmۆfi~WgX2xlcxGtb%kL0uI+sćZ"$%2=mC6A50(Sy oUAne&CO8WKܘ~p`)8lPH Uvfw!S.ά?2֗|v"Y`g)gaX#Xp$ '󆥠`u[ }0}_>q~h }&-`?9XBí,R5ʪ2x&މYׯ'۲lJDHrIB^ĪbVž=r ɝi؉8Y@Oqe$ƾ%1NA& … n(ۄ0"Drkkxρ.<(MERcXt1Zb-!hEPiwF5 ␰気bCVYO [<Vws6?y4dSP 1.<0GL!$PG-<)sDdYt>N[ly7$!N7az5T۔ bg85^9{`nѽRAOZI& AZ;J4O<0GL!hX 2is|'1@[&w2C$ևuSٿ?lP} 氋AAJ6'Sn ;1Hy?8.KHĦ FJ! o/a/=e\1L=N&RWvrP+ vs#FUgr,]i󕔨F j*lHR~)%MjBbJ;87{B>, B&ØVXCt!: ޣP-+S~7vDw}za˰EVǭrK5$L+;#&b m= ^3ullWg٤MAڛ畈K͚Zf[1M)*!v \byCV+ފĉ9vj셶&CD|V&>_JE5Q5iB9 G]=fJwW~TbG L,Eõ+~q3G4lY)n$ sۆ0v$!{i +`:)Js' )9kHMY [JiAC,ya{i qWHR$a CA=%I#8GYҔuk4d8 e2Kc5ǩ`|1cR$aU1ݘ108|^YbI܈,gύzȃvɌ S6G*y(0t~ %S0@j`h Afgb$ױЦ ymSA(}ޢa9Γ2kH\UrYC,!!¸O+IX{X܌PM,z.(je Wk&%Kq$ E0| z3ڀ~ sh7z ?. CV`<5|P NC'9k)xqWhdi{W@5Ht> 1M@@n2S@ '(F{a5`Φ/0P?_r4KUwS%QT80}(GJUڄf&d(6$ ըFS]3QqR lx]6ON3R~,.$6E *c4aKʒ)B”XWQ C"'V 6 3Ka8\W CL`PqMf aVhoQQ4X_&G2&5l|)51 8Ki5.QME3S*-ԁǨ9CM H#@\$:85O ␰H=fL͙T`S =3>qO)3\=dxX4(\&N\L̐^K{ypA#т(z#?č/$eJ@S P\{(!=G#t5(.QYJvfq*]HsLIX1mt(Q8lB6UQH=M7oeko7dJ:o6Ǎ~1ZLY_5b3bj!ۥy_'vX6I WnlnIGty'Fs !vVEOM'cDQ˪X]y4{dUFv%ġZ6>_I9"RQ&m@X[,/+j08axKn J/2E^_UrS~d"U֐P`R!qH sKkxl}ǐ5:?}P $lcv{a[*xI:RY/ƣmE-C$.*)ǩUֿ7KܘB;{[) [ F%a= V<9 I<&]ǑΞ8fip>84$fζTD t2&*0GMQKb7VzDFv)Qu@ ]UNqUNY$_C>lń)k0o8()(Q4@=>sNL!eYJ^f.S!`1ѿmB)G秃, pk=F:{ ̞Fzō_ lClb{2@~y`{h !t \"&jЙPU^5m7uF0ǭ)qTIXqINi'cmJH=(z1#0)Լkx[BR1m##JNi)6QM^^Z{CˡM6N&WT9+淵1Fi˓{>8B}36 a%a=a0~O%^+@Y M8=Y nsMq+ms\KJ@ lHK)9i QG28+77܆P0&9냅4`Lں [ZWtZA%UCsU;< ,&6Ǫ09K&aDb~Ǣ09Т,§r7w}X$M֐0nzS ^[! 8T`{a{i qHMV(Qu)8]{&׏üvZ. 6`of3roll,lR PϢاyьmqu=6B”fUqH sKkxCKh,1\x5Dќ{I[}컟Uߧ%nLퟯM9P9b( 'n V3/S]hE]f슜 iGO9 A[ J̑>S=Np)@ɨt=bhCYS ,5!š!!!!/#8,VTahg-^PrA Z5]7vlϚB˳6@0k Ç׿jt0.7wrM)G$"gцWU8Xo&?p3CS4)}Hr!9%=^RTjRص7C5VGQf.!I4$ lbPn|҆p\*ya75ߓgb~)kFwWy?5\`䈴Ͼ5$ LMNi8Ќy i~܎M̓VV> 6wE 7 $qwjHIi VPmR&{氓RZE߶;FR2F:ơHĘ85Gjӆ|J[CmH=-["gl6[Yl%v0ǍT`x џcƷ7<&,}oȜHzW - סAYS.ӄpHȉas]S'S\VL"E*T\}u8#n,MS"LmC^$8Tr0``q󠜌ۈ&3Ȣc%(- bÅH%a 4A>>Z{P,f]mPRE0"(`uLƴIqA*F1J sgɿP Q壓Pծ9`]a7ZqJi vkh6/ sVV$.:3a;1?6qśP_e+0.9_O l@C`o)QS:kT;;RF&$F'PQ+0>JC>>K5t#x011))"ӈ;M;`!?\3>>SSN| T] N69I1{4>Q|]g5fHGU^8) 8 |0\{#? VVu_'^%1❇㢀ą>MPw]I[֨/Tvw}x88?4an ~ZD=8t"8~w27:gMJx*hb,BK_{ryI(en ( dv&R1-`;8Iw~$oP}!9$7芨G]Tє_5֣QP,+ΥVj:+ uoGœ\'[g"#oJ֭k8h㩚JEypƙw0G- qp2a:ܟ:SZ?# ;iGL*vsDaqi)f_1 4Ge$/i^q0NMg|&H j~7LAJVmPC.# MQ&,ʅ Bky+sOp'rZRaK{h'FY=G `(Di[U_乑0',!Vi08n i܋A`9ReInjЊd v*0V̓p LtXүǶJ0IAB8C="V{ʬTdp!a,-[Ɇ|l$wWTN5IJCGl{YUo]4jlƒ/v␰qsv_?nKs6Sbf*VO4Ԋ(1-A)skHLeySCM ^^Jïq@G{1K|DYcxCzwb\H[CY >Va0|~"HGem+Gl9EX& s[Ґ`R sIkx?r7%rDDMjC`fS`hQ1U6%Fg+(bl3A+xQI?Rc"PɍU>2j8-A^wI0K%S58N]{GOJPALpFIz^1ߣ.u{D|GmP C.N~ޒU FaH@ 'ebrl@@6 Z{ bw7㔔N ۨzf]ɑpGtv J~Py+ o'̑^Kk*7TѶ"|T#^w0`L!aJ5Dۇ @숙CKFYr8), 4' |4NsD9T RM8 QԖi@8BrEkx?*޸ǻ"$apNl"΍fUeꊰ(hcZBZlJNih L=8Tr#;18HSXΦK6eZBfS[t٘B08x,*cݔ+bQq(\5Fl}+HXIJUDx&('a= ]M{I1[oakt6C(qݖ=^EXm&^&v7jFjd ]fO}3Ö+qZBLH1Żmc`h (@=3AaW\*`׽^X"vP`Ls} bT]9oix#/*ŗVv/v7 ĺ\!5G!q6:\s*_.W^.Xr!v#0T i`fPGȐS,pJۮpd! _iʯWm۱=x{B4+)g|$R*Un HqQMaNp^8TZo`hQ<"yT"v'Bl%q`Lml'h 6e57$気\o^j)7rFlILs9 R`|;禰B0\&nĐ>JQGQ<)֨]AHwgzstIXCI4l]wث:)+a u}`,jrC@>_PU[O?  H=eС~RP4D4㇃L戲h*I+ܜ葔* ku6= sKi5>WŎHv욄@%Q՚e)%E$mC86MEroi5, {a'KVI X=DZH`_83˛b i8&7\N̐^K{r.n$F֊bl1/T@l ƴMS*IZmCQj`[۳^,Pu69GE+vlB%CQ:7#Z-[!-`}!ʰJ)ڳk;ah Q Nubeʍ-:]AMaZ mzV`Lzr4>+M„ b`Z ޣ'sN6Bua: \ճ/\z=8nnc)LI $l 0Cz-Q<*uUtDR3A8&MC8Y/(5FH$EҀCmҋH=l')fq{ǫы5gg婕9NIH,|,RA&?_DrE^K:QU#WՁ=ru$`>!s&ejj8%! `= nnoͩ:Xp}K%F %QUĴ)[ꛧ a,䋎đ~K{G 1jlS& OvgXvV `34vO1U]Z*X`Ѝc+svg0 EVNBBE195$LA]ᔄm#VR^#8(|$Vx0 W $F gŶU=(@+98ڿ-lO[vɰ氇Ÿ7g 8Auo8`&zbə12,!ž7֐6B”~Mp18Ki5en׃  vC`eqګbI^:M֐KA8^w`v4bMm/Q4VSxt i0 u};5R @9oixg(Fi2$)pY l)2&TLQS=~bes?˿-o\?lͣ㰢JbڑaIT$ K]6վ'=0yRNaJDRD27G`rX@Q l1/8Gi5 E036"/~LFqZ&Б%(od֭!1m8{Bi Fu{b$*~<(re3+Z,r%q() $[y*MӲ8ҳ.5hޓ5F$8A J(K4kSm95̗%hC^$8rm ZtNT.y3ƒzGpjb U!)_j(6CB^$L qG itR|fE7<.Yt' 4ZH 4T6L8:S9C"}?7?D2{lBGVTI6|@PFZ;' raVmZn /̑^Jïq_%!ns |m̐^K{<MocL;Êq1;ŪxqIW'a w+ vv^oOd DU PG[ع7ϭ6;ph b$a[Bx]j7IXZ"]<- U͠dT܍)Q Vp"-b-~3EɿǰC#7uݔYIeLuf`VTK-ۂM u>a{MFj)f DS7Z8|kD6c^/KK}q#,]/9:2;F(cf{J,G3xmkwy(xV%D4^ e..uP <8 Zla *8%d[85d`= VæEkfƝ1튰rڲI9X] b~ Y|~Jƕ!G0pwUBc2Š*+νT0-pS*I+(hQ GX?G<c<3mڂu8کSiH\搔n ih?o6 sI+x w&LܰiJEd0VŤŽbQ%>nqc+T b>,$̑NK{; ^uޔ8m ksG+Yl"!h8&'J*8=6!P%:aQ~Ba-80HKX XeAv*Z:J~yWzv^C3zhN)ǯѢ[) 1cN%p nr HC60\Z{<<F̈,K@+ g]vÊ鑗AZ@ qSٚ/7"C14M1*i i0 mqKئlI qKkx01Hz'0)OJ7 ˤ,|ͻQ-! ƴDJvd=YCTx ~Ew^SqJr!&4<)!Xxci q`<E)LNIƩH95DZ*1-)We Ƹ{`l7HrZB Y Ɨa>~@;IlF%F쿑P|0| 8QLA r!a*xe{ 6ޔuv (܈X8v%1 iXLuZ~7b9ܢX`4T&>XOp$,ngJ{}+(( 9 Z=[]2YAA=GDc%tia<1Kc09;5$" k`mR^^Z{Ǖ5VC2ÔXefdط}+0n)%[ᔄLn>!(42V!QDaVW,B1U9$6t*Sq " 616I#82rʢUI{2]8x6Ǵ4FH,, MƬYQⰗޗonV(yP>( lT j|[V=qOF+Zq3)e$lÙ^^Z{YX5E7ttTQ/O)!TGךDŽЩh6UVt;a;i Q<*9jC'ꛪ)W0[Di$H$l& kH\㛔 & bldIvR ޣlBFz= {q=8nV6L]:):@}mpMj;ahQ\z*ؔ M?Fp%g.eg2LEIV( .vuh,v_K'.c1 ZDrOԚaTQ$PB)߭d#:/7L8m083,ĭݘq~J H)s,ВiC)! E995|vqWbJa:Pe-b3 GekHLD(o qG! r qY~cJ|}O79#O& {.b㒄5tSMU6K^#8XO#DV>,vV:sE'KbD6Hx #Ֆ6#}l,rce}u|S.WYۅN} ^ʳ*om2Gtៀ 6.jS:aw߶A}:ahQ07aE@"DT| Zq$1-!~6TWGP%ağ95ǡK+_Xr(m'k5fqD{ O|76<Ȣ,~ Z=-,` +&n4uhэ QU#4й}l0\Q|h0T> |Izpl)z/aF11-! ƴ=yV86'O qKkxc$za`UUȋ(^|'f@k+HL Mih -1jք\{ECo67ٔ3j3Ƈ2*qn6rΦTk?X]" c!ߝ`P-Dumql6ǐ$mJ4Ψm0y~Ue+?&\Wc΂>'Æt-ڤqB# KGiz.9{a2y^q:o%m6>kfJ|H6't a$Gz-Qpd]MAR|= Vx[ˢ(d!_7"l:ܔN˷h:J{{:: Y&!UTfFɬ}2d'ԱXGJ\)ۀ+]H#;%W%lvf ')E8 cJxB'5m#.F搗=& I23hCΓAa˨r?8sb ²ڔ efk|a=O"]0.~6PQaA5rөEiM3ܗpJ1-@ bsehՅ j<(^Q bbDb;)!8pj ƴQ$׶ANy!R~c#{8NNԸSn %њ>_I\TI'I6!r"9KH{L,b`2S&fG/{ıdqJDkZƜ$54۔ b!a{i q(SG֘?JAXb f^ޗyV*]R6"j۪"LqZqJ1oOy CE$8"ptAc]x16, 8Dҏ-C.qK:J Oї{<]M+EI,DB/pcr;4샴R_!m wPk qlv9L |ΨmI81?Mtq}c-{K>L# K0c62gDmjNx h).I#}#Qr0yɌxz剱A,Y&!k5<Ɯ]5f#. Yb7Ґ`>!@c S1}^(,(m M!i>8qŠ24$M@qlb4zѬ z4`W3Pp  8a h U2 K8%gc.,Fύ/֐)Ii4}@CI}m>HMQXQ{\[nmh$n=1~98Zi㳉rףhgjz+Y'KX9W욣Tֈ2JsbO"Y!8[+@t9j Y}\$1Uh'xLҰDƱ.J^! #KpTCj w JqL0ጢƿh!u UR]ߌ)'~7ˑ81%h+}G>*U k3dtǤ8WL8=~Dz ͑ t b>H~N p-A[*lK4nHpkDa[tحTYrj a,Z<߯xPc--Srf% +-ʭd(詛W3 igt™FbZ ㅽABgjh$B>W|R~ǯpЀ6T. 2W*)j?TH: K8 ,!{ { IN l_q%'pwq\?M|(sݓ2`"QR+~|:'|r";}F.02^ex']Y? t)؀"W0… tA22jxBWB?&n4F16WOqUJ q0xi!JE!% Kdܲ^)T6_ kf\$0A-Á+Wu`A|Fr! (]LJh~F,סǸh`p0dU6,I~~x |UQzhƴ I8!_ 9^h}h}rиTWܹc`um<=FȏmBUةi844%-GCތ<fTYhG``OE/lyd'CtѸd\v@6`g(q2Ȗ ɩ@Q`Fda") #Ĵ,h% `+8()UheJGg7氐릠&2I:шX0M(Lܱp٧|F:fȽ;G٢Սʔ֐9XR2$2H ޮo^f'g֣ŝaYIzf;%J}<,{9wj%:Vi4}|~kB겏(,q{FF`Ƌ<Elm6*%@9OKtrs X#^Qrv3xkCp M-o-FH@ܴ6lLrmuV>HVQXQ{+FM:fD"#/Tա;ix4@`.Yˁi<|pM8ј,*gU4 ƽ9:jpMlyp,  CAEa Gi upOY.$}p`.vꪍM ӸbOJX0M0jiấ!י(R"㖅:$0qu(a62=L , X$RI$}$n+ gʠt(8K8Bx/Aw0D'0b/"Uila* Dˇ\@)|d)q{p2hLBO Ù5$(m :'%cޗZ.l.:^04 a 0IPgXAsth8K(Dx-irP5-~ȝ CSfK!JҀiI=8<ɀJ:C}f 8QU+JXӇs Xls%aVEҰ}NN:yXN;صK0(peT&oD֝\XLڀizН 9wnw y9+U5Q6Ɏx4c#Q}f϶;Đ-$}-rCv0ܤ!X!c|O] u~@V ݺ8t<5$=EMyZ0M[Cf`>wV/ێ5J+\:Pr6)R[VO8H^?볆%`:-^s3DoЊ\ΎN`}4)؀i@9үDK8c%2hנt4"XnX42k*uM5uG=i%#J1JVa; %-N c8R[@?x]$5h@<|~'; Pur!KkHQgkXY`>sbVRW78ǹ<" F-铔ȓoK@t(,)8 l%<k:>ީ[ԮP5Bn%YHZL.:.4Ġߋ"D2`]f .i$V,. яi:UjPQ}|Fox|^¾HEb `(a1I8t<*ǀq4dU =ҟ$2f꿗-|D$ޘN[Gv-$qD D~0Mé|RC{ ̎N0΀1,l:5g"%$.9V3\nKv3Mҁ"R)$l,mT䠶KB-e MfDEȱ7g9#-{Ը؇P)(m*ZrP8hiÏiTb#z$4l4}ќ}Z(ҰҺֆ%2nYxc۱>4-9߭ѝћ@Vv4<5d4}@C d MA%TpTPWDތe2ǭAOs#k3E 2phNw|Dr;vBD(5(+S40"CTdLC˓2 K8J[xCŋ2,݋X0cP$6c)00"0M^S J` /~d Ȩe n9&zЊũـVh1؄ʖ.Lɱa4α璆,xC4v"%-סU0W5*_ Y/y$4l!KYx >4vVҰ"xfTm|pXl:諸aL7 I"n:]ue0Y>HQQXQ{@H\9Ydȃq4 ڈi?K@"{Ck$vRvHqR"# g ~pU E^mO ȉ@$Z@mIF@ i$% WG @U=+rAoizhTnHT*XrШ4QoR@>"񁘜ZQAdZFf ǂi@g86ebxAՊll8;9^uy~W78Z?2?7+4'L{0oΞȠeڳ3nD<>{"Entu"DҰlDLKqQXQ{̔ƴ/SD>521u; LȞnA}1кtC9Kdв^Z6.Um՛Ƕދ_ǽUxV(%I`>BÜw ĠpA:$2H ATA%gKo tnՏ,re LݤdSS4GF8\ IݳM m݆|-Zjt#l $24]n\* [.H",e*ރ8e291\:Q$GoC$D r $]I />HXAX1Ut'J*K&'{bq3z<+DҰ 7Ӥa i.a Gi ueUcor?zt! }q{kJa[*0mxڈ$o*v88j nX6ct]9ꌊݦn\ G]|X`7vltéhx8d 1X1{L26R‡eC*UQ$8CXHd)֐O2fzFa Gi uvMJs4 Ւ<n)@UV̉(jxDbH Řޫqݺ8{ lE/K#k$1D}Wˇ3-5>4ˆH8J[xCߓEe_ -N Ew==d4]|~JCd;}^M ؇RN(w_=il)arF]4όnhWvф~D) UWVP Ùs_80Oԩ[\@ސƕͻ/K kȂi@"Ne42q֐:T>tY<@_D˻ OR@U/s ǘԏfp@ $ZDvY=9byʏOǀh}ԐۂiN,dAs:jB8쭒38Gokt{<,1a I)FL% I i Ugpx݋_鱫Ҭ{¼JJTiBETDXB,z; K8J[~!!49]uZ05YI8 X 6 Z.>? vI| 5K-g@VaL&T޵uRu>X5qWVڦi>霐c.XBY?!yl<ߦ&g-3Ϣ4!}a4}@ѥ@C>DH[בn>qCVSoY&tF֐~MNcOO%-K+u &ơug9/HmӾ{|pp;is@,%doPlbE6Z4K%itXuf-Q`}t}#?9Hs8!%pYƢLy2$.kF,UkIWg'cJA/OH(B?fpb4Uc4ciQfz =&) [0]4?,\h>>H~Nȸe HZg'9DwdZCBi-HF;,*Xui% D \pOh!*|Mc[k)HxaN48C{ ~Mv<847q/6뼓kx@r#8n h,JIͥ2% Wu'99;6B [Y `:V55d|P2MNR ҡ , m 3p4`rxF; L=.H;8?>CfT\s BDŽF9[;>WgP9s"UO p&";9UCJL/g>%s`gQ9( Kdܲ^G^z^eʹ{@VibzҺb=%8X0= +se}G(,q{|fkv=/D5*)Q *V6Y2]ч}m11q? Lh(F6owa,Bnll-2XҰ=%9Iߤ9\p^d `bre$WGǏ It[A{s;d,C KrA`hUhKed-I!V P0ݺ.fZq)K8FxB[z΢#"w*F p3oO"0?'"#Ԯ=قM"LmSF{^4@skA;757+@=FGp9&hUf*gax)R5դ~w%Y049G{P A;@Xd*Tcd+ 07#vX_-IC($G 2c2r?.D LADFޫP\kd:;? b4웃rA"[6`.>?3m&4xB QB:lH|i<*F5KZ $) @utr0XҰҡQXpUTƑ hJ^KotcCbgIՄ5h!BÙ֐{K>wR?|_[CJrxn~ q/~ HDa h!i@c!fXqiQZ,סKgfYU=82b-nPy#ڂZֵzZqMci Yc߁Ap/74wP$5dtz89q/ ?.>wV/B)Cc0) 82esh%i\XC] //7 #֬/WGbXCPIMp5 DWII8 vVZ8+96e!f#?\ N !Zۭ->={~qgCl -b?]C"a!i6ƴIωKc"dJp83t٢xREwhxH%[zh$KDkPA)H\5&QKMsEDT4}T?|'[?yJdU*{bviLr;cY5)uy`.iH"0IÐտP؛8Kd %D=ѮD|N׼)RL"./^%-e7 Z[OΡ3{12:?^e+)Nc# >A98mĀ:],ѥa =APS>HX) Kdܲ^7PXAd>8?6qRcJ$Eǟ%Zm!NNMY q%-Ve$t%fNk{y_l] s`i0L.)w. tdɧ?(˿!g -}2]P3oVXEp?*(x 4t}VSEa ) U 3pRhO 9gA{ ) cIbXLpM@eC\ 5$m eXhY {8zEU|6L$4l!inej֋}|V#3$QŸu~ ?h@GVkck<=i`o@U[əs2l`l jkAp^;%\ϡĆ.Q,6$#u?%OH%.q R!{ Bi\L&S@bxh_Ť#tXrZ ;f<|qQXQ{<,Fմg}fƎCO:5$ݐd DG_B}! {O=(R Fl:fUuhh< ͗ǂi9֐Qt">SG 5F9XtޣU *d[ƒ>Im wⵛ0(XXҰҨSp^T}7ҀAs9gMYJ- [غ|<N,=Gm8,!oR#l|MX}vAS{U6hw8Vgy?,mU.U]v]0j{馦rt]5(RSZH>!N| i=Y" IV뇺@mCW8b jfAרq5/YbXCLGhSmSG=|(,(m3f(Ti[rQ|niLyib<ֿ}II~N# Y\u"_̿~鉿n{|5$ lLkY\q]WHK!X!c,w@@rX8⠍lgvmI}+ PTB1Swϙz-GAGmUD"VKV[{Sx/ШlHw!@U\vР j| W_YC@3٢_/67k"@z,h->. [ х%~:T]Ku!ᗾyW/x8lJҸb$EP[HfZr|G 7-YR9qߗ ^ ^!߇x{<$OOU9l7G?H ދP`2ywLjzE97l3FL@/gWC07gڦ,R{ n,rACj Nn2im}0/ҝ|rd i0 IdgBk9߁9YzPukFX0M'7ֿca.@p[1 QcaKq北'D"S*9$S1sK4OL]+xdw]FvUȨe Mʘ,&iWTϚmwҪ} $4l!g2l Z= K`TM7^ےL`|SNbۂi9֐ேҡ(,q{|OOjrވsi ވjt֐ q5d(ߋ<]L4Jؖ{ٟ a!+ I_MNYQ9ɭ$"}i$+ Gi uhC|/?`4EV0 ϡDﵓƎ)苔lscM} 3KӪĀq"48?ǨcL4#PXZ0MQ7p(F/,1[xC7lbrꦎ ̲DLR[۹] 6`fp.It!2C dWP`!o]./ɋA[u槂$x)L06m  Q4,1{ zʑK c{( 79cL7[K 4fN-\4۪R_UTBߴON`f?tSH!g.c2 L~$ WGRnr9(XV(D3{qn! 5c'-s ))As?PҪ,Yj6BWWjy-xLgs ,a<ۭ?vXV΃qI̤^;71W SCd]@Am?!it sl mp6!C8#FzXHȜJ&q; **`  NQ}p:ga3fb:Ҹ$PA=X?I~sK88[x/ǘe"Ic21_4ʪ/VZbM>l4\{N 4p1˅P) k6=ϜV=>:yKcv K3d4]&N\PG]ItT) d2g~˷X|87~YƵ0zIC=ڱpTtiBQXQŸuW:mVt[@}8 )$RBQ=L7n=-4l1X1{*ܮ5ue۬ {=ȋ?%GMH7tUor.dBt!Q4(RBAkлjB(7'TrשhtVKVO01!@8Bqӟ??tOiTUbN/:{PDws @ mc ؾKA'j `uo{ T2vpB{k@ΤD&6 !򃘌_W[=ʒa31Pa9 A,Y/6kD)no9~oRDcɝ $VY]20Q@ !shS{3:=81C&BZף!I".AIjGͱI(I>%2jYxɀ3_L&oA8u=7Ѡ,1;bHڃi4N"z/A(K ssZPm@5/!hIGK0j !C{ 'gb&Mm# *pcIOhӬ! l =>H~Np^Gbu͔:9% 3rvw!iauz$rc!ȇ&c!N"g <{/WNͽNofKGF+f4Y:cL@tL ]OaZޫSӄα|sU%T:XlU[bZCH*րO3Q m uks|/z=8$H ,K#_! ]T4_w~JQ 0 uɹ=cn ſ&H]" >3gs%-]5rij_Oe=iW=$Q6ղ4}@CaK',YoIeǃsp&j#6ʯIR%'hҰq͉O&zjkva@RޫV-8'G=ڻp41<$F }lM ]r1X1{4yc28&H l-'Wf"WI(Ѹ!P8튧HOPs t{ ȟ2 d2Z792#QŽ4RAя*n 4Lo*n5bY%U:J%WvF+:q Hn_HtCW]"%'PMB %(UՁ(u6"^[#a~U5KDZy0Mj.*)-|w'C%0usXLsb_"Q:^^ ̱K`Xh-x}(,q{'C MS#4Vs⟰idUNxG3P}H,i؂i9֐k]>Hc! Kdܲ^4smВm>ޢHܩGV{rJqjm!:4D 8jx>F؈tDĔ 5 ؜zD:s0bB.I_*43h(mP1X!k /*nxNbܬ>D 8xHIY0MG`iiA`i4 ^|Z9c[@%Ʃ%c|, SA:4%2nYxCo71U"8ΎJ귛yҰǓmSy<|($QŸuˈ8N5=c0K8t%ni؂yBХaQX"㖅:TtR0 ԾF廪.Ӊ*w/iX(! I/ q>H㯡($q⦅?`{4#A~'c2~tr}{ I.VI*9[42ѡЭ GmUhZ,vH`mVƜ\nךX?aN kM`4c4D- udq>p6,qL9xҢ̏. ÛY0MJ/37{FlO>Hg< W!\[>;%P#?\nL8aԇyp TmK>uD- uQu1/gjė~N*R MT',!{ |C(eLUV)Q4^9ҐDTec$=@^ K=&ҸVȠ{\XO.amupG"/RCQUL_E@i!mАѡ(RZCs9Fl<+76NX )x$cKw$4d I$7NF{h+~v㚐H) 4alxr+ՠlMLe Y0MP%'.+Q#, Ȇ|d)({;4&9rN/Z@H|?="" %j.첁{a 9q SE . emJSn yՊALjbΙ%Nm $k'[wZ6 LũȒ8ŸRq*7ь' (J5ŵHDz`%ppd9.ii%2nYx߿.YMx)n>Qe˫(!M* bMσs1.:|rn.- _epS/?tL}_/Kv~y|]I7r8#Y.;-3K8D[x/B͛os 6qM+_3M=KҰ#4)QK vA:% Wo|6:3p~7:6=83'(E[HȜٍD ʑ|(GЕNi~_~ծU q#Mlr(9ĤId>g}v%TE&B lǑ=Hs?"KR59Bև8̶-SBwZb1Gh'^ c8 )S&Gb5/SmS 1G1JcFa Gi uXJ8p>8M3>-ac0Hq%K&Ps`mE4ʂ%2jYxΙ51fg289o:?z!. iXbnB h[(ҰhRp^)s%p!%91̛U]{ޞs{zbE $Yך%PX4%-W*_uU&ok\doNmV1I.z,Ww1p@4^cؙjmMKgq!p"?p03*IXJGBӂhPZhȇOF,!ܦUTYwoPI7blnIiZʱ`>t=HAZMw{Hdܲ^f)E')H0Ԙn(N%`Ox`T@]FAAX1s *P=ȜARDeDi,x LQT7-44H' d2^`W#u@iœ(-9 e {Y7]l4]&6:lHH~N p6^dm;@gh:/tޝ5BFNi Y0 Bca(,(m,T:R--D)+W+C/*- ۔QXa??' I8Hx/BIU}p8&ҏM%eu ?Uv $ÀID̈G{؟'F2K8Bx/A.nM5# oC(nk/bI7f ,L`,;\] ,W $$Ka/&'Қ& >hI 2g*\DKf͹ _ZSE3ICbKJ5ƃF p6^:яd|a(o_ }x؀[KlkȂifYp4R.H}nI8Hx-%Xt3nt̨ tXұ9gnJ"K},$M03Zds"$("|ʌ]s 64N0軺0!p[~ $]K49{RB!Zf+v畜şGA=WCʟwg&.+ez̜a Y,c@(VᶑK]3Q`v ,3$gKpg*= b8QByKs34;m<s<[bI1'n#c y,C{BINki nmcXCSӶ`>>?bA:4%-סmbQP͜ވ4vvwĿ0 CYT7-(JQ% y5?$ěwgh{pnExlE:2+}Ƿ?$vWÄ,$]!9i<Y~_c{n8>7@zN ܄.=it-%{eB?1Pcs t)i)KS Ud.1RQdJc#9o躘|!17O4\(D}iᒆ}V&QXBAk|p,1qK7bh}4aa s(bD?>DڈQD- uXhJl2'LMSSh56WkHb,5l4}@CN<z|VMV Gi up[_1=bnԀwo@&m Y0M~ƿcK.zNp2^F&g` Gna>l2D*l9I&>%oSk'4-Y`:O?{SiQ46T—qb I>a@a280z:0 HLk{. 87Q3Tr "@V.E'/%==`z+t8j[XҰ%% ,%P 6(F<戫S4%T(H)%%RRk 4?Ϡ^ڠ ǒnn({V6m3װ$~"s#%uOwI:s=1w}pWgoF5/WlyjQr*qPHN r`:4BJi"x^.yi0'nZTo 8?DiqS@ܤnB??hkcGW/#>sR&!{) =ͥDnx^0"H.p`6Gqv!f*@Fޫ? vEsrxH}]:öKh(`>!XX5>HQXQ{WBU) ̉R MNy֐DG ڱ z{>|O(+1G'}FH m up'l3SQ3 ,}L$Rwm!cOhVtT^ωޫ~'$FcWoF)s3*Ab7=ijAqLc]~{*x٢sH9֙FJ0B6n/cSbkBҗuٸIyCt!V?Hpب(_MéI ~KY,}O؝zp8Ȧ-[bRU됎5-ѭ! E,r/|iCQ{~%sfK {J\J.`Řtt G~DE~vc4Ɖ,@}BN!/5y&f Z"jL)G\*:!X!{6tyr p r5 %^WЬ@5aHM"lUbO.D# bwK8j[xB߅е5p8!@i63p$FV[SWzXwb@(V!XNi1 + NA#l9obzrG*$L Xjh>D#a$%2nYx#.2f. pQEA.'- R"+P* IӇ.깔O LJha\6,=5>/'m^qkI '#Qv,$}d"9CЁ'p| G!RgF}2 G4/f-1! gXCOw>|(,(mmy-,IJ&`y 8x( 8Ķ, \'38:- 7%FkY;JEs:kD]׬ɳ%5d4}9UA֠DF) ֡d{mZ[rq|t\DcXHv{M@,Ь ГSa ܸ xYAޫ`"xNJNsi1*ʅLyZkPBO䴐4}|~[ > QXQ{ͩ,r9昱/Lt9VN9H ` YDp`l!,'OK_*YI)l|>ホHkEsDj+i=eZyܰiMH.y*}! BF{497wQcZ HhS_b-CpY@ }n.:#q{2%ZMS[L4Ie)0YDWS'Y̘ϐBJ8hu;A7oE:،(H*woK[V7]St86I2p١$2jYx_yqxgF*𔢸S(zM4Q VKd3Aqz89sh`8^4zCofM碭o.{?K$I6  OFԿ_NIqSp^lgQo7y-ש- wHh I8J[xC奍#qɶY^*N] B :M~xC`pGd}t!6޸L:JH~~Np6^ ޶hQ(1R575»nθ\!Q0&8-$݌ԙr;! BoqLk^SFb4N֐Dmu# 8&Sv@ A,wϗI8D[x/0ݒ Ѵ_ N:qi֐Dw,@] , e M`ű`ubĹ/vѣSnsJK`.Y5*/*.4r ,Vq=Puǜh^YkQq'}=Dϑb+jBAGNȸemQW}AT=_Mo{$b~X]KdGg: =CDS<Z>?3q'w,Lit`Fa Gi uj#rDZ!'X!̊;DS|ZA(Rr s.B$A"vL+3qeKUr&Q`G*Dt]e<n҂ᖪVXҰ,ׁ|9Et8OfBN~A<ٕ 2?oiŤimqn{ԫ]:i`ȹ%-ϧ3F yN$b@[H>$6eaL~C4%2nYxd #&Hi9 /֢2@o RDlχt+/slS gS&  W+MnS&f>qEwFspK VO% [0<(Uq2PR.H' KdԲZ! 4FQTpUmzoBJt̽<L9}!ݨ>"2jx?S&N"#D@7WNI?Gr{h']]`Zw}ҟ% ~FE UBE}pi0_˱"$>D [\t8>[v CgwܷDRl69`J_;3| #R2\qHI ?( އ1Y~[@Ύn\m8IH&]m, ~V{;x 𿾏x&@wr8Ct(RXZ/1.2%6 Q5ӰcI>Q52 K8J[xC@WI"1lMF`tq9qodY)ӥk$2JYQBt ?@-5^~ŶHD/xXHČf2Pэp\OVAXAk[sjU};^~Ews-HLf G؀6IEǽajՋKZ8\+` #g㔄5ӆH)(vL be+@4ǡS=8mG'Rx/ړ㒄5GO%agw^#8T5[T&:xL``urQwfSYBeujC9;HymAmh}I"W$"'FOŞ|rĂL 7LxZC b$ sKkx9ȎMAXG3rofT K{M .N2e vj8F&Â|vA VPDSmZCg.9fqJeL8eBe8%`t ޣЊ"I@DۃqclbK') kH\`iBI N 10(Yzmɤ.nѱ HygPVE aH i, V$` g`Z ޣz[50V-)\v~46ݭXHj i0 HR,! Yj/a/=_1XV֤;;$1C"Ym/ G`Laojh61気+^+%z?ck~ ]-1*& )Q? Ѣ-tAAY ^ l#4)vY#x]3QLertIXCUSDM ;$a'5 a3,CBޓUjFu셱.BVS0M`'B=@'*-1-bG]kܴ"IqE̊ߕ r*_0t$6Mg EQ%`D8N8%9QP̆g{ř0G)\JE6A0(8dK$eG,R`gMy-5he`D)xԯpb y255bb;&e)تCuhKq&ybxc wj0 uCT]:hbߞ0(#\P8L9#buڧ1obqu>@P `ʆwi!ԔסW5=0G, !.Χ F&6u P旱 ! k0 8k 7Rn{,0G- q5TńAuNvZ"Tdc?wTPm9 f??9vIPt1eCTЯdB:G( ?FYr$s$:PNq*3ZLmo-S%xu'ı3sgn;y :@Qk,jGb?c qK4q PVpt Ȃ0!9k)x[6lJ MyNɋld Lp:1Lqc'ʁ=ک+޲#X#2JxESRyābij9x~'y5e۴!+) sKkx㷟cI ug[8p'Gq8}J i¢D ld]0]ey `ySR ٣J8JJcpAUbDuΟ5 o&!5"АK>m4gH=eUFX ʅLlRڻ΅#EpդtKHCGpA~-unE'bǛ%:( 8ov ;9PI<c*.vX 㮐 c/ʣ Op!F [)XDS"9=B&As)h R;jz aЉH5}{l1W7 t8qQ H xnVcĊI 氋uztLi^28ϝ.0YZ9Wc64RCM ƶ0G- qՎ6:;'bz|Ek;-f ǹp\]`sp[r&DqHB i'$DT,Ʊ  ;a,!O_;+,-0sW]G u-o6)@}jxI) 0=m#H気Dz(AV+QbUvCl&pߚR"yj()UQ qT_1nOJJ cg֢>D4x>ꑕlT;ӆlݐcZμ(ǩH*8lQ搋 A9 Ү ΥV6%L|X i,IPAT:iAa%6sg+^)&]%Ue>ӑ;%a }Ꚕb i:bL/a/=?ֿ Cc';">,V6_E ". k0 Er(lk%aĚCa XP O^(y-G΋#T'TE)DFa0?$La#$!fH=bwU8E(1X+Ozb,.g(id bl-1߫3v67^(l#ƍw} )RC/ &_Si4A ' >Z{ &D8DkAt4|쾡:B45$p%)* F008z{EJEAQu,q 5xkhO/4wblsEkxuo:#nUI.EazKދyإ1UߟIh'^6~Y}Y5vDWϕCYCd,4ws4!o1ǔ@ѠG5 jqق";+8sz`J@s|A`g hg]4 FNQFQtC,&kI=i:']0]e am͘vMJuU&+x.  lVI ٚAŒXTB;61O LdiF[LF;'+"lf]0]{ @հ=))VB3I25$Ls>q sTEntaR {, }$a ƴb8n6eX[ݐ0G- q HdȤ08ݱC_tM(~}^tfbě \öl.O/~ؙ3Yr0hǙڃ/Ka0T%GKqU8pzee]PZXj0>`R8J JN5k qL5;{Cu@5.01?f]3x$n]gԐ& `C|RAI5H'̑^S{^v7)Fd}4Vanӵ/ [N"l,Q?Z{̺>v8Jlu>*NXYxU#jהE1mܟI9#!D8StS37)QU}^ӺЄ*52Lu:xB8oixC6}5ʉh:B]#:'Kv*i+TqQ[=16k$`osgt=)pFwiVGޔ5 0ǰ4ӆqAyt)5DcA 6K^Z{Yڱ@Z D4~}LwȾaLC"9T)5U=eCJRBͧѽH AwEBބ"Hs_(-cQ7%1;( aLBTŭ*&e970(~M??Š'J*xh7E菋 ;TW1H4WrȰXę7 8o`h Srgo^(Q(R~'r*L>>-ilLksTY"!lqA+x'6񛔃}:W]NKcM i$+xak4@`h A,kݘ 8ʾAEci5ӆ|%p`+ ^^Z{_WɈB4l ]{ l̑NK{JTGB)6Ǎ=.$$Y=5ӆ.$)& eX G- qhqϪm9y3R83ZLfc0%Sb-F#IńP[[ԯҜp.wKGvv C ba⺞Y!ᙧƯ BއvKj *0(we4!K.REQFQTvS*e6L6jC劕AmS1MƦٿR.[ F̑^K{Y,ʥ,ʱMQNW Q27s.XK &Ϥ٨5 3}0}(Al] Hn|(- ҉wKz_j{4@-`=%F)Д1uGP1KwNu`L*H m1Boj1>Ϫ  Pwe:ه%1@g 5GD[JCٕ+ew:+ziso8j3WWvC$}(0IX)җ4I3e`6B2&Z=13xN!>g 뤆>˴!|ҋ䰗ve]!DNyB{<8y`堣- ['kTfu/Sgs%7!ů>--H`ZSzCT)y7Gz, ~hPg׶M/񤗄9i0 H8$l^h#88(3Ye;އC QG0+LƧS&e"54DsRT`Z ޣoi܅`n>3q5gv@.8Ĉ8b%(Lܖ'UsCkx KŨIֲ1ڈBB[W 4/Y攄9h`ܲMR,! jƑl9oixghn39X((rЬ{GhĨ+s*fw'pJ6N)v ޣCkRcb"ShdZ7쎅!OEA ԦtNlo9_CcFkRM хPTG G9T7^iCuAP55ٽmGz) ?Ƒ>n d! -!aSCݞr"mjh ^^Z{T%}c$BA@#j Z4bfx KzO)#)91*lTI.Z{ƼkaP gb!8Ԑ8wrZB6v0Xʿa4pYu*rK%3P%z]$[%􍝢l  ޣ}uyTWl^)蛂qLb5~2B(o72T/oo4e4ljf }NjqOD-c(b= :g] ߬vPQX-5p}6 S"+M>R.w\\~}##DJ}j0.@LJ_S. ֕気COb;RMӬƽ=՞4!JOSK#v_! C}‰pȦmߟ95DZaBy_Vu=bL"KO@pB6tbr3aƛ,4kx}\Z\Δ@SQNՍ/:9%AX67 ʰ.Pqbw"95DZpP:~0 (--Td(lc|Z4ӆf4ܧL sKkxF߸Y7& /& n>޺ا>9 /`LgRRamlJV^^Z{jK0q5OK` Zc߯NsEFqXBeLʰΡ; 6CXBX}9ɔ"p7N-@ǝw,$6"POF=d/̑~K{~) zx֮\ ._g~Յ#ߝcPlxV3#!ai Vpxo 8~(l]{9 Ow`t<,r7bkڅu>HwJVnF>&*/gkc<]ٳ89X!:d$YjI@&q- sIkxb+}v8֔sgiX<~4dFT9$/؊pt}?v;VZ{2']*:&̦= ٶGb<])_A&035aR 6McfJ'#(T #8P Dۆ/y">?(H\T[aj*~@l~n$>?S"t:r~|}_sgDLֳ=3o$vٓ)3v פD xLII&y3aĹs.> /Font << /R314 428 0 R >> >> /Subtype /Form /Type /XObject /Length 64497 >> stream x\}I,ռVqWPw+@b3`)HvwciWtXߟSq_lo)Ju?~︎↓ϟ{Vzjc0D_їDh0 H2-! sİ ␰気:[ۦ-~t_gϒD5$%$濭aʦm {a{i q`lY;h󊱙Ҏu _}>ϥcZqYBe\,RC$!!qZ^okxckw1M%/RmWee$p-FHRlcI0^Ounuxbj(o=wYv϶o8'٧ɽg`L)%.c]oՅg _#aW, $DH+ICܐ4 3G+3 ]--z?I7k\х |`?&,?@ LIHzq~3 l/q{~xKCp}Dcu!nL!`ʠG 氇RuV\C-G}nRNf}nz_1.56 AJ\`JPC\`܈!"9ejxl]rx%뗊G_KS#cwp i"Te5}'+&{zL?#Xjߔ; 兯W>=8$!1l)뻻5w| ␰気;>mWL0J7^jV0;#a)K>8$6B")kEk8lS6"9oixwc=1m' ㌇&>, XK6ǐ5$p}u.$/|qHsEkxCZr+uۡ>4S pڦl?0G- q1+7W̠Liw]*8s9si" i0 Hb j z҆'0zK6񘙲zGĂ xh,!xC1m/ʔ5p6( sKkxc[98;+fʚ?GX֋zaiAvg%昖cڀ(k)X&8Gʺ kv;2).XH\17qU,=1>,A̕5$ Hҕ⾂MƂ^$e j3N皚ԎI)9z xθl\ ǧ/CD3`ݧm<,}0}(ɚ)d`;G6%q\SXo8,6 a%i  {a[U&~7#IYŤv{ᾹEmq|XwpjHLI ;a;IF<\'MY5c[-m'Jo>*[1M@@n *Xym89GoeRe>&\q_+}b awbLNL쒣Kf.,JCm6CB^$ YS8)_kVZ|'Gl=İW(Uְ|#1!дm3i(kJvKرMU-GRIc}}d ƴ/<)) /0>tZA``}.MU{65$^fƲ89f֐6B”na8$95cƽnug\}xrtL ^!1 q۰])B]\D ()^şh5 c8L X7ƵBPbSjK/꺍~H A 9 `QΝE&3&r0]T`L!<@!fҳ A,6p.żGƦ~[E`/Dz1iĘɲ9ߎsBqVSǗwS^W|j^7Co-rJ@v c2dN5u>ʚ\kU#GQ8?xba)%:[AA[ Q\0G:- AyMCjIY3.[XX8aZ@ i5ٶ B̚- hQ蘨wb/]Nj{}gB<Vu):+͗4Ѵ;}0}{Z~p.vK cc`sĕn4__ N ĩ95F K3usވGIףnk9IcĴI)H 6e869oj3>R0$;c%WcSP"9b[o $D,᳾;iC89Ki3׷k#IY<&ر{f 0zzUǰD?^tVpPd~y2!hpuQ*W c"T}Z}'s quS뤆_e¸p"G]8AL&5⻎iYX֤8/W2GDjH|` UNMtۄpHȉ䰓_ez)W!j0v˅|ʈ񪓄9 ,H =Hʉ aH6/a/= bG#)c޵q›zi/B5yvlraـ(˰P.IȆpHtK+m qplq )o{ .~3 :į 7  X>j1%̑>S{ׯp՜"F4Z26M{O0- 4KRJLY>v]he~qE˿qݝK !:%Τ }f5ΰci'ZDņ/#%eJ&`B4&!kE}rNBLA:?cjf I<" SoVYkq@94_quK9bqb{}Ksx%/S`L!԰lm0G- qpv+}R:`^ÿ*oc$a1m J 딆L!a/a/='D'f j\[G1$wa리46iC8lȋ䰗ckВ0j_#XHm_|nk^q>֜!V` a?bM6@׎\0]x;F~09^/3fPBOj }0G;J1,=!A؟t ␰Rōc(ݔ_ģ !.ˉAb QCPbqmto/̑~K{zUuļ)7`)gl8hrT 2a<`1]{b! lXO'%eׇf /mj,#:L[aQ|_ \o氏Gƨt̽ı)7_͊@`(q]M֐*#)mS6 sKkxvp',&-8KJ|cxep<1=1$D9pmI!t ޣ1Z2۔Ɉt5l9LkH|(O,)#N. ZNNJ{<ֈ| !)1T/{o!aNoЛ-5ަ54۔ ␰⡉ lPpw/[ܘ J=LIA-]0] S|nJdyZcuI미EaJ>MשWsL HPM@ex؀0v $ l!0>y!Tl˯ 0Ǻ]p` t):a=@AP[{"yL\`xM$S+D@Kc|h0 H! V*08xx`lB--kWx+'G㫻˃cZBnJ54IFض気Gh <I`*c"= w&̿l0CPb21Kɾ9#`фE Y4!) A6}kOJȗj^8T`XmhevH=JJ .Z{JVpplSΈw' s HCC;kIpv ~к>@2edkӭJ. s[#i0 Hr/n|q sT;m#p2?)8jnjff z!#O',oX4NB?o5A;CQi9hg(pd̎j0 Z{^ ^{ 7d x.9nIXCbelJ[C\?Lr_CKFRƥKd&2[uf컰"|p4R3@DTDڄ0LDrT>˝}R0-[_IY- c$g ]֐{IYyI Fza{i qp&kQyl;)Lqc K0ثOLTHTpW6eActv "Y[䈰2M=C5.NbcM oʉ̭aئlkkJ@^Rq(>`.`ŦIֆzލSbhj/9jsk6 !J10Jǹw͂F<8p<8\V`H!`JJ) x Q0\xYXAa}l "a٘#bܮ蒰~S"5DA8I^Z{-N@L`6S[ɫ'N:3> agGQ@ Ɖr[LG.tY!pf^U'L7er!"0a*#q &%c"+ykH|phS.KzsF*H{i q@:0azp>}2C8O c 7r3bDkXi Y?9,7%}SZGW5N8 sy9 R`\,Ta0~~Z>oaL5"^<@ij⍔Ǻj{h00aJ d7i9a;i Q(sv{N|֥.V9F`9%&p[%a ,l F%!'5^&y<I$<4nMtbE/r~2z?EA[ό\~bE.qTgbpn7Dz!ɉNYꃣ҂@(L b&C</)GMU_[~~*tqIHCb٥CA$ ql8xEkW&$e]H>5Y2 樓/fk0 HְΒ6q&/̑~K{:MK3K3)QI#r<7% y6Z7‡V/oPQmئ}3ǔX8MyQ…ӳr͈\@a<bh,Q sr"qsko&rfV#5>(H{r|"!b^cZBi0zgk8w>~s|-ů(jmŚXQPG (d&Ƃ+f>I:XI؉dV E*璉!-F|NB]ı\?ܔǬۓa SbG u"e!t"GkeoDuHT-1Fd%faـ(Q_*DȄpȉROXSzXQ5%ĹcL5cj %8$k1<OH@! ␰搓VDH?wtH?Μ׺|8G # ]Tvaュ6eX4ĴoC.Z5~HKwcR-EJ=[CTp i2Gm&H᥊fUN@ 9a55ׅ=u,)kn9 9CKxgCLM]v>}2$a qG&uO3G)3 %6"f+STfIUcϚ@Sܖ6σ?$`sȒ,lvSnN85;Lrc&%5㖄5$*ݥK6C^^Z{iT9/:ja$ 5"զY<p/X" 1 @@!3{q {`{(!pg bSQ.5TFGE(hraܑ/;c!1m@XuʝDyFGLc!/8Tѹ"JMCd1z$6 a% 69oixPQrklI'0ja&u 19€!k0.J~P*ޥ6e88 s5X5Zr&%Vs0/%1Th8'T°!rź>4~msTv^ydE罗/po µI,{bOJFo ƴ S P%a8ДH=._Nvىo21ID~0փo8 5bc EA^^Z{ʕ8ާr%L8D]7@J_'-p)>[A-c*a=TTy+ $)Q~U13w3R.aLk =d=MzQG"s}=L)Xl/nb$",Ova5~r -nh4vma(.9i89<:!p+kd͡Z!qQUR:JTm 6eX6H^Z{Jlі|Wb0n" skH\y8jRP\L&J0氓7>]–^eTR|1cʿ):κ:&e"SAvg-`3Rtpn ƴٔ! k(mc+V^^Z{ oE+AAXٺ eRP[ܴGԔ D۔P%aUn qg<ȼfa#0Ȉꌿ<q185$>l);ICEmǨ91ş+-g0s-&[͘rliA=| #-X/lZ1m 3G. k0nğ95ǡR.c R#\ǜ  Nj~^Ɨ4+{AuVC*;^`U%q*CjᇭrC)+\nJ]of8sR߈Ƀ [li#Zl4IN惡J W=,Ii( l88G)3 FqGj3Gq~)nehN#& k0σ" khm' sb0}͌`&7_xM_H37- kH|0؄Hy0Ĺl9djxB5;ۂM9qT#O$,8%6&J$mFשPzġ8~NIlG2O30?& JEh*@# >Z{z( % {aB́lJA<0CT iϦ)HibA k*3,TaLg낋[[[^9 ć3L9ۿ->lP??sEG;vE߱}P:VظM^V#uAUS1M`hU)( UrH= Lr-)yl},n ̛}7Jn #-=XK%W[y1l~]ܓڔ{yJݯ;qG$+Yo^[\*)`o 8?dխrIH_˿s&?jNH9jKi|q!6n `ZV0(` o{,xz!PM'w !=0y``| փb i%}={8⇯ܴS:KuUŵ1-! 4 QX~k$a[k\}n#''qEnƇDǸRdh: )uw^Jbę˲95<pFz4>Æ<yQIUY7~ܙRfі]|qÕ\H,p&qUǞ#,!dNDPT6RX0%6 !i ia48|j@dAbˑ?q.nHdL6G,`C.IQPol aFӋ䐗=Y 3Hu51@]?n HkS%`Yֶ@a= mq RIaˍ[e#Pjiͱ)aTKHC,f%H/8TTRE;7UDg⏊Kߛ#4* r [yk8lS6dqKi3[]X!rJMW!)eF7rR1L *;#0(Zx[_nmJk)*˴3l&F6G5$KPCT7mc֬ ~Sßq:b+֤LDuV;c(Yw`$㔄5$>T?)o+ bS9G])ݧ&IfXFG Td{5čiNQe\`tQ cʅPʞn-{ZB<gw SNIXngq {a{i qp+OVJ;Nߝ6qPWw'KJNi(ձmfV}PA222vu]3!VR]֐:'awlkXߎy=lc/O気"SMFg妜.fa0_Wa2C39V& JCm8bQ 㣩)|hvLA-T?9X;AOx&ކY}/O>nJ!ZJ+ cxsfʇa S.IHMbUj1k+xB[Sc[;$ĭѰ#prЁ>-bH%1NA& … nڄ0Drkkx*(MѓRgsW?Zb-!(/_iwF5 ␰気/Zl޲>Iax5 P†=/UYܘbJ0o EH== 9IA< "Β'Iqbʻb$a uz) ڦl+M~a/=.Т\e<{$vf}Uԛe<gq~o,q,!s7y` Cаt,ӿ)b~Sԁvsz9dC)HD)ؠ J C14e^^Z{4bvD$Lr6 mUm Gܡ¶8Yj#5')QU&ls.S=?J5Ôvp0xoӊx}X@ cR1M(M]1},snGRxW|7vDG}zaKE VǭrKy5$L+;#& m= mO^:Klʍ4<7DZBoz2=X i"$LV) -l-5/LxwV4%N۵Uc5!"⸷4yP*!GчdH= 1Cs}RP.2MFb՚EҰgI4`O|l؍85wݔM%u??y`L5$V~怜ǃ6M a08qݩ9cR"IZGBFa9@whʦoKܘ!JFD I1<,!h ARj! (5&:yZ[={bc 0_+8qxM0(9\I@-|IP&YdMS֐X9I( lP995F֨IUӚUψcVU#j ]5)'[Ðmd׵TDc=)wwwM7m@7hTC >S1M|~VO>?N%j@tƽSaIS]Au2.Q~ptIX= " k sQF}DMQFۉG)UcJ qj^j Ɨ%eJ&ZmӍ sKkx{E[.jJʍ{vܨ|^ׅ̾ csD}Cٚ0P1MK 氋 0/X"gS`RnQ'b%abW1'P 7HȆ0nSJX7U>h&ZkW#fssv$R>5DV &X򁿧3y/B~0Kʸ+EÐOMiA`Z ޣYᢎe$ @'5!t> 1M@@nS@P '(Fua8g˦:,iH0Pj΄?Zr4HKUuS%xQT^60}(*e r1`<'`jTDAqn("D8)`yq6L.~}e)x!_Pl"z_HQ+bJ; ure'>47%h 5N8ʍ'5UEe<rԃF~yC!n|P%)SlXzpC)x!K0k1)QDn*F̴U"!昒cP'P*olD3}đ~K{w(:ݔ+ʝ>=;Nn9ՑkM_ukn7*. BB~vCP#@LCE$zgŤ([KsOtl<9ܒO0C:,(= q>f䛢Ce^8,KC R1m|~rDţJۀ,K*j08xn ,/DY_lSBydX"jU֐P ]R!vqH sKkxqQwp{52qmSG6s`}*! k0>u0b*5 I14Ǒ-507Z4FsЊs{T3?ZbUK/b*H\TFpSSa vg Q _P_ʌdPvpl<7%C6>?R.XCm^^Z{WΞo'b #zG\z /k &鍋o'p[AAmX`= qUyդtXA-}0}(^f&ٔ't'k"ם8V0?8ַ[Tj?)R[AEYl{ D: 3u2^'- j#nJ#2k6);ahQhsiqy0v׶Z 1R@>13'"vvރPmx;~6(E0J#!1@y K4$ HXm ᡼Gz) to)Z#-x}G`9[P5ƴ\JYxZ8عVߎ+VͻtDM "*6b͋{Yf0sW=>,nLXPbA: ab/#P`DSQx67}8*,"m&˸URl@”+kieC0G- q=w؍vSSRV#:}3Iþ[jIMxRðM FMya{i q:-ΒIo(086^!\W=ߛ%nL!`ʉ-#Ⰻ91a~RNhjW,m#P'ADǟ>s: ƴ!4֤4h b ƴ-R5v~O%kE\4O@D"I KRVrIwحK{(!,}R|dsݔ93G5$.ݔpH6eo/a/=FBĬ0n_5D֨g.G5߂X?Lݭy%+,u>RADAUng̜ҞI6(/j9gc SuJC!a/a/=58"|2k6 nM +v{~x ꊃ 3S?\f iG1 AY Ȋ>S=2ߪ 3ݔrHCỦ*|pi j`ᶐ ᐐɑ~K{- +Yy{\P/@ ~8{ōۛPQ #W5̖͝PS ȍEtUj$=o8,ΔKvG߱l15\Hi ix7ev i͐ܨZQWKC(*x# ixSʍNK%/̱?bwomʚx'jؽFl%JطĴi)) n1m~SßqpQ:sR*ʽ@ݮhLא9NeN ]$>) MDutvR ޣԩv bI` f;IPQ#Eq1mhv54Ig{a[`(HUZw}l~_ s܈OnxoBj@C?>hB)L2Qetqp=")0Jp!7MH{m Qp4J52%keXxbO=3C|PƲٔK:!B16??ErokxC#dB=(-h1 +0.`. kXg ޣ{?uϚ2"toCI%HVkˆ<Խ-B$ۨ ] (A#a%(UL K'%q;Xs.}cu2X?㔄5$LAX5mc[H=mZS3aˤ19msz?ڃcV'%v#דB[(JWO{:580#ǮF/_tBQIb$R dz+0~JC>>K56>}RbcE cw,+c)"W;fׁCYMrStR'&t ās%q9eR:SA-c&]4Ů)NP" E4% a2+kHӸoNhVռNK@!.ߍ\TϔJuzQP,d*Vj:j) CtoGU+MupA#rD;&+&*cL& k0TS6μ+9oix[tb>!ߥH}g☷U# H;M1_*ـpHȅH=.X3T-*vj=㳷ÄX e e1 `f Rmn@v40XTEQ+ʅ~kxqh{Op'rZR.K{h'FY= ¸0ݔXЭcisFu4wsKJ4D@{  {a{i q0T]IAk^m׈0D ˻&ݳ@!$vS:U^7'&ճ@!c*xBqD-("kla6qʋPZJ@ 9SqQ$g`yhC_X ۾O "ic~Rx”19d=)HfqI)o&Y\:ah Qp.9Y$  `jLtgqS3vT'cSXO`*@Lɶ@?Ò~ @۾*ԴDqi}E(1|YE$+<8X^!;VnI[+PR$u) ʨ#6MY*.F S5LBIX;qHظ9]ix<߃y)+|3XǙ Gy G5$ H<޳Ȧmd/a/8xp*ޗS%[3߉q !9 n vNE( v C9:FŬiX9Bf8Vi,).I ٓMpNNZk*,QCQP7o"%a!X)e4l^iiW8I16tL =nJnNC7z)Gp3Z.eu&-,ND:]v!Lʚ~j1gEךns=j 1 @@.wla=V*)ю: n*Q [}Ԟ 'ebb`@@0 Z{ *nv3G)˛0"dnռ;g!|pD`uy!pv4F`WYSMHX>}z m8;1W,! 44IXCT-}ĎトWA|27ĶUp[@ Jm uuġ2k3G+xBCȒEd1=SFck~dݳ!Dț58Qtk0 nkfњ sKkxC:^qSn  Ps>RMaa"`/ML5W>!}?r+ 'A50 >pbRSbf蒰U2$a !695JGMٙĔH?Y SOۺ[X;ɩm039΃aGR H M,3??sJ2`O)%qT91Ɍ `^9 %%J 7'C$JjcStۨNH/a/8aBG8[Dٳ(lvAqpaL Ʋٔ"!"94G=?Uք=rTճcL%YR}H ]OsH=ny FqIsH:QY [q+a(76qKצt$m MAI^Z{h1첻.)HCZ|qYpT #8N=CC-#PlS6 sKkxC.nڵ9[h9Ը(7𮪃{0 #KI }(!A W-LdpJB&.`= 8nn|oͩ43p EK۫%F;%QU~Ĵ)[jS a,Zđ~K{\t@1jS&o OvXqvf3's-hL`%u8bXkʸ95|j0`, GoR.h,N.l~0=0ַcڀ) 5 b s_b7 C@zpWlM\ #e{`{h !0$s) X;'Pƈ|8 {XCb SNNN6IHH/86\&(>(})pko%k}G:xp4IXC#k}R,eP+9"#PxPף-7!*[X?L"%6-pئ 7dH="h1"LܪٍҔ- ΟxHKS=ENyq(!,?Ϸne7EQXX^%VH^0Ǥ@}%"ejZ{|^!)kJ0%"pΠү*|tG !-)*`6A9!QB{݋0SqZv:t$vm??ٟukHLžtZCeٴQ]608R{|(BX3ZLss#RE() $[yӲ8ҳ.55F $8@ J!K4gSY65Ĭ%hC^$8T7PGAҦࠪEK7 ?xǭ6֐XU~iC8$Erv8H.Om3WE*p`8΢C2iHDj2$i`ѓs8qY} iY}SnL?*zgϭ2WpԪX2iH\]ݔ +hvwyaRCyT:j%EAQqRM(n9$CA4čunk)x{p硽uID7 fvXe-9.IXCʜ$ n]6ANNZkZLd DvePGC76;p b$a[Bx*IX9z<- UI͠T)QIVp"-b,~3EɿhJK7evR!zuF~غC#ħ[o%Z9kX} B?R؉,47_lpr`Έ=^ẍ́0f+H@..Z{;W[r"Oy1=wV<{G`;n *o ]AYxa[`y$dM) vA!e~~ōi1¤DT}HHF0=HTf> O(2rtd(D I%l.YXD]eq) aK !fP>J*M'.IKp,-'.^ <` Gla *8a[8a`= ·d`13){Zѯk\aLSM1@RVBB???Tu/̉{E&5yʃs mJg$2~~pusApޙXW$kӡh!qձR%aλm['/!'50o@S*kA1-Ϳv>W )vӀ^iV;a$atZރP(έCr7%[:X Ja7#ͭ I9 {cM#N#}?бzu#L(:ŷ/Yt2{7ą"%cňe@$]Θ<4h;ԄюKP,7,oxhkB4) 7fVOFP,"2PťJBZdq]ahz@FB\̔m&t˾9␜<6Q{ļ<8%6mnʥbA%a/=n@>]IȐ9RXJcKOmݮ nj i00%R#z`.J zNlʅsp'jXaH%q^&ZȚ0=ok8%aq気*Ut*)We_0ؔ{`,7HrZBT ƛ~>~@;7l{FO%#TD6B *C{[OX% 9ۿTC@.$Q.prRNX8S -I7S^0`L09D6AWya[&bs156%k8螈Nce(]S&sDrkh0 ֐R\" EA{ya[a aUjs!CLk)Sm4}Gm펂rHP SZcP3&,׆|Z@ 'ũ&-J}k4m[@?AKg =q >TL %[t}0Cz-Qhd.[|ɑŒVYXE%[ee c8 s IX1mT)ʥAQmcl0G- q('v\Qu[G27Ꝟ F n1-! ƴ2 Jd31kVʵ85SwaE{cF8&2M֐h4)1LXI89G[~ĒB6j͊[q=8nVK]88):@mp`;ahQA++;6BZbS8)VIKY!YR?I QRx6iCXz5_j0->(,6> 铉ފ$a F~H=-jU>,HvVGKb Ex і6#}l,r[5X%;oʅ>*8{lK9Z]Ŏ7戮!.l\8)tZ@mt ޣPt8XMYXM4Z5#F %ω5ӆb $l08WdqAizC l~K‚¸r1F!h X;mdžO8χKēC`>x NNZkz6芃FRoql`V3DEKw$1Y<5$[!  {a{i q-bRNUG<-}O{IJW-{cv{x\NG 6RqKRUXVl%=v! ж1 QwiM$ U"^صWžkq1CL$YPaCm~ҋVZN;/uOŴaCLvn _KA1(`tY CPUcͅp;YBx3Jjc8m7{a{i qpoaB? yܨlpr-7#DC1L c8M밦Z^^w>x|Ȓ X)-a*昖c^wFʢb̰H름85tC?ě;" WwfU {mi) /SAEY FRP}bshRE EeX͍u\׃#T֔{J+aA2Rpz8M 3QBDQc@>E1$! ;M꿭!|y l'-pT׻ ~XJA}dEo0Qd'84FR]sz+zب,Zv3L`S&CՍ{AQ9֘[ƇwL+:!Ieba/8k3bͶ=bW 8HcX2iuD(?* 6\za{i q%ơ`17%(_O%Qa/|ˍoEi5Zǃ!a)ZiA8CrRGQؑ) $S׳`BO*{OSM|9X`x! D%ʌ(h2cUOXC:RJH*e !ScR~٥Iaz`|Mc)P~0x*R>4)I nmˆ9ejx#@=8OJ[ts[ kϦ\ŷ(+r_ۆ'Hm q#֐S.PNNxGT¢tK `J192_EHjXC&[qMOEJ#lC1m ^B& khմm#JQ^#?P=S'+Ii2)7MchM2T`H DA%ڎ 9%= Bx `)S&fɃA6SıcqJjZy\"54۔ bD!a{i q(TQGhzkH,u`}r/˼u+^T [)Q;mH-8-qx ~JI0YH{AA#DɅgc-O$ؒ#Tܥ_8Arekxk`FBRn<ŅQCŮm֔Hf1U]k~qs9kx\:l6*dJ 0GA֐X1r !¨@/^Z{V{E)7cU`c+- aq6/zS%DWmXlP^)~(r~+9ReZ "-[Z.^TOW#=rjںIK$q(q<7qNv¬zSHwS%TOSJCQA `ǀiP ě* 4T8FxBG}H>91#37kN ~D-$]4Bqߘ` Ҡ8HXx@RHPYGyp"e2%ir$j&&/uݘ VfaS'eItԵN8cin)K8F[x7|Q_Xuy85sqK&zn|Lආ$nNI Lb,yN>H#KQXQ{\[nnmh\m=Љ|k6Zi㳉RϣhbfǏz+Y'KN9W?Sֈ>sbJlY!8K+@t9j Y>}\$1U}}dSshM]w ag&2HiX"NdXHzn %}%b8*M ֡5̣n%sBQt #$uy=G#st`Zfw9'fmc?ĘxcnVF?'^X}9ҐωY%hKSڿ oNMKIu*9&e SJ.A2 8FxB9 pIw0KLrd#U$9{kH"#a4}@CkKA%Ȣų`y=jftk=:j*C[CDblध8.TJ.Dcf I8j[xBw|هM 'fG ~4)1F|8%J4cc*BH34yJUd.McGe I-lĩҰ^4TPAsgnc^ڬf(6o#a as8$-Lҟ%2nYxCSvSU F$(ާ{_ui"hP\Iv @Sq>3Ȩ LA׫%(~JbP"@g0,. g'#@,%#f{J#V2b8vK,$g:1:ƥj&^q6M uOb@KDHƗYV3%x~߄ђ7 '3hIFuW] 6`u˱p$] `ZޫPs),@=/-;i$ c=NQxR"}q{=cH)鿖 87WSJr6FjN@ec' /:ӫy9c"Ax{ #` hUpe6'J=vãם4[ Z>0i!3!% Kdܲ^)9wT_ ka\$0A-Wu`A|Fr! (=]LJh~F,סF̀`p0,SAH"?e}ro=V[4J7nStƒpٶuW}}(9QuFBaF9W'(IrPH'&.1zԋI3G`1HaĜ8O졩` hU-p l}pbH$;پV34,J XCzU};Ga[lG%l8{19l$f("dM/9P_KppǎthK!Eل(ynah*Z I6%96eUJ+Q:(,(m\5͈CF^U'CÛw*jm>i=@\QI!xq&%2_ P9V4 ʁY qoI?Lp6! ?:'X,>8e0c;uD&|i\a'%rq,hpҐLQ)q{L\96F$0x&XdW+g>ozҕO3ePt@:% m~;A>q"8A_*4ҰD[y Cr.\BAC>D~2ȸe ,Fbr!P 5nPTՄ@,1! '3qRt,8YC {+@bvakuёIH kЀIOb?aTSOP6Z+H[4;%n̖/B]zpx|t,,q?W%-בw.k +`DQҩMވ;i]<;r1r ZW.>$2_ kz+vmMhFn ,̞m}9xw! [H ZTGՇ`*CC z>8:' uqx okH{:`>!N,HAU!}^D<8hfZp ptRۤ"Ko[="9zΚLM @_x̼̀yz6O-cCp9;:K!Ф`TJ+TcueȠ^؉l\cYU`;ȨGHԙ7ՀsUJ(uh#[rP,LWpZ: &7KKm\pt=v]֠94B5vɅh.! GU`e۸ ȯ(nL Zǹ kMS▂ID%|; K H!{ <44G<8W7RQk^7IĬPLlwN1`k<# Fh %pK85 ɑ,&q '2I QAļLp6䁤w!^p3 L%q `DL:2݌G}t Lpl` .fbB+ 8BxA=:cv>9ҺPnfvC٭#QQ6r,$hH q5.!:4EJ8J[xCstn"4N>8ZhZEc؈& [0Mg4bߟ4샴a[v KwkDT%G:]#z(3m Y0Mm*B}(,(m_xENJ͸Q/s4N <66!P d|w7Oj'Id2^CbqbiaD5) e `uԐDX SlSJ#}'ep^6[!#e>Ya|cCo%Il/R``1`.$_v1f,Q{s S#1b -A]cIic!i% Y7P҇hEJ8J[xCx!a0P'= /o`򡮊Xĥ x HCr GR&/F]/`ӾϏHŗ Pbr:jEZLFePjIM4 K4  =T۔itW+ K8J[xCPt܃c^{ek Lh|rBܬҜD094:{Z""Tj~ϸp%} ʉiK{19:.i'bFa Gi u0SZLLLƬS(X&G0% z鮺)@i!w,A{c2׌ $gWoz/~VZN$ɂi s1Pp i*GZw/ꂜkӹU?BX0uNAOEҰwp'|&w65w1g--* B \>@tqq4lb$P 1tI,ps4'-ĿT `a(V(,9؜ 8򄮼CK.HN-T Z񺔆%-wW_(х$ߑ)IMl% DDZÚGQCc3'o s8pω #㰷Jf (ª7ұĔ-$]p3f$%-V**"E@u/gኧǮKItlV *)Qa IAScQg uۧK|(,(m:tWИsiJg%PO*Hb-^0؀hζ7O$%Ԁ>v~/[-ڿ19;Sy>IU`l]Y5k7s*l{ x` gW9f,Wr/.`y2-@?$:]itM@s zbAZPt瓕d4 [I=QzX0΅YDžF˞y-"rXl cNCq090cztj]UզmK FwkHAk= ^&'{*n K[Q{HI,kȂi98T6pұѨ=$dx-B`LR4!= EV?$k`ù_$ [Džh!p뚘5[hNnТ>V Osut92(v@А3 rgpJaulRzyH, f@ ܗNhR UUVc?n %KtRH,MJbIw8EpaqiPMy[Agy]%V:İĖ-$}/196ewRLJoNp^f.ϣ2bY&tF֐~MNTcOO%-K+u&ƍug9/HmӾ{|pp;is@,%doPlbE6Z4K%itXuf-Q`}t}#?9Hs8!%pYƢLy2$.IF,UkIWg'cJA/OH(B?fpbTc4ciQfz =&) [0]4,\h>>H~Nȸe HZg'9DwdZCBi-HF;,*Xui% D \pOh!*|Mc[k)HxaN_48C{ ~Mv<847q/6뼓kx@r#8n h,JIͥ2% Wu'995B [Y `:V55d|P2MNR ҡ , m 3p_rxF; L=.H;8?>CfT\s B$E9[;>Wg8s"UO p&";9UCJL/g>%s`gQ9( Kdܲ^G^z^eʹy@VibzҺb=%8X0= +se}G(,q{|fkv=/D5*)Q *V6Y2]ч}m11q? Lh(F6owa,Bnll-2XҰ=%9Iߤ9\p^d `bre$WGǏ It[A{s;d,C KrA`hUhKed-I!V P0ݺ.fZq)K8FxB[z΢#"w*F p3oO"PTa4^^K`R|g :790Uf#LmIxTέqnX:לx$M @ WkҢjKոVF>fMj@LtAE'}^O` RSmr m$;JpQRbi $I)i'0zBlp' Њ޲o)=F"oT"؀iδIFH $2F 5#@:Հ.UXhg$XyMӁz{|cJ>HFY3bWUqRG2*9{/э }f'EWRb^֠ gZCb^j.q !J"x}}l) N)ąFߓH#%#MP8W}?|ƥEIhyHdܲ^.ٚ[gqV!|ȈAC`h gk[zgiqĥ;8t6Mc 8.hf}½h +ޘܕ[CZ6֐Ľw,"Jg !Z] 9< o$✊˔͡qb Iw?,<\ހ^c[j<\Qa YxC%͛7SYضn7}_%}'wڑC{[i ۔p1/:1sh"okض`,] hĝ -Էti8χXx&e`,󴋡%-V+7EELuk\C^'g:G_C~ڱk??'K8@[x/AO*1.fI1CIY Kcn]. [h9>a2 K8J[xC="<LR"%p@DU.5!S";S=&p0oL);|V졋٥3i8-g}Ʀ偂u!c$=|~CV>BYr"co,A+"eDه{:e^K1 yEҺ(hEn=8h^do894Wǿ3vD-lBO c4,q{@->c|ƅ[J(4(jA;96e @vp^[嗑a9q}^mVv*$Qg$ QYH>pfR>z8 ` XW,לe89߿ 6@ۭ[ƃ(PGbIA"/2Ϳ/h X9hFYOI {$ja@4.t0 e!H"C?PGuDIN])ޯωュ2M7;5j$Б%8,~wNkCμUHZLHb ¡`F1[M%,W'`EKuNr=-25TₐF}+'a 0Ié6) q4֐c*bACd)i >@T CLO[0YҰq8mY/j!Z}̐DF) ֡6Wm(pQ%[vq%SIV!o%gɰq.HAX1{p=Fʻpr;FQp*hR>S"iƖǁϏ#Hl%y/Fr1N6Fsz D}BEbi/|[AZDa Gi u q[WvrX';'Z I?<8֐wC/-hUqe UQ8 K8J[x?H% _rWmnljH44_ XC vSGӉZ^,N58Pלy6`ҍz6W%nK$-މn caI>HZLQXQ{,SH&]4`j^6if+%4la^vp8uZT>Hdܲ^ K|K7aONVKKRDFZQ>tVTuىCr8w拏ѕw9֠ľJMi!i8ew ,f5$$ Y ]'\y0]rHga Y0MaNMYAs:T,SmɉFM[ȍ˫'3Fi0Y祉X%q'9\0g W֐&AZ0/fqu]y8 .` h a}bሃ6QJ8ڵ'0tBQA =ROݍ?gN@{ӟq WX-qZ%JrnUM B!Iߕ@TIWs BT@.D p6^]_$f g[4O|yb%W`zp4lGSqwb.Y&`_]+IUBm!k!CZ(iE' KdܴgJ}_2xY.lrx:(?=YHWq@>"%dx/B݅/S0 q;4R '|X0M\4d!>$2+T/_(an~͉gçpC;.Ipi$V*,:A&nG ] p6^2o]c ҷZRͺ4,8a!骔dຘ㲋K'K8H[xG3 +:ӁXgWi{m*X,..0ݚk[̉S@;/T,m <6+,&k4u7nҰDl; * [(KuWQ %-KA%j1$6-X`]4-CwcHZ~:?X]nuw^dN̅jTGDŽ46s8*a4fSY8O# 8ىnF*{p psmNΕ4Z6/$ DАC-FC@01=DbH hTؕOB`oqZ֐(`;뙜& [h<>gp^]0 `i3sFX6-L-JC zy,$,%l|?Pq%'߀)D7KjЂi@8{Om!Z]gԐ"O@v.,9 `X6Ln[$”J)Myɇ#C@H?kӣV2AȳÉ=\JY7{؁dXDyoB*-` $mEg5otl:4K֐氣XҰҡ(,(m B_iڨGm4a[{%ܰ,4(9ĉ-!wH"%eZxCn{86xB{$㟑$vqHD- u==ÞSYz#Υ),z#[uSCb[CL4֐{ jXIb7y,TN ksĉxc,T1BBbi4}6G=dHC4[;loޔ^K:*0O3Immzt)؀˛ùrR&iЅPP )Q_cByub;n$/6m֙ X0Mv( \Fb@,5p뱇*G.5nn4 zwkІ1pZo--rӘ9EpqӔ>.o2 K8JY~WQ }.?9qFhL]#Qg$?4C{!Ȁ h3 C> 2S p6^f]J G`[eIǹ钆$p؎CWY(8|ωRCI"lz6g8 =cCf\]b f2}Z8njlaY8",M&'1z!_7ch`&^)L]GM{Vvq51ӁϏ#K* ޣټ T 9vҪC"~ba!#s{(՛퀴%"+wte8E8➅͘E}HC8fbC&9-lc\t&1ǫɃvƌ~](Xj5<p: 8+@<,CCAL U kX>sZ8- [`Fǣi)P/Dt38qBY(vI$Q$1U-by1gRGa G) !:^[ ӱo?K*xbH EZ f0|I [ҐM`hUplTmW_Mm*,#/5aK 1vBU;ɹ H ҅|FH iEBEh P{ܜPY&_Y/[=x IM~H=akQUVu9-A= 5/LE 2`Z.ԶA5` R>Dc.Ii**^[H4e׽9PI i9Hx_r.۔> ,m ͺتAorP|E@xvnBPT*5$AsuWrs!9^,Z6 |NL_gȧNR2zF[ڍIKL͹8f翶}(,q{lUuc&q$1pk \[)Xztl fDY&*d.H~~N p6^ʊ.6_1oN9.T#U$y uӰy3 ">_ ǁ_'KdȲZvD׍+[f81D-yKĈ~p"@CH"Cͨ-l>V#nkb%WC*ǀi@ 84@Pp p6^Em Z~fxMES/c C{@YHox_~xAS VwLHĜ7(b2F|}^%n+K2 @=02IdgzftZ.KOxdI%w6Z)gu"Gy)$KHtzO9\! z`s i]"$ $I5&e']dȨe '|1U;ͣ?6EdD,$kRlR ]LM>AX1{:NxتH*{Րس}Yq$z)7E8J!ik⺧}8I*KdȊt.O+7iA $Kռ='<. [HjtrmB'.@cE(c8Z8͚<6?h$%?yZH>!N,- 9QXQ{94SX &l&ޅ9ꑈ֏"8>\\1 ZE\c_95R: z9/Zfhde%2.I3_P,\v)??2jxBxLMz:ST VE S7`qWmi Y g[<>δGq$-ס r8{XB ,Q|,z$G8viR@j|y(?K8G1V%FA0\ }vi,ԓ:$Μ p@'p^_e&T4-nQzs`q m9Զ0kBOϾ.Ήl. #ΜϢR$VWq,&T+eqr|FMQXQ{>i]} h1Vj0H#M)TTϏ3^WrJ Dm8cJ4ͭ[I#9 EKDA|X0MHQlԿ[g si$N(}3ɸ27 r+i$! [%OX6F'}Z]n E=ɗ^*X*!`XC 9Kv!DR@F-UzO+{p02C5<0 HU/]j%'D q[Q&! R1{YdBܨ"8v5rpAMo Q! ܨhDB@ q|y#QŸuhOv&d! YȀ!W2ZNZ=\;}h D 1B%y?)'9}y65b ik!4MLGh44vl5Ĝh˹RwMsnr{KslScqAq,El{w7[v4ueNR"i*cɚ]>ۻ28^yL;8'\af hFG Hi[ r!%!2h׼_v0 3u|cyӕޥjrY0M҅~9>}d)e= /TL̥IfD?6+ Wwcƾ 6zQg[dcisk49'G{ B` LtqNŘ+@\$.n9Sj>{}ҡ,ef9L/U!MQ2ӐaCI:RSXdi IW}qsa"-ʦӡ_%-oeU]ᥜփkcy3$w O2ĥYxہOb m Eh#uk 2lOKȰ B'mQL:-Tii$NܴgDl-NxF?RM߿Xdw$ %4l4}@Ü=7cbxAX"æ*ĄC2SftҬq2j]Oק֐ĺYb $ZA*U#NpZb&3}b* e5@CwMG+%;lvdrmBAZԈa8n[lL4+`/,nh֐ۃ߱kzXU3>wV/ܫCGGqhQy]ϭz I62`zhN烃gyRҏ[p-AP?QBEg$q-6m!KoNm㉭-t/ida6Zx @\hz*&fB+:HN%",$MgYCV>H[QXQŸu(Q$^%frpB>CkekU˼qOp8zr;oJKwĠo];ݣ 9 c66n Y0MhJ*.(~$-!c.MnNND ^4K؇5O- [0]3 8Fx[8 *8szAd]y _MIp1`zzBrP؎a1dp^gwXϞse;مK8HxBXhjž[ptV@9+L'7-tiEwp`hUhNb 1&A\\%aD㞇@Q+# >5C!6Q.H#,1k fȌG JE?-d0Mf~U֐-2K8f鿖Wlx*UH\1fؙ[1 }#]uB7Rnt &8;@kTU|ٜTzm V?*,uja4]y  A;@kdw`ϝc1͉}݊Fxz933bʪJK,i؂c1Ar,D- u )@l4jN4f\[͉¦QU9@!a XCItU 4,q{ CKS8~0{#qvm[A:D.*aiZ]҇`)m >Pb#aS&P_0tbsx% < $}Ќ̀c٣\J@`h5p._[C)9r5w\!1.&e4]@CȲ* K(Hx-B=*kIbv+ &glͲ;<z<LqTt,LipȸeU>gVT%g|< 8;(9R4nBYJ.ONMYAsDF) ֡Zc[/#3;gd'/![Ŗa =8gB}nDGa[PIlr6P~!諸+O'ܽa4,$~8tw,ąR{ ĉ9Ќu=+ɐC=˷$S`ZAD$li@tDDB$ W].KkMX#YIsp]k`xY;-5==tZҰrӌ,ב}<їx<9|0S0I2?Һ$o>fe4](>Os Fp6^pn ܚj@0+pݓ3ᔆ%"SL)R. pԉ,FsF՝H$gs`C_RP9E"H1<7OR}ڟ$ڿ#@,%ʣh×19W7[PDf6xHCQݎy1,.\H.[!X"VHEpa>%75u6 l~SIH ID9T}X0}UMoxAC>DHj Yx} D2ӫ89b5L㑌-ݑҐ$]8] ;CگYnڙ?kB#%B/Є} hT51%5d4}BD,D"!QL u0И9ɢ nL$ke!)W0h6`c2(ǁhlLBJ)Ha(Na1P:U+3M+9g(;WВQl=i40e"KK KƩD3:(#V$r+"?$^˂▰XaVVȸe~f6D/6618y;4&K4>?w@VCںɹ%N'|Np1v-{|i/n+v'ᔎduз ,ma6o~+O-pLb5|ͤ7q,qK0F.)Ppp2^ p}(ۈD8̜tJm!j#sf7-[(G1n9 K8J[xCW7: }rv|֧VW42ƍ4]R"jBO/\$G8Ó]PI G"9 @G/KH-KQXQ{<̕`0oVnty{ν 6d\krl`ޯBa(>Vp^JU;q9ZI#$qKDIKd\_)zz]\?bg6167/ʼn:Ջ$ab̨,'[`) #O CWCm7nh!n!??EJdܲ^pwWqReݽ9C%߈ݳ}c?$ !hI+ǂ‚irӭzOh Yia7!q{P0=@z"PcfOo28;i=%:˃σQ~n] vA9a ,%C`B#sIxjR@0 FQ=kܴ .fx,Q{<]A@s7@/)e@f-SwtZh谁"{ 91X1{MP?  Tywb=8?$5d2Mr&J֎ @c&:T vPbxJ/0\\Y:hHʾ`.>?3lSFaц}($ ee&VefhH?6ILT.V٭+[&1#Ja?,eէ75Y׌Ŀ9̊S&Qޘ&[0MbVhZ _K`vnp@v}rZ0@_96DF,QM'HkJD442 9&)UpeK-!5f~iAN|$!:r-+O2LbK@r@)H{ jD?;5Qg1 3cbn@",! eq:@K $ e `͸ѕ2C*@aIj)/4]@h=̓|38ދЃ+36v6ؔX:Y 8ʇo1tQs@r.i@0u@OP6ZijC5جL SJ=#2OяRҔPhZ m0~;KI=.DB<{k.xKFﻹqD$[Jش~\a@V*7΍\t =%PK8>[xaR\^5zZԼ_EuϫĭnB!92!hЃ`)q?ye œajQE{+dMAiWr YfCկ]k ]́JO*&n`*0Z4-I~xt#d iiȂƙڅHbzL.`]!WEwt}<HCb/Ȃi8 Gcaqd Da Gi u_ U0'J1[**T79iYC%hǂ =>P!>"%- b%LNG,1yH5ZYVsaw n J HVC:rԆ״D,˝MQv>r Gi up}R.e1/.)q)Mcұ)U  !.K?}'>rg9x8NK8Ԙc(Ph0jdT8Kvp`` h Eɍ*)ș׀(z_Am ~HJ6 thmVj=y,-m ~B ` y.YZALoM;N6:^bi߉A Y`8=$8弑!IKP3-hc %ȸe὎*ʸ.J'EE`r.XJ@i,$MBR>- qKdܲ^w<0~vyUޟ e$2 XDE۱ iBA:4$2JY^jUa, N"İ,_>a Y'dYEf)V+q)Ot]K4'F֐Ti—it[,YkimqMae"i5 @ CON-p.Hf 8Fxo88*9ʦec|(3iiA =B9o},7LFa Gi u61Xch[T0qQ Z9iP#%5duÁ<-}dY'}cWֿ:"95Ƴi̱%jqLw_G$6! 4$2H  "mF+j#r!L}I¶`>TnDeE7ۻ(D- uRSxb=h=XH7MAl1Ѭ'KJdQr]}L=Kg3c:>Cf)ᠥ^5ݼ`3"5c b9$.Ao[tqO\$e@Z~H8dx'鸨8ѐmm¿jt䗾=KA31[ovp&K:#mtX$E)[mBʰ'=$2:V@`ǀh@FG[υHC"3e*T'Y'N ً 9e5iF}(IaH N`Ɨ=H>wR>Z#FűO5m Y0 PX*] ;B4^z֐L Up*7`G.'IYl MY ǥF$2JY9//! [7v|Fˣ5$(m6%fy8QJtq4W(5SMҁ{3 (y 9!X{ xfFHiܬ׸ :RsDഐt3Rgr_R ҟ$2H MZ)_ 3yN1v9"8[Ciԍ,^Omi']44>93kq[H,mzGgsWנ[k7mP^fPC0I6 t=D[q@Z?_>x$m ϳtKN+x?D~|):9 HYCݥ`>>?E vAs*V7Y:ՉEB&7[ OO) K,ۂi@fՌ +KdԲZ)@msyze^E!Ɲ)8} ?GLӇ=6! EK;QX"㖅:GI^EeP|7iגaAtQ/Iۦ@46 O^LytfNh󌽀_ziqwYb^5X0GP(MUڌAgR/?*EKCrp),0t02fppH*H?ɋcv7I) Y]Ѣ~VoqP}k HDM}X0v8Ҡd?JCQ[^DڒYέ[{7DLi ۔@o~]E3g$V:US9ENw-iX•`>Йc4l}\QX"㖅:TIp6Ҁ;⥀_F/nQ4=L1p:]L2ޑj߇}m?qMM@``g103U}D|CJ IpR~˥\d)Qk}T-jYh.# n4DE I}=,L S1]:QXQ{bA'Xȉj;Vl<âE*$$Xd\@Ü )hЅH?Fhbfx/Jt.~n`zp84죻*D- u緿蜮Ͽ??Nzm ;9l#v}fgWf,i!Qv%뤁Q pp>eT1@_|==]U~3t4$xDB"9) ckQ&(,q{@km1aJix]}k␂%{> \cP82}5gȨe ]yr31+C.s#aNIg@J,i؂1AɨjrAx/GW{;9!:V-,n-HZۗu(iXűФa(%-w"&qwfFeO# KXHĜGDz,LҶB,Y,!:dlvvTi'`lIcobp$Wa<,$]fbFIV8.H' , mᵊ#_zkRrQ Ϲ Gi2hMҰfr*&N-AZ аD- uf0D\uyrQS?NƩ2G43Hz[`p! 飸́9Ix A k$O ũ;&s%Cv2 1̸2G4$Dao0$dcvz ^ZpC <L$zLa)#1*퓣 ^diHy-hJx F␼08~ >ƣq¦zS!vd3U@DAR^SU2);5K,aA>sh r?ϩD+\d Fc _yg%a ²yDTgc{NFiN #ݦ(Jb.`evc6c%a .M?X%IAMc' sIkxET&;_EW##-Ecju> SV`LAِ_hM@|}H= QEْegxHccQkHE䨖cڀ(VX7`L"9R謵(NAƮI d=/g˃cD{i0 HҰǷ44.Ę3 sKkxU+I~agKcbsD $`ymJGC)hh jH\vP az#{cQ,IhgI{NJEě+0, }M;$a'5 aq!aęog/mj-c{aPmq LPȉ bq. n-237-bR\Hw䢇\  MKAUGQf *8X&,({?⌭UXDȆgy90G. Շ.I)hk l9a;)Qp +-Iʎ g,,{MR?wC%ĮQR iC8$ErJ]|pmAc{UEO|ݥ&t7tQuw,ĸzrv^Pp13GT8kzr1"=PcĊ|\H,@ H+ZBlx.K=mCLI;WdXk0$`4!?Iٰ-%=%/ؕ'?O%zԳE/ﶤIBSKJN XA=MNҰ$M|\>>Z{laԢ;¡TGCDx9c 7ɰ yP zTI~ _n4C7c3'eऻԂxw1L$aV8>IKJNiֲ'i95'J ?)(:WjSKCmzyQ 喅]m 氋Y2( /ʁEZt F:)6X"~B.ɾkCC}ԖnێvxτxY@ sۥ&OR"=J-^t!(wgakL:/)#{oX2KŧuX79B,/YE}}J?W egdR'bJy|E!8fqHi<(8Q9oix#5cðv#7DQؠkEVQpR` #"`Fe&~ A\0=s@?YvE& K`#@U9eix \IMJ=tuu ϽP! k0 8o 7Rn{,0G- qy5Tń !);/[sf* g_2T H1M\E;k)H 3Eɿ!v+|RZg$F#f[)8]bK)1n8ߟˣ e0$'L8Ѭ7gSd R6.~&V`,#}kکc/4 [~cTk6Ɣ8 '`LR%a ^95ǡS]SLSjt.1r]Q48Q*θci ĘsEkxM]&il͛&M䈪CCTKHC9ye8気Ǐ/8+|?NVs(LO~S;1ґ[h҆T}j-`o4A|}>N$ao̍G3Rv쌲i X0vw;Q8-! ƻ+%e5l\% sI+x bE՛'%|QzGO 㒲V~r쒠scʆr`'Ʉ0u./QD˛N8wOuTqhn*!UXtեGҴ_3ϲDrZV>oU5kW$bR1M\-+8dAiGnٸHy(7)s(Ⱦ,ebvcY4OKagye?G ncI9ykע)X<{ky~2"~'y8e۲!oR気~#xa iv?xt?bg<8*7R1G(eg/(ea1k+<%)K7\t Wt`I⨝kH\T0+) QSÁqH sKkxcUè*Jd?_& _*'s.TKC➵"Аsl4gH=eUFYLľ!Jšyr{%5{G Y2,! A6tగy6?X<}Q*&rYtcO|;ɁM 3SQv[<1 `' `-ԣ" ;\D8ZT a,)4` ڤ98B)]6d?ʄ0`Dr[]'9ǔ>_q73C kSLJAZK&vX V\0] /CcgJ;2}Ǵq7V$1 K ƴ)Ym19oixCvٵ9('Ck\[b<8xܢ KHCt=y|vA$ɘʶ *7-qE)Ы`8q-#MKv c_Cy7X3aRNNK0 95LRK2w;E#v3(9Pc1mn4-QXti .߃έ{mKűȡXϏ%nW'0!Z\ح}Td7{?>'ť;ebY~lO䊒_uNԃx[CbLʎK&  {a{i q2OƉ&5MN|Lw}߷qHB i'*Rp/0AvY ^CX93/YAA 8w T9QCbr.ٗw)0ӆ0Ґ$a{i q (}E2wV+Vbi~Cq&pߚR"yi()UU qT_qjѕRw8E}h }$#+'ؾ5$ m35l)gya{) }ɓRr1ej;\$%z|DŽ@I=nC/Yk?V6^o\ʍDfDXT(>ĚC)vE9r.EY Z>C.Z5#77S& hl;[Q dD2ŢM0vVV`L ADe/ '}H,)t8c_rj=TofJ)hC֐اI)(6xֿwS 9kFŖMoXyzDE潈cHiB`(vI0vHsBŽ֤JlGK~2 e" H(k3L c)lԖ$da GS&B:UQY;vd_78)87H-! 5=6U8#XEU:猃ӿWGccb1 &$M*bVKhOO6]f1[[A743Vg_Hě$P%a>気/ ʗ~?(KWc^4{8K -9ξbH`mP-a=vf`rV2ΓvQg6G$! vr-hLgQɟ〾<?6Y}Y46gvGVؕ!Y%Cd<ix6{Bz9'bH)E\?$e>3cE~B9%t&v7F渄t#Naӹ , >jz?Z%;'v$`ttyRP)gi`4Eu`Z ޣArOYf<5Q^Q -V5q<7 coLbp}eoe&vM,QIZ=-th 6eS͋)NjޓnH^qC8xeBPNJa1gx{& :+OP{ _lR  lVI ZA1_ +Jh&I9@>L|ރ! ΉwUZ6P3n..J=GII|oM1'rOE0J0@6 a 멠}f!`'̑nS{2E)&3W.t=ꃣIiCMpd m!a[9w;DL( OxD6k>x_X<i[lek' kx_~ؙiʽЎ3O~Mr¸ȌK:㩵y̯>f(d,a=XEwDH-'(2|p쒠i/>S44I1^|h*޹^x#Y0ڢ GQ(KM0ZDVCq0P`XT&dx2 lWJ] 2Ʒ-M`yYr6}dz[1-\Eк3lbnOh7J˅EXikypƷxp$D{YU'xs-0R9UjPvڨY^cY{ c'è g ae$Uu:0 LDu.B:aޣX٪` Yk>eU[buX= "l,Q?YZ{Fe{V%fCMU)fM,nC1m\E9&! 80$rLMdJ_wci5uKMR1mSiY؋2 giP:ÍD̠E9%6 ʭZ!7485ǡݢ z B#%kAk$$7$CRTEP ]qY y,t<_T(M(rҢEtqcYҐ&"DRܪRlR&C sIkxsWXULMFhy2Yuq89* Wn+8-a17vW/% J (o#~a^ܒ6I<E+R?1֞Y{-?[Cէ.%10OJO Ǝ\0] ~6s9r"U9h~a,;cT6cбnu0eaT~A|}気ǣGy8]Q ?'r&QLj/VӅ9Ӭ@ ?kGszjg~P +㒱zQv$ s KO ƴ/tSC߲A/[-UyX>h3Z`"trٗlwټY)x8K!LpSp$Sb/H Ňa.c}<-icWyOPOzϣ^;`;h!_܆8vVHkaozS %GIJAsΊ%ASk *mqiJe(lHz KHN4`QzvC`{2u(~8p,m* )0HUJ ␰H= Nn:UOt*Ǖ)g_1l(#w*FKCm-&5w*81ş+k=5 hS"wH30봅 On2@`Y ^cjw[0ev>Yc˩å&V"9$x8dxQh 6ew[H=_`_<*|70$R: 0|{.l.06}PjF,7uStQ~j)xct-m ?-EVvރ%1jU;]o%]EW)ka=$d-H]$Δ}oʖj4Ę)8JĻ2"˻eCa/=4nmZBŌ}Bq/[}(0 DB կ;" z~_UL))E(pBlO_|?XZ.ҷ<~S"oX#X쏍FJCYmxoIJcڀ)-J6GWnsӶ(O2Xe;6G Q0+,ݩSai9y*}0Cz-QpQ54;@ khGb-ؕO'%RC͏%Sf&4tO!!/^Z{!Yq 𓔡ƠF4Ɯ c%Jd'7'?drvBF|/JߘQOt]@ςS,m`'X& HQܦ @铼?Fӕ[zxcb2d U=xbOtR% iL(氇R/]6G H=C6-j0\&Txp<`L')1.Q/,Fg%vŝ,IE D[alwHح|䬣9%AX6d,5) ~?%ȑ~K-YB%"p`4σx/u\7+!.}-U@W\0] ֔@c:2m:^,1 \ ޝfJaKZ"Bw.g`(O#NL^|o?ēd.fr< 1M@BX+%6A>.R1(\i>hur'#51Nh$ &^ i,ʭ ڥ`EY Ʃ|0\k̛՗Љ | X ^=1b,!X!>v,d}oKWۓ95Hַ1 ݈VBP 4EYK}њ5,pKHá5qd{a[Z+zOw~)8h#4^s=1J]eܬ 7 Y$lNfV#,b_)@11w4;jY-g xǃ!:V͇ěM3 lo9_C{$$%֢6)!rƼ4ӆj+! kiw  {!RG%2Y:B+ dp J9 / w{jJGl$a.-08~vU7F>(|D]$`s_ /fbP~~*7e$%5FM!>BrEkxbNk!-+l#Mzb%8Ԑ8w-! \/k^^J$d|֬JH wIźv]]*} Cɗd:x&)*`l:e @氏VZ=U[<\A9&P;Ja Gl 79[B65d bs'{i qdŃ'>1]AOŸYV\%xpT6_K ƴq}射4| 볼082JSkT{qx=:1BE)!ėHĤD4쒰 b}nPIiU^pdMh2coCGHCߤpb4igya{i qR `.}j [ ԞA+{= _+,9EAZ mNZ\q[oJ֬=uL# ?OTXqcZBeC{LK諚*U]^$8~yO4A „p(д`dS͓D}`LҬSR_6;-/a/=7cԤ!ɍϑ8}Lcڸ>rƐg $lcSP䍳ZR&zzw"N+/6- ,gQuNy!!/qT>eWMZ8o] =?qS`LZ`b igxQӠ9oix#OgqPg`L{HXg}pD#`LZf$44$6- a/=_q7O\:qL7S?/D|+AyNRP15L:#G[svQrk545ǰ7>U8Ziʾ1b 5# R`-UH2!CfINZk?Y}v86ה3@7xvu>ݏ&Έ>5ǐ\[8v3A+xsU"$K]Фٸ]{$1Ri3U63Mnۄ0ft"9kixBp0 kJm 2\'/˜S #jp? УMQ[r= HJݿ_eca羛v`gf݋hy_%gS 7\AydB"a2%%Pe΄}Ib˧ɴ}WϿO5eO?}=/{5iߟ?Ub: _E-#~"B1g hDF^m&j8?7)/.(\> /W [ 1 3 1 ] /Info 3 0 R /Root 2 0 R /Size 547 /ID [<217500729b21213f9c1071faa74b7fbd><4cc252972b0b10c3c8ffcd946ce2217d>] >> stream x헽/CQ9mD[҄. "F" F$B,A$" _k&-˛}s\g%g0e륇A[ =-}uq䓮|iJ>ɍ''|2pII>I3T4kb<&Mse*5_b?IR//۬ObVbMN R28qO}1V>RRUua7Yu-0fkwtw3 1.6̓ie 'Ʈ+|;:)Wj'?#6T5/r2 FyL?@7'Yugzd̬}RM endstream endobj startxref 421160 %%EOF clue/build/0000755000175100001440000000000015145302607012322 5ustar hornikusersclue/build/vignette.rds0000644000175100001440000000031015145302607014653 0ustar hornikusersb```b`a@&0rHs$甦啣 :)&cX&1h0X" ,LHXs1dwI-HK î?+S+`zP԰Aհe ,s\ܠL t7`~΢r=xA$Gs=ʕXVr7S4Dclue/build/partial.rdb0000644000175100001440000042261415145302602014453 0ustar hornikusers x[5  p_lDэ7Hē9g893s`.o԰ MgiIz؊d=}m]Y$?dɒWd;J(V'ڱ88o(MjUսQb?!S_D7׮kZ9bOo'bvs1zx<'zB/JƶK|[/3Esm N)ЧUI42uw|lN u rGțNݯ=Z \JJF~娇>Xl~oG937|ɔwIz[kSc ?c/6V.n͌N297 c$ٺ_6[kk}^~~\89F} /:V aۏ)xHkwwLJmF92`=Cp?GAWI$ԋ mĆ 'c@4[ hH]څoY=32E;oesXJ]`oLb A!29s粘 O<拏!I񵫲CPxc/ 4aKX.JEtئYRFDzB-J;h_: U5k}؞iJs.mDML@6NodM'SvGHX4{a H֒4贲.MR3 \4f$<Su-3i^ܲs9-)h{Bȱ]wknmaYB&VF#g(ˠ;nΖ3%x|]W/WXµߘŒ;LedgY/f1õMER;}BrL\V.ߛ{vy.Jmws.|owܵ.vUJ?'\ܕ} j5eKаH2ݮd8QSOu,0/ל\tu@M rퟏ8gе$2t,IÒmWWf*b_+9&Zƪقeo񛩸f:؎U, uezEak] z`R?BKIc[XJ5Z#+0W0^)1S3kޞp(3;k/2fJgf4*rF[Չ.Ͳ4FkIcx湳ٱfh=Xtlݝ -Ɏdڦ86\iM,IOδpOgdVӟMόmn5u{jpy>@BWB'|iV_ 'yky*UAeK/UԱp׌NIj /tKC[9 c f LOcC7z*d-1Գ.wPCWMި%a[Fǫ1jwT =Ͻ'ԽcAA @iɍ[f\%|C:4 Y׍~v:˳z28i=kWD :eWnX\3_se/)P?tFƺ+<b.V3K4yqIu~MD.K\DSFquUꐎz!053/;΍o\/}|wZ(˅x;Nޭ=4ԏZ W*߻ O'ܵhRtl݁sC[omƋc~_8Ȓrס^wv1ap_o}#f,7f7F̕SPq}aoĆ7\x%8 /75^N[^u:[UM:b!xyTPp-> p1mZVa]ۂ{%k5ȝ(ՓGo xIvO '(λ\9;S"1`f~vut009F;BcObzƜhȎ瘳縓F%Q V>dcȼv[Pe82me!+Ί v kuh 譪l:v2i< !== Y>74u|Go×adN~ ?я~!}GC(;Cgz~=&2[?wv2ivp7\lf?{-Cmc]OnB%VEX~!N̏}1[4wIŘZ,qBęcrɕ $ 9Bg ;eHo>Ɩ}woꊱ=Rᰛ\Vn.Y/pG cFd*[hͽf!M8 ?wE`50 |Q8#)!ŷrϟ%MrħɃ^N @hS[QgYE Le,s9N6eL+'(|Uo[ôDb`yOP˿ң%+>Ѻ,p]BEiI&fFETb۶Q)Z-Ոɍm5n>1jJѢWǎ <1 dR|]]]mBg@CyZSDquP-QR]OԊ\60J|*u?oeb%Sz_J%+W{r1<ʧsmY)^R&}|aK}PmR7U9 [y-̜_1"H<_T Yz1cЏ{guU\gqA"^ o‡~XN3#.;8N 9m5'FSMN^;PQw[4N,kGT )O%ƌ7o8f rG ds>6.…6;=Y:xh{)kQV}+2Ȇkה9ddEնJD:v_x,Gx4Lx-^ރV{Ak83&# %¸!&"hqC[>hs>-wN$|d.@/pnpXUxy祠nЯvnn rirG4O[,ή]T>+/.gO%YS&6!S p2Lbc;*QX9 7D{Jh Ɗ66ƊEƶ'FSM+zuB+uWWW7V{chw-5 NMnR2e4J0o>577)cPځ\ɲ ֯r]*u@Sg31jձ;uuu. SmZ&$ fKmjGq6Җz_rcidqZh;p)PSK_.6` Gཋg(0+7>-h"V#m ŕ$ g0} i-2=5T2{iL[h:Ff:kx 7Ng;{*#T*Z#%4V#~.i>b6IPs##əN}i` Qj|45>>35grvbFQQу鑚8OXO{~\׫SU! %n^,~N~X8jZ_5R>Z~ -,F}js;QnIi/*mQ|V9d8fF(QO1q4Q:'O)IM?dOi| cx:5;9;;579>>JG :LSh~Z&AV![;Zk:Yzo ߝTZGNՖxPMPeB,qGr>,;uTy}nڬ\5(AH0:^'掁#0\#ӻk|ne^{K%N7+p$7!!WVR{oU=a?nͳ 3 z|oo_l[[V2 %%\c19_3W[ӉW.٭1j˕]OFLK^%:\2*mH8zHs.+Xy+g.e:¬;'aOori:_KV)/x S4$Z`7SmNfYtmn.Ԯ^7yB݆8T!]$:fI4A)`>mr9{küfRt?担C 2b_zfMNiaJA/6j~C^H?hXTp=?#Pgʫw}6]',Z^u0׌i1|0?]eߍ,(7`e[E;w,kmy3prWA*wE9 읰qI}Z*n,A+K<7Q#\܉ .u.ZV;3͒T8 zVYR&/SptE^NoB-o,gǝYT!GӚ6m£q]Y,9lv.7b44eV07x:!^ׅ!.^zH/^rx^쌺Blc[0dI/$\9d3^;RCQ[U29Ǧd92%ׂ^o݌*8 Yږ͊ޠ<6S6Jh( B0˹zqwjà[4܉(͝Riי+9jYg,%g'Hc~虓= 6]:Q."ZmVW(UfDx/on5P,H|rݢU(I9? Eבuz*12E bFG_pU 66"t~s ōm5,'<ӳM6Ң^:cN/Qi[ˉs{p-䰛`lλ*xIG]j3{etď) '@˝?joyoL</렯k[NOwIIm*{ 58=,Xh4&{!L. 2 tfecet+ ‡E:h*:GmfE^l|w! x,1BVxF 1>@$S+H"7W 8#9NC\xuhJ5p^н?y5|0إ7! C2]Pwqd]m=rjOx^:cNQi[v'Υ>MA7#^A/6 'c<|3|*"9 r M`/h) QT?vYger1pk3aލU-j{+zn#o BiT+1/@]IVNz~%{ /? S9냛#o=2cZ}QUh*n#B#I-f(-hk iz=ढ़-/_u zNtU&r+UT)|j|t2515673Lf'd(- ]W% U%`S4v<6U`Ⱦs>VVs4TV6PY1w- bأ窑1/] -׀k=X{c-dcҡ?[cz4s޺w'~.X}2{1,1tˈ6zb?q{j̎_$Ӳk=}%̬3:$\c:;^5}:dd5pti4yWլ\9>=(Lfӳh_~Z< 2C<_1 5Y>1r~K91Yo8ԏ<1\++hglhF$Z-;.p #%&Œ+GDƈ71麖s##SNTN8=>3椦&3sbj||Tv9bizc B}`p[w\ԩTKf-d=: !M+le¾ؾJ+OA7Z폹 {cj_;vL>J,rx!b|R=ղQ*sr ^~ }GzoER>!(ޙ[S)J#FFgAˠSy j|$'"jFeev,VK_iew:3g/;j~C<;=Q|+̱J,!_0tZrwlzOT('5xxz02D)C1t[vpWʚN^>DD}|/~YMjG17x؛R{ W@44 մKr]XBEiI^FK50#")~m o5b󰱭Fl%E_0Q9ߊJd« #>u^ڄTv|@iaKᕺWDѴ\+[]TwNrOڼAe›o6ߛoqGRPt?oe R*ZַD}_ʾ2ʼnݯ5RCשYGzAf0L[Yt+Vqg.uOaA?Ι؁E%ʐ0sý=T@pCgklN'AOjhףQw@O)KN? -"IZJ$K#Y2ee* ]^WW\$P=l*lMX=.g\;zx/lexuvmh3<\Yߺ'S5xTQ=:jw[a$KNrp$QЅ 0%7F̴\rѧ<-3Nfez|cyI ZF~_9=Jp)aU% eѦ9:A(QFͨ1eUyǁ))m?L#(QowHtCIap(V٥ bׅ{2]P2qT:(d5M,O5$*:=2Ch{0 zX[(r6zl6blѿGZm^XPR6>, u8Z؍ \l"Ar={^H{B+Mc[Xi5Z{jVJV3I >TlfXrgazUӱfRV匶aƈWxifhXDk`ď6V2mGLy#Dz(鹑L >T)\?ebw39;!_NKB-/W+[EF^DsD~NԿɷ"-YytU(jj;5>j;]Fјj;FWT4#]vNV0}TB\α`59NVMzOPcB,qGr:,;y}nkkO$|f82WHj#"C-G `:+;-k,֖EWI^0HZ^mL'B^۽kU!&+Wb#l-l G!C8Vʹx2ya! NiSl\g9ȝ^IGG0dyQ_`7^:c1̽B xE 6Ֆz+Zu<++(Jp,J4K,'֕{f6?AeFfƲ85d39DU=U Ti*7vݿ[;һYsb ^O\)*"MU# 6t?-\A^R/'"r[iP>h3+3#_X3畍 F ,ɖ=Qt)1 z WGRt Gi[yXyw_mߍ3Yd(5XjڋNx&SGʝ8n$FȇPTrR$AV`Q||&d>,PiD(%|c7\x[wu*-ڛƦ9x r?3ZYNu\,O=n'ﱠs&v`cɷ"~ ј]1󩹩^L]DS/*XWN3 L L[Lv66"n;aCzWԔpm o5_c[8O|x$H4QfZ@?* _ ^8FX566}Wa{]gmoz9tYz8zDnj9KE_?wJaC,8'| r/nN7&s9dTQ\aEqwC=m;7vhV|8-5&#V>?9{($Nɜ&^^Q;VtJ(@:woJX,Q=:c.9;FQЊ"F/gAjӥb.>9e߫AI$ Z8T[!^A3QU 4j k9$wyo*Uw^ ׃A7(!⾒uJ(`V#f5D{,&hU|W},b-ܞcy%.>r5J]7+sP™+o7FKc:N~ȧ 5.fSr=ܜw,'yHI/Jtcd' kKGLj8"a5F~2ّ)KzRGiRXj457>3;j x"2VMď5V_ "hp\c*EXG*,mگf|-܌;5X}4u=7[_=ZT O&xLrMPy!u\~7O8&( 1]ZJ?` C;nqidiFi!rӵb{Vu=-$ޤaHټ yb)+0+ZC>_BpOQ(g g4{%7΁>M81K}YfK!My%t%C9GJDj u5(ep00#/#jqGRFm](qƾ3Es͂_! ,hҽqt}{I(M:EFQ|-}5Ax.m֫ },x-RSP*;d/[Cm򋏞Эc$8[줌{loj>,~<ֱa|D jCh ]sa&hp6mh9l|̙Y3XcBHj>CxuьU]w5∢`tBYXY07.^Tfg<57o /": A-qPs_)nK)w _qƛi@ m)SoX,$@,ģIo?siRL]2(Y֞;\&ZU/-j.Irt!mpJ+Xe&Ҵ]Kw$=qE8.kKW[v8k[k]k5^o |Tv x:W?#6\uGl#g.Av?T^S~}We% x rx0$1|4z ^-u"xlLE50:8e%uE^78@^3Ǡ7\^ZZ'E/A/+-׽0 ^S#0LI@qm)[yC2P~ܙNJ{iЩ]{fXA';Z V~j-MxxڿZn-~^ nӠM p􄲈N$lMb΁nt뾨IBl=>> Lע 3Nr"0no,˗ݕadh䤮^-5Yvme5{-~JUzEqd.wCrHfoB*_۟¶/ݓͪ}` oI/im*Oו3q׳*c.̥"â#>.E ,XWWByu!tASʦrޘ"+Pq]h tP_.5 ȃ[\; ʢ9m@RVݡelnp-Р뮋*q{Z}pw%rn@, BY4JMC WA!K¢Af]f$ASb6#.\^鰪5Ud!{X!B1~.t2x4MB;{9['@KmD=9p_~Ʈ9Nl&A'e'AOR2!F/ 94iq]#' AcB6D;$: <Zv Z;vx? <Z>T&mށ&=L'e. )aړs^mY>wk G7zV %LV#A C#-LPLB=i ( 1 ZA-満A_oRsAЃʪq7ؔ-;U\BzLL2hu?w#? V[_S3 Ĝ Q|CC}B}cr&geM7LIB|l.^hB<ԳJU޲ KIAw`y7g$8  35wxtf6j8|ST|DWܖXdRNh釂8Rx$ǑQ/@9K'G50߁mmxO"јovǎH,qf4|T L9Sj4Pn @)@aP{Cj_4ӥ&bbP_%T̶k(5[4%xEEs0Z fMw;߫| bb_O\CI5{C<lnUѓ/(mJD3 5C<3H-Y`] "{jEpd>utZܢR&i'@O4_n$x$Ve;[./+#mB ͜ ]^2{OA?U~TUK؟ զ\˥|57\K+|Ƒ%`H-G$QNR+W/D}CpfLdc/XwZ>%e/O;)dՍ $YKvkMսЋXYf~,S? :>^q'wݢk[lx*9,guEpLKg<2h)[#kԑ7fѡ7m9t;7Cʶ @IR)ajiF&ATf1\ud*-`;(,VsP^/W/Wv 2C2hNz[pH])j.= =*^B,N[Co砅Z.mڹ 0`:hU \`7|AЃ7inG 4kHOMF~;](@lt9\JPmVUʽ5\|Ts+9(0<+35ú:B?:_b9(yҢC[GT١hIrvx +|UnG\W&*!>Ns(g%f]䐋(J8(m6H7K+J=fȤWer\ְ$SSڄ+e5 \- .tṆսd5(o J}5@>NȔ\εWr;hG)"(6&TACGx\{Z8=V)HR7\dSDuAk92Eq'b- \݄K@`~pg~gU9hfDՊ/1z1áQs),YeuyG$JzsE725]+}.qC<j/V|T]9Sˠ/k~t }P E?5!Im|y 8+L:Bx\4Mrs@!JfԠh%#oהp؎2@A)z4G6I9ԩS R27w}O/\4&x?I~ԧS7kwuųs0h!F 4<Õ 'r/No)6De )+ٙtpŚ3UܙIDŽ`y3ikW%zHa@tJnx&-0Aݍ$Oؖ؊%KqG\UZ; ݯa(z o"h)G`54A.AuJ`E f.o PLs|zvO@OY}/\ɰ} #Sd1fUoT8gn-Q]gk9Ngq99Kzލ⮻ 2!hMMuY+FXlM&c 8]R,k^$j3K(p }C>z$iEC1q\~)D"U *{/7+ڶp!>~M5+7rTɸ8[[Ab7X ;Hv܀ yx3,qd<G}wtZ[|r*-#kmZmqSX^ϧ>0:u8Zxo:>uaV ;VADumlzjץU$)IzWER:1 'xol ɕ'$_}i kUԨLꬫYGhu ,xĴq;"`gژb9Qd£a3ycJQaĨ05 T {/QQabyQajm(0qDi`P_Tx+sPTaWRǀ+{2 j1qHu 8`x<&{jt 1`]eA?VꂯQ1Nx#&vrC2qISQPr1`:&hSZcx [{Wc2n t޼  zqiw4Yh.ʯH3igՅhgv"2bx QsR{d`V!kc {(y7*lUȔ*E5x |Z}wG ˽;[va.V> ʒWP+PKUU+CI&| \S<4fTm>57܁DԳkq=on(Ò!6m"mlNK&"M Τw' 7Ոй>v{k@1*>MVG}U᭞5>LTs'_i_=<[/‹'v1kܵ~nvRs2>3V=RZC\dkc#t|zE]}9_[ƶ1 E_ %Y0VEutN3 hoyJ5r!VwD݌%$#ta28_+vJUi+寢[̊Fjg-!S6;SߣXQBEģ(cEng<#%A&d溁@eQ_$^tLwu|{E^V?Џ)dpF<ts7bixhlZNxD c>,X0.uH3ҳxDP[4;i뚐iV#LK}R?K;ˆ<)QHA/-d£|&@O(@}_=iR?;c\ `xX⺠6q£z%46ՈTO5Gj~:̀23@<<@_xC!G)Y)|1B!ztRJIŊZHXheB\к !ŎQ (o}P^}Atֽ_żv޳rlza~Fcϋ؉j1:Zꮘ!taz }/KEs'7dvvSm!^=%ЫaGw>3=Z=ܴ ,s &*$$T,cq b JŴ"A,$rO΀VyDT_3.3 | Z}%?`+pgPT.G}3`t(;QB#z9ģ(>>PSOP B[%z3Vh \Ro.No.! IN/զ7j~C<-޾'lAg“lA-j~PR`?@Bh@DmQskk~ک[H'N'nMF+\Vd~@c9N(Ez jgp@["}ƶ/O!Ϸt2 :|T_]]>Vp^3П)óE Wój|xw{?bclA/^c_IR1C B5DTK|;&ߌ'eLJ< t#V\X-mtl]OLնx0ڈ>mso^5 Lk{^y ($B^VVkC!>hW$W1^fX4IT>NP? jX`2IYpNbe.%T^}^+jCOsغfnhbx # 5 -v\x/rezW<Jx!9cЏG 8('k՗5+ٮ؎)2eRTD9*C|OU+Iv Z=۸ٟRoKs҆Sο>^YimD0< ZjI(Duo?`' o*Y8(|Md 8f9닠O]!j h҈IxmJlhl2{ugZjq:\Ң?kl$|?Us!U:g@QLCB.=~-<z F?" ZΚ? ~FB4W J\'vF9ӄ!3=ֺ!3-dy9ӌ!3|Z=o͉$ѕxX.^jJgB+M9+ Էұh*%#-ZLL8zDgRh͎IxِgnB qI˸dKxI_R{ϋfxgKFHa#U<ػ=ޢy8T᳞2j.XE` ȹSZJ(I_~v?Вׅ!.T"m1sv^%h_HICvm0 5Ո;HDz*ÃrJ•j:o}`]]E=;o|9dJA)OQSL6u;9lFun%:`hK`zSE\?p@Fd6 -5g̖2<ς>OcE4ZL '3x,Gcۄn-u&vi L KǼQ`tS01!f =rցfb-+mI/sDӄlah\7P0t q 4qz?; xA@T5C=ZJxs(}CyŻ'lx6yCxw}&wj>Xtk x t>j 6huO68E\;58ELp NVw|YpCt#n+"HeޘEKR-p;cf#Ty5!:E ƥ`HXω~> 3[TRXl\IdNF6qw"k*˦ۻvH(LNkPoOx܏$d5 \- hL?"- > O>_c󐽑ј=력)4ZTUbhė;RU4061X΢E\2YP9hwVWdk#wWx!Ԅ0:O 6YƁ1{BONV Si hz hv\fzSᵂC++m۴I{|t@MKS5^~zns3F[:X\p\OӝkM#+M]+jT :/_TPa{x+P7 3\Fe;4 /bQt͙-ir!ks{IAލċ&Z=2s2;&rUr9~#hNhţֹh413Z_52M oV o9w ҄}Pϴ_ŦZWőz.\{Pm=3da١o M2U.:Kkt:s4邶CW5-v'~a[I-8g[tzt.c/(^Ձ`u9IA»_aZOO)<S!KVAEuXX.:Rʲ"s'QTTkhsy:W "8fnk$OO@A*v;u;aeMvL"+L;0*Zx`Si#Џ?0$1B<9y/{*1&VÎ31 rflqW9yI%(iw@Akӫ.NFnJQs)c*zɵDuw@AkڮDr\mr9KqJ{9Ia]QzN0vڤ|9ӡ-x E7f\9_24Rw{++ڙ_BV'Kˆ߲{IpI-{/t$StJ"&kmEyjn,ۄj)p`mF,V iw\fRx$նCߍGUWTg ^ٱ&7SJ1kȢedMwpgg37.T!8cx{loZ̎wuV=\ Vciz/5ײ. ]sQO 3co,Od/K]sE6i9RR0;Mi[)86\i8J\*™> }V~/k[V@j|4L9xjrf񩴌9c 4r<:UO{~\׹SU jOaAЃUӟ_ֱu|<8Ó*8FWGϿr;Rqﯥ9UP0=?X+w^oo>{y>djzjj,+r'NWZ[V<+m(qOBօO=GS^OlZ? B"lu) 9KT4Ȟ';Ir}]`[= S9 _y:5RcDhW%UtE\ tW#%vifR_bljjbddjzCv1>;>6N&Fiw49Qb>~Tßg> ;KB\>z"x/\b\@\~_ lqf,aԛI؅,3.",yxzc/lQ I˥/xjEFrY>"#%ІtҴh˃}d@s/4uԖsXڏ#%.@8c#&z$KMSr όmngg":sDEC^ /n }8+g/vU՞>qVS-$ }qO>uv-E{צd/u2l8.J8(쥝lqz槉lX(G(sg/KudƱF~+To&h{8=9ZS?)V~:=;7>=fW?-V>g?óg lq?hܷȸ?KB`~F&Lꨙe޸F|Ѣӝw 7F\I[=CGJ^tGƟ>bh!ЉiʟC3):"9}OO\~<A/tYQK^^95ZA 4v㟯2")Zԩ}w POXjץx*&[غFs!?W^\2l=r|= .{A_hf56QCk2L]7P7q^hW e18Hxx͆hULϤ$~Y~.-з?h0 mз452mSFmSSڼ*iWtV#®|ޟpLPG4k]nQm@NGc~(7o3LAja&.{Q{*kD|g!aYAXgyu{0ˆ`p2+._RaM3cXꄫt<_84e=KdJ1vLk{4/ܕ? W@(w%^fp눸k`0:@. jƻ$σ':t2F}PwǓ,=m1qpJ8zL^eny7[D1p_837o }s]3Q0 ٢ F @)"lGvK`fl#2vUk(s`sʬM>Pudsoai},^ڄ{'Wm=L % =S26۵a3Șۋhtzl1WjC<1]ΌQwGMb9{kP| R9#rk3혌B7Gώ4Zf?^}o]q,o{k631TNj؎R;A -x凮UaQZYתj=_O:g9#,UVqJ_S B4!#>ݧ$ hW g1V6wlP$ K6lޅJA!kiQ*FM;Bx拈Ns  ?]g)˙MPɡK,rTkM1YAmENKz];iڍm.-xU.(0gYJz7<S4贶{)[%c3'}%mC+BLA/7ҳ!E6h6Jʿ[/?̱(n0:MZ7&6rX&jd@iȂ?:Up +ov(E8JK${_}y:oRu4 NZX+RntQƖ"nم^ xoH}guph"j.=,txcX9c3g&+#N8p:yvEVQOdP5Z2) 0Zad@j2ps#G4p„W `4')}IN6lmZ:H㉛x}J+m׊V6gqy-gƾy] 1EX&0~m.9 =ЙqYG'饊Fٌ'~h!r%:?z'+Ոƍm5">^>Kj<1*U?XTQ]& ܮ>rp‎E!ejQ|q cE7'rtqy'NI0ݫdJXp(;#( ~2hӖ(]vDUε, "5;Z=*%<bGSOmT!m35YKRA~M<#z][OlP_YLaԫpjC>łOqG3ߩvt-^" BͶY0F[*VPcP;q6yOf6(1 vέ 'غ?Oꀮ,M[wDs];Oh$%AU%AXޤ3=ΎH칅¨?boX*!>p7}} >.q5n1EI)Vyw );,H%q"s@/);% yBѤ݁v:0{LUKpxZmݟ>-e<57| Z}B^u(q\ؼ óѬ]ͬm >^-uIL?36^?XPgLJy˿SMwRp."|٢e  e)b%vmDZ6]C`eIp,+–wT5<}{,k]%W1.#U#gI ƏWjpl+5ǀs甕,]ҧщoQU&nUQ|F{PRB.f]`x N;BWӱ1G#tc]+V5˿1:-[SMI~eE;cY/e~rBJN] 8.V^\ 欷;vά$"ڛP_o yU"|"-Yy/t7J T.:b:Ⰿ$W}edJI_*Xyk⿒sYc`Gʝ8XMi-µaHr{Mkj=.LN5~ӠӍ_l .^ԜNnj^ozϴg9wqמ]55w EF6׏,-%\_B?_ʅYDU*T$kډIMMiG2+rGlu9 u5aP74,:-gQ!@O@}Ίsz_wnZNfg>577ѽ]OA;tMp]B*Cl|ׂEjAƶqPUOa P5G?j ܦ.9A~ѵG7}C=cEM l)M|?+- {\;hDu!mƇǀ3rxQnj^!'7fɰra'3#t"Xjv3k\L g:;Df*^q/㡷{L뤞GAj:OnI,a|Lk ?~RFB\c/Ehmj]ۗI~a+1Lc%wb&S.=/di5[.Gh:#dᅦrYl@΍=UXDhdڒH-7>2 }XF#,֐;ZBRn&6kFfi{|bVR\t|e ETՋj _0ũh5e_~N~{ E~X-zFWއtѻ2v`z7G]cFBz*@x :"&ć M }~Zak ^Gw`#cA9:5371;6JϭM,MM,LN.,?ZYLgWf'}e?-eјNv~-2JsS5x׳[#n-cGS7*fCئ,8ԳiңKm\:W+ Q\z4f6JOL{WN W;7oeIpcp`y0"Hf w=K$`-."[q:0%xM3AǕ]%W[nlqWVIx}Jw9cJQƓl/ .^TǚB)ʎ9{,3jsQ,EFa/YrypbrJ{Wդ Y!Can!!wO_}Oו`%XŵglrӟbV.4{$Uu~$׸^|aG,Ǹ"fo'oAr)YgksϝvFkBsG Dc2 fMqVWߛCFa?_)"ɡDD1UAs"aQZYT2 ߹U2қ RЊlޮHȆ-s|R@̿I5{C<llU:NʾpU37lP9ģI0+iW34YY4=!(0\HjT5?!ICDx2cv~\OUrwY;+TT'RGvYߺ 1 S@?2 d2q;3a e}7 4c`ZLPitC?&TrjDa.\ n1/[ 7qvË/6~=vpы05| ZR7u(G:em1(N!5jG/j4}27r2+~MLu't%^?&h35K RM.V_ʄ$L$yiw9oC St\P۠R4p{Nk7 5蝰C+64Z; Zn?NsH0 :]Tlcd) q~qM~,R;UC\ozP5إ tJB,-u;KHIX .C( 7B8aȌ\J]c;QV[͘Jj1| ZXcAkU 9u!XF'f2 <%#N F8p/#%mX+4Ո5zմJ?3>jl56*~CxEҼ]9u(l ݚ=<O YA='y)ELC!P"`p] \ܝ] 57HM = Gk9 sl* YpǦ+gwAKOhj(bub(fLk[Ž$15|A a"VbMZEԋP /$ )<Z۠-DxgPpYBb[b%$6!&Hl8Z}?$F"+.rzaL=+ߊ< E bK,BەCFۄmۤmU+8!"S;~ӧkqC]x>9RxTh&|->HLT&lzgfGvmPJ/3G敐%h #I:scDD[_7e`Á39+kpW*aIծT Qtz'2c>궚R:TJO-d5Qk3ד뾝[լ{O쾥A!s`g@_ CJKfz).^0QA2)hulٟq)AK:Nxpl\^wj 1N䠘az³O0Pz7[ӤY덕9e`f.ǿvhok~o2bJxI4ѥL iJiѾ,5k4KHgXɛΎK)Z\5]-/Qo]>jߑ> tgcr?ABv'ԗ. f-۰k~r{R]w`8YH3B}XY " T?+"=%.Lkh"Duy@W%Kro{q0Wʍ7HQfVOͧR_?X[:sFxTUK NEU0|dPZX䋚; sAGD%s۝]3S~S"ek cbT"[Yjc/!֐t7>/SV"YW}=?* T8~"3.ޔG _>ԍkBu_tU}ctǾz*Orx'cJcD̕"sG~bH(I 4VP u}]xcV;rR"uWWWN˄jy2>A,pt#4*|Z棘Matk19.xzG$2 ZB Z3^;}J ) vZl >6MKZD7L8zLIKS9sM8pjM}:h?̅3aG7F͞,3h:@=ѫ>0%O`p6E* €7A˛.o1Pn'fDno,P$Sm:hyש]xԙ!jn MgoN4"#~fQ<`K1I<.:(6&YRLW()jZ}gLf b@#uuāةzwvb ;UQM;ugC&A~YxCdޒ>ZrkO,juU q],8ժ lh=n36cЏUKk(XuH~5f$6edɥ5]"7.״1B]N>nFh >uaR ;(^fW$%_MMFDӍm5"0O8Y%YUT$_Z>KzUT,YWWY{fv |֖kK+T]_ Ԡ+T#w.R)]|[tuD<[kV{ G(Y)Yޜ)KЊRL &[ P6TkR~kה" lu=aT9bCSGj ƛP^ I)EaoL5w}Ʀ&bR~?O]E?2>v溘pod, @?֨_cRdAzfxTj>K1'ɭ.fmo=@MJF͝ROŒ4`$Lya[,%L8$'ZLGp>c8sռiήI2~Jbպ[cf4\"lv6J{E`cUg[]2E͂d]&%ʅڏ#TA(1k.o焏c.uUܐ"EjC2|P)]h7 -Mw uggA(=o>uaD =d mlBzjDƶ_O5֫aGwFnJB|~2"v8ģMpz@X>UncOB2ЍQ}6b4:6FPwW/ML'AOjTnbiæHo #ee,NH\<>ݪ S1M0`pczqi/`R-,`Jp)֒ />Nnb o=@M*`J͝Rgh/`V*7ћ݈ @w@yKkT,N}aO:҈?ՍROrx= <쉎}ıO͝Wqv޼DܘzGeWt1{cKݰ]W8+;%j cP_ zŋ#QtԮ!3a:&jĮ!v;]3WxfJCHhE}ӐI37+ȕ]MrMg,nv)gv15cBIjLg1iFlХ*.ZtP5pF@+m HLg4jPPk7r= O97a3BS\u?>~dC ]YP=?v5?|LGO.7FL1)։[X?ɡz8`(2tL I]2ݜyUI?~lise^vMwTRXpGhw":uHF&1Qe\&Z'<R`G:eJ+j߬C<P2o[UuiÝ<:d:wTn AiGLp># ZjSEd8z\`A-@K@ gK#e#R'=-wssXw8xj3 :CE`λ-՝Z~U,kzyW-aڿ!F]dbgCigEHͨ)}rOOe-eˠկ=**etv]Dƶ ++ | Z="0^økVWD'E;?ㄏ@/ 8U~s;`߉lh:@=sr0ܽr %K7Y2hG<)\~LPBeO;YhINv;_:C:;r%zd95 zud5]wF92 qcЏUK.:wP'o?H<}: +KVص]Sr3'WM5eNEx¡Fi_cծ?'ڍ08䲨 KnTOή;ꗊ!O5sOn/^PÿꋗTR2 X5Ȧ]ȺܒM #EckI8Z.J{_x<ੵQ$h}CٍR(ABLWA{ԏ*%&/{!87 Q$AAc^q"yub:p2CM Xԁf[{u'`&,o t6)xi}t{k n&?)W1w5gu\oyRU#VA6ޭ.h,;)R/p؝UcNx'o@Cec[^̙y׺u Cpg;Y4uun:>~1*_RqtF;[.yK7#؅֍_ 8*OU7m*-wFuUO_RA<-x]莛LʚE>RŖjxUfs\JheAO Z`?'՞X= L Z'pЮUf!{-Q"@/&rz z%ЗSC_tճx@j0MUGE<*aj=u=xY6M:JOP}\ E3%5AsQx9'MCܝ/cWz@KFȡF &n@i[AK9I’.^`.[c8XV_ /T',Q^}AҼVb*: Wj"pX]RklI>E1MU؟s^mYW('[u&HH͝-F:w@1 ׹ *#&v9Z@ҏVd-$?i2hԗ+uC'PXJ҈FQdg< {1/xiaTdIC {w,%+bs$hULljFA?VjZ}* &nSȱͶp+CE͗빽A<ވ5 t8p{EF7@KNz*bgC`A[㺰&ӅJ ƢۧF:U=Mk ^蝪gr*>";hEz)ЋEҌ}!?.‡˥ھam5TW A  DG|(F-_~^dC4_3߃'խMϼ j*~4+@_PC@qpA'6'AK]v8zJYcELM?2s품Ӡ\덛;iPkg2x D)NLSߐ 9.|X=\6$ lwP>}UkWPZh܅鹜P4 A GAm(&9A@F<$1K8q4 \wáȩ3hxkm StL}3S)y != Cx?3(Бgal^}Z;Ru {E 1 GA9ؙ9s67guzsBLL~]2_ _ +$a,WAt|5(ljMi7$O-| :KvŽ&bm8!}:D;b1ʬӴ/l:#)i`?Z'4K MŢzT5n9p0WjDYu]A? B-U -EA-$v8`-fA?Tdwԋ1Z]fbQ 7"p=#p}Ⱦ-KAub{4N{h;kۈz+lo3Ѝu4p+~Ό_ĹoM42ɔBꄗ@˅0xƊKׁq 6 Z*qE%9eۗ?StN?&%U{xIdhf~w']LٳB5bg%I1FZH#nc>;q)Чsn`hM+1?oUjRˈ!Qd?1L?$wuZD -m8]Z٪_ԁWqo0,oWεn3&W臢>1D<\EWcO*|dg\6ͪ^&};']f1XrJJ ?C}rхzrW#gۢm9 ϒ8iH.`eOk8-ntVP UN=Q d,>{9<^i;##k>1cRM2:b3'6pҧ56z0Lg6U\"mwdJTbN[jDc0=>Nz{Ob. X󩹹9G2x:pӀ&}c_fxw(bڳ1x_zpEsP68FYمxtf^%N%_$cFu5bQ[W[ OV;kFjUE̗%HHC˕>4"[V?2cMϊ^+[YTU| X=]w݄/59. ]9գ) .xU=/cXȨtE6i9RR:( %ّj[jce&$O̥-)Xg7вUk GS3Nͺ68SisJKBͩ.hxhO#;Ҥ^c/;ӞuTUb]oUr\{??c˵Y}xZ"ik ;}Z/׿)ZS~۹]R~wVn͌N2Y+}te*ƌT,d]hsDlϟzx/' ss9N %s;jQ2e$ٳmTS?^GYϧ8"Sc7o.S#[~dKv`/]HϏg_.}f'ggSsG"tE Lz DžE ͕Bd@3/:j+Όu{nK~晝5sUnL\#=16wx湳ٱT#%Ou7Jtznd$=9!)5>>35grvb&3G46XOKDb\KF-z~0}tV}k,ȗjU( b˾~j\oiI%E7zzc4sK/ő2(TL%_Q1F9d9{{HIݳ8bhg5yV?1dzV~~Z<+zGճ:vVfW=롿5r{hTF ħ諞 =: VEŻ \5!Q].\ĮQ n\?9os ZKf|Z.yiDWFvO%R草X0`rlr^r^mrN{瀠W9ƮJ}J}]k-?Zpu-וTx&~_0NiMSyޝ͠O5X%%/Pt Ca9ٗ jݜFI̎ H[Cb4͎!,~Oſ=aN)!f ޯiUi;F4MxkmܥݤUuWa0K( ٻ.Oc| ~G&-!}@ŒQBrGjkoxN_$l Hz^$fLǡQ= +I, ROUwq& . ߄}j8ψWݷ=$nK1+r XF70zcqot|59Fa}jay#S銎2}D_0n_=KW<>'̟'z*ݎ3G?M8PO;) q/ֽեCk/;pksgkl2i5>;98NwZ%us^*X!Z_}KjVOsOlz:>ƽu/-ʻZ|c֜8r!جҺ&Mo2H$%g&VMo񗉬\r- 359["uŒxzA<߄bJEtȂ&uC E%lC)jr读_t-{[og`:T?8sKtH3tPH pL@_2&sIbLFԪ$]%rJ7Yyur˙{_WBx*ڨݪPnvkKVmy& ̛oaB%&ٮ ͅڇp19BjձӉjkL}'GMb9{k}R!. KbȽK/m QHF5Yr\ګ¡X]*~cc~^#E$Je/}{/N2`/x:.jr)tEw_*e 9_O#|P}$ n^-0rViUf *GpVxɰ%tjAfjUcAC^Ӡ5e26q"6|Q⑔][;(4S.r܄*C#QD{B{ו_h#f ,)!f~ȚNhVSA{)Ae|!I-;S,ēQ|-!l-/s>fm S"Y? )|_PUU"] [r)97&}_]vy;p4b5kG8#gT١0i񨪅 n2>Zǔ^ J!qbaCUv [u[E/{}8 \&h+L$f1cQ}i*w4F iBts $>H<qkn c͆ߡ.^TV!Q_.^}d#N;]Ń_Ogkz#gHw8TC3ϒU(jσ>a{BxeYs(JCI .J3|D-hC<p_P}щpCa&G+[%gSu< 1F;\53t^i4bEH9>%eOx3&O7=JkΟ}n^S˲wG *Ytm-_DIfߺ"CwD;rܷ=_(o̢CorX(evۆUpsӌ6@7+3! %(+ԗ@+_dL"MOub{%#GNQsq(/U1eUtKqWN[CӬ%w@n3 e:^s3^ \`7|AЃ7i oG 4kHOFP(@lt9\RPm:Mky 1Kw Ie>΁k2΃o2S9ģ6* i;K"(ա3xȩ.&U G\NT.A ~s'@(~e<^&\ЌpMC<֫1 ~ ׽Yo-1={qLOOMO871yeګJtJ;NOguZDtJ\8lV ъDŪY_LǩHWbh8z1!fh8ZFh 9Yh}o RK3*ݢ@@PH&@<ژWeSiE :BxiΡ@ x9/UnF͗9IydMivju)z4^G6I9ԩS kMS&蹜<' hPNd5g],V|ZfnvO9S.^4lٹHWn/))omM:s927xYFhu&:y& ZwbE-@,pQ1J%qlb쁚*{x-bt/@36fw/ڛ$!UEb4qG}mPHz#GLԻ'v&8ԛ dE&l&PS6)M ͇hvzE'4j~C=iJd1h4axV埸lOuh&n1z Z=؈`93 h81#{͓tr<X NpBçkjmCeIz#DXx\ $g TVěNG{&Z',ʲIgk'Sop)+y_ԶP :TXYTň+렯7\Ũ ARk1-bo={RhnGA6ߦ8h@ǛoS)R6ަ;{yjy8z<5 ZDZNֽ۞IlVU-x澤I1sRIpp^ujp|ۉ,3[Yz(%@kו'ywΑSÊS9fZD[Bf7-!O)\\wEτ!%_O,Jf`ܞ/ N!Eј}SĦۃ3P9obHl<"ej/ 1*\,Idn' O`s;%kQXG}f-JifEn F̡> j)xZ`ǻ'ls"Tmbx8\7"h9Gi@-<>PP4J&Šj|$VAav? +ډPHG)wbt./h%Vb;> GA˹>53!p U]7᧭hYU+f Z=xYh-Qy6.^K)6Œ!6҂D]j.Qk@S@&T0p9V9+3#kk}ZV@+k;>Ku<"lGZJ;e-#.@<- o;vğ|+%l:o}XSJ Qs ,VUBjvEjp֬%hRL$YOn*%WA5EU \6J,tC]΃V5wSQzz #AE'v1keev,w޻Z2AE{Jhݽ+b&ׅ2] +ml{ Z^¿dRdwVƶq+E O/NxnTZG|FY27>ꮮ ߨjpIyG4v;bWXFEo;8ds}CJՉuL%݉h6=,`F6vYI"4\ݪ\t(]"vNs؜@Z'PE ÁBt~%4A.@KNR9#yK';W@ !XQ KW9N"i4 D^a|iq,JQRbx\r!.^sZ2ňVV*zFn5ra<ҁuNeO^kNrWA~r2e%˝_+J2֘ 'K6mQƦeΧf<0G}<L>Mh]XOs]HtA=̧ia|^|X$PiPFKty_8,A մ;u>j :~{]=:x!G3I؈'Ϥ}z%T qC<~F4'QQ"mIiς>OҸȡtH$4ANnX 4CWqqeitx$rx]}Y(x!p @.9K\V_Vq?QYOE$r{z]9w;+g4hh:@<-1`gF0'(QKb tƙ!"|Iu?-q > r#.gIX~J\0Mu\*yVdQչv>r 3O@/_@ @)e unJE;[HEkF @˯*֞zQ޴ 陈7 \VeFAZOzdV["Bᱭƶ'?Ӟ<(;=3*>CNg0Kaӝhu@t^[i30}Yt'fhSs@}{2tEG<]/qz#]{&,(7޺7=о1 SlRP03_asu.T~HP/c5{FݹQ UY FEX#s>MStXSɘBa #M@̎Qsq4ieel5ȓV Sꑦn_ev=%ݭg̿.>h.ϘSk̯Οۮw*[xމZ4zMV-OE`M,Q dvETr-OCر6T۱4JVד/0{b#,~#tې0:hu˸e j+A7 ;@7u| ~]k-JEk|+ב~nk@:4ӑ3KP~CRC.hgc EWҟuXӢo? };D$$sx%0ɜ#v!Hu 8zDp:7,ۡۍU(Qe\ξݱ2;XD5q5\<,OY=-h[=IJE^-5; -[Z?p$OI`tZ, vf9L@9~ vGyZ% $k- >p~ĚMu{bKyB/r惞xqCw9ݢE"W 4?#; Eͧ ]:"w-8]ȕ`M~qDZRs#ERu)(Ku SMYA}CRPp4r.f)TWI7C}.>*22I^-wIֆSοXo(}@yLjE61jmzIY)e hӓgmKL*P6 $,%ЍOVEb jA^+VsW,yc ١#%%Qy( =raՃTf⒨A~pֆ{xY'%xxeL"d  QEVɪits |D=<㝕jeZ%팴GZl˒dٚ%{m)yZkKo-ɞv_dLDd>2"~$qνs=G}]PTeG?Rf':Ȼ\?x!o@1SS P[1Dy4Y)Z.>WJ%ddEY^QfT+|\Ѵ팹ΠLi>a[01s8i٩5w3)MQڽWU=tE58> + -2uiT CU֜LMl}3 u"<Z~i.!K/_D$D3!2*ʢG,[&˕Ke#ˏ|AN ^>>ȣ z%LMatTv|14Y6DŽqqekIWw$S37O@k vOڂ P43akBcሮo^-u|QǙG){6Nx*}//-J_ ^ԷS-/ Ǹ.t}hc#p/PBJ[Bfm5:>&0.W 'j =H;?vYUP3grK٢gbL}[,8%4֥A/+K#.U Pߑc *X$j9MS Bbg4?D~Z)%^8΂,T␇ȒBK}\V1BV?dqD5+A@dsfs&;8lM4j x#v7}yoxԉ0}K(#`k2փu(Tn}X$8eFX1,X( Q&jx \ _׼_8ƐdojtCMaOx8 Z*5h'4Xi} u xT .\ 66HH!w$:%d5#OO#E'yf1Wӹ-m#y^\}M WŁ=KI.C8/,L満à%qmEF[(oLRb0:my5m_n7IH&p\~oӡJnXmѡL,gA3}L[{,mMqM2XD38GEeozbxڰ/[D٬[CsbS5<Z"qNFK{;ZlcOT#EЋmZKФRnc.=~cφ:Zؼj~BT |mlNwFg jc<;W6QڅBFt&e*cwZ&&\b8{?SG1zak2|Q3+_QM\N>eUjdI ytyQk(K|ˠՇ&~ދhrYwҟu#mRPocQmAuVmё5u׵+ל6̒굄eRJ.,:ć8ʑG|Y r5D5͡ 0M㩓TӵZ;=O0*ș% u"~Aߧ[8|i!/ck. Qk]]Wj8z@Pz[B2AKH2 ۔=Xdfe?N b+1b.1#,F9&@17k%<k .{l'iʧY6wFXB[܇ğ -Jzao,{VX'|Z}U%R_Zw[BOЉ4COЩEɯ)iЧHeH.wP$gQQu[Q*!0 J|`k(Ih)_\*q4h[ .=~voBB<3e[u.m9vV@ XQ "rGQ|2!]n6?htK4hXlQs}3Hw1NJ\tvea/|ѢVxEdPА)~ϋV~K/u(*hur/1s_A_ַ\?]kirM͍2jo0&Rw$:3qV‡N.!jr>i+'[7Rk]@}S9ĥhQ|ڽP,!1rZM-b'!*=,~gx.}qx>MA.-uGx:lֺFu-t ?lʲawD 㛸.^Vr݅B8 zRi(,Oo|Ko;6iNp6-Z# }b8p`Zt3@ɾ ߌxRa(x!<tt6YϘt`PvsC,toO>QdcέXl6vNk{*gD{kB#u<=_GiJ2|#=mQd]΁Ӹfpz:r1@/pA4Q=V+Lriv~졙rfq/oM26I7B[T)oe,uEka4ƻHL-2<2=jwEeN~҃cjH>>MOT I%XeX{'La5+YK[m  @=w{ricrnñ6Mfo|Srb̜Ʊ•`aoZDpyQN܊m 9{CgŸh_[pr_̍EpJ+mG5jnZ}Xfn8X>~?J|@ν<TV weԱN)µʚX?uL*?EBOH~mlu7("uPpi뒐VC,T}?ub$ QSU6sCA]ߣ ߃3կf̚0'̓Qޢ`3]K>جp\ZoV?W*{ifPۅY8vv1tH? V%nlEMfl(Õ6.y|B|σw"*:@߯ \F@#G5|qi TLɘ;>jIL/p]X邺IѻľFxSv~ m5'B_$n)QX^܁8m3{U18E33Ag+ rɞ%̬">²X^HhG1O]\~񲏱aL&@)3$A> ͚ rVa|g,{/ _9s* | Cع!ms] 6 +PT{' <8;T:4VU&uR%-hO hnԲ=3ir,n[;Ώ  :8, 5Fwu;3Xo#-s|*^.cmnr,63lcO;]Z#?M8Y,YK7dV">+Qk!JBOV{&.$룞G]3/~2 F!37+p ,lƭ<˙m;&'? ͉( 4ՆFW&]WŧzC4x'\d-wZ9[_#6e)9!&O!#<  g@B卄\pr*eȝ܄)nᙞNnL@>ŭm[%T_ qC^:F–@j4"h!n W" 9$& @.Q` \U<ӎP@Dk,z]&#hv zZO'8ӆJb~=ᵞ8pMZk|k=1:Է/}Z_]؍-LBjD&Քy!&2 hi_"jI$K +:B@k' 0Pɟ,tw͞-YiP8v* *MX4mlyd%Zj[F %З3tQɮ/'bp *4Pk nv'J`3ˀ DsC<-,6hn\1;C+(RR榕(Aw v.lȿ#:nr3$Z t%!A15s kklWt 啸-uGlygAKU1s_J_ι`G+,;D\^wAlj%.Pi:G0 la<_Gbi 8z#6/v&w@k,׉lW["Ǡ\'j5C8 ;aQ #vGAo֪<n7>@;c&5ȕ,h<Լ-U vq)A1Km׈~0ary#!i\Nρ2t7hy:gu#h"hTu!64a=cŗybpxN!<5ZFSJKaޢ|6S28 \-v; i@'ŠͺȢ=נOx=Qeɹ'VH.m24E hV3?*ہYгʂ;l1q?/<*92ee{T0w /U0 "zU>^ uW̉o?r=]K[oS8!nh=%u\E1ꕌ9e\kZȽƋw:kԲ/nVy Y3ĮVC2yS63kdxa$#Ah$dۗ`oVڂ|f@^^ =*BP 3oxRz;) ZggHbj"0Z=T U#b) Z=o jT Uڱ"]l[Vݨ4$C@K`ȴ?vAO˘K3QD:]4&06< ZJS8>)y տ qrxBݹW3Z>t"c&)؝O]A5,WF ~*J$$nV2~bN_'uӬ2 ƘgCu x pdcE)R^t6aD_p] }RGD2M66ׄcjy,=:(kn!>}?=fՕյ;u:->o|g@*R!$Q+][>9+oxj颚̜Lv[ҫ/,1DZvՂf+4#E6zW&$ {@gR1AI-8z@o8*Enż38b40Z)5O톷zLLA뫿PN rdt H=w E&M1^mݷYQ߿驁7A="gOI,hu_heƀsׄA*a23̄恫6-`3;A /a-#G=d7a+¹% F6*:CޅwֽĭxbSqɛpe}5k>UIշ+"] o}36w"oo`m٥†{lR,ܚJ!n>CN0>COr]z66gэw y62=i2djה%hm jH8>zz?̓@  | ɺ*XsHtw`ډv7:v'~XJqEJ"ݡMz|1]6d*.ډ;AtC<f>aI^mEnޚTTu˕Kj};b^mqAf` t6{`J04=m 6HA vO>,]4HQgBvO{ᓢ*E<^S,s6&pYs9.c'YR/zF`4lM 3#zmskգZ:‡wu` t>܃;faܿ\Qڶ3aEk9AGrD?4D nDg6{^ŒҮ FMgx淊6`9S>&~[B}N.a}ic/+i|rI1Ja5G'16\- 6Qs CW PN k劳::s2Myq;P֑^mV4gx #:d'dNXi#Kf#.^Rbލ5#X%]w+CiY2E| )=\.z&]"LNjӫ8VTy}R\ &Z*?Z:kSmvVIvljrQo=ez;^,eZv]]p~'ۢ]QDE9YP ;l94RwqFG | Z=챼/Xq%KyhtI,-f那dY jxu) od(ph72 ⣞̭ȸ( x&Z;hhtb]#G<&˒xC!=k_4 < Z*X@Nq(]]l;6xi_a1#[ڶ[%{cP=m+m\=>-eO .V?{^5ےNqEBv݆ΒPoC;js&se1 Y^.)Ną7W~Y3E;J2m8[tBd\[Zi˵ڽƲ ?4 ف\Qc 655;>>5ɅK3g{&E]Sa~"kH]H{,>\AfD!v11YVdQfǁGv@k#g9 yq/mgql džʹLc)m{y{2{t1H%Ss}s1g*_L&R-} t^woӁmԿhDّ}VǑ4h~7Iwއ]?}tRַrNM74v__wⓥ ??b6_l'fRsl٬>iu9ڴf 9074| ;NS㲗Y=p$>lZ1rLybk  V9avI9ScRA+?圳/2MYŒ#[6Kgϫ_C6M+ `myC-MM0qt4R=Js~"9?Ytvd<ޯR2|ԹN. 8z?,vn{QBrtБGt:hbS[@_tE*~fzi^9yi-.l@InJ0iȟvDǧg#z(YX9sb2935&jϡH@~(>Q`4P䃩,w6e.j \ot#0I=J\+#?p7h[a]cR:VWm{lr/Æ Z'5g+<7J {aWōvvvqZ[4(7&cygL/{F,&R dj&Irv;vq̮ 8pF/a]7;v  otp[z`@3wKpN=˿r3[6W`/͒gm~808X{W&Y%?6bStD0EG3*t30=~@^Pzsx4WU,Uz$o={x0]+s-{#k\-fa?.;ETLV{y-I7#ӴYtE$$&6f?57>["Ǔd,;O_LèPшW&p4U>Y5߿};htN-{y:HܷZO̓{E:pS5o;`&Jk-2[ʚ{ ga.8})ͧq>d C$8=p9֖XjnILp>\&<|59=պF}mlQȕK3S(Iٸeόc]K,5O) ~OLr|f657?k.9?WR3!:^wBCqPQhVdy?Bkϼ k^e9Z+{*xoKАqI}`xGdԧǨؾi=cMUp,L*[[l)od2VAR=L:Pl[fMySSL.={z!jtb;s+ π>6W=Np}ǩC[Zf.I2~hX2KcR5,XW>eՋ AFRn/Ca)?qL|ENgbΤLΎgצ7rr(\ċ$9^a7n rϾ`߷ɾt@zA3Nٺzʗl`wR l#z_7DU׭>I|in.[lБ=`h[ٔTmt̙)ke&C܎x?a4!=^3߿ wƶJ7?a+ЦE{`O- q$žm[po}bgF6k nh+apε<; Jp7^!{SS m6i;9QN>/exq)ۿbn{cEgE =و>ޝL9MM,ga1 t~<@_zLCk;tg֎NNWb@)Qv{dLSàK/U[ c|x9^!신Y2 3% Ah!df{@_lٿ\'"苭Ki٢20H $ ;Źwe'԰/C<(%Ⱥd;լL!/ˠ/+3yԷ 5?\E{g F#2 Iߨg_:H5RV?^}o&K~Ŕ/6V6J/d6 3IGI ~׿:9tw4I>=YkJq.$^66tժ#Ⱦ9V1î}Q>mS@vgn(՛Ny矾\IR7vWX>ZM_//՞}c Vlw=snLPn'9#q@ X7hu#kAyFGi`S$Պ';!k^6hkZ/U|:T١UCRS!39ģZR @Yڶ3.O,O.Ǭ??򝢄/y\4!FmRjjԫqG񭈇!"ˏ .5bCa"uJS~Hǰ2=Y m86ⶵBJt: #=&N4; zk5?!M]7 [e P;ƃBjC<)UvXw_^9d"C\[!PNG:_CZXr(J9DيVԼL;A(8ʜ?f,VW͕8 t;^', mYoL=1w=9q =q kssb9|1%8b.a^'l\pQI Z+PT8Q=K&{-5} M?.^R4cvl[gʡ+e1hM|a#-yN)rq5#P8u=NGj*gx>c 5oxw2֦]0o{:kr!7첛;pT.J3jJ0}ԣܽP]tq G?Ώrb~hv*'ĸX5< :*Xoi;ASS*e]uuCfe[;2+1 TPab'lB}>&1 +x>yN6⹗knDơdDF`ٍf5ZL`HZaGTىG{8([bz@ k7!$€Kn(![hT1uצǡi G+/7YEU>@^_xT|RVl )Lkh3?;?MK/' \NC2vZPī?4w(51*{? 2V/vnsNMw&bz4g/d=1N>:=1Ɉm^&#vO$G=a>}j' 2NEj(yA+Sߦ{-1ESc#8hjd%La6@#~/7AK9BD;=jHvv2:΃R \-u@cypC8|_>~c*cVzQ%/Y|c{^$'p9Ms 4$h3E8p >UjxN ^.V&%r #E60o2gөҶg_q˯z*ڋi(t,W_ܡÝ-6xDkP][d n am We`ƢJM0&%@N8FyP)GIyx*MuXG:>)%,m/Ef `C5˯ 0ZnVInPʕgHw^'Bbc.㭗5C<:Q|+Oj xڽVrO̚0'|'(1s{afMUs9k?^.WъMg\€ᒝ9ͼ(!$KA6ߵCHV en$8;=Ǻe,הutʘF^ƅⴭa|7N&8] ~ߊIO-P Z}It^i6aިǀˠe|MgՖ. ƊpC?NPIŠ q&3mK0N6gfim"p􌲾t'$,pO*Njvrgfj~C<-bffjS&0T@ &ĵhRCe3sJ(:$Ӆӗb3K.)ɿ\D+}gxe+`}\ .ǀ@j2I_ vkdxe5OWI:{~7NW#mQR(;T١C=k}V&V6J>iOؓ'hsEeF[~Q$G5QezrXwDd Gp8/q %ѳ,znrRA~9'DԓjX=c!vA[ ZSc;dߍTߍDd. hg޸N읂5ssPJSQ^i $/~8xc h*Mx/n= ;+ww]ˠmmƾ=nPHerg#\FdPwHl!1ضњ؏cK2;^QawXFِxm] n5$Gs[ Oz=P x ;mwz /qױM[gb'c2%EFN{[ Aei9$$>.qG[&)E͝^TJ((*r.}P ]ALM΄AǹwK ЭH|TZ"5TQ'BʯMxUeA zyo%du 8ZR(Zpns™>"SX \вSKJm!yED=J1:e8QMn<%R+s+*YȔ>yk$)%ץ[nj℗= /jo]'@O(씛"n|L9+YK,MW@(Y)/vX/ ;r~PŨb _ u|۰*.:P#qa7`$ge: #-jz~Ƀ+@cFܣoE %=UN-E|˗QQ'd/:SrMpo$otB%]^[Bf`/s[ kk6ΥhJxyW`:6Tf\y 1B7IT L 33ԉJsm^6ɕ+ l (QOB258qIMyhFMŔff6 *DjP%O(*8ٗTT l''/+_hJV?!צgݎ(dV~d6UN0鹐qr= N!1\F"}#鿶u"t^X$ΫKhZ4&#>ݧu(wԫ lݲr"8ģI${7|BS^^0dgzMz|޸⑔WJj>SA~\CyuiZpSG^7ʇ$q!e\n&w&g\8r&MVVm:n%n]9*"ZUo[ôt,|I5Np%#,oRWzK>qUvhZ>a]Sɷ"~ :]nzҟAR>gO ׾H`SIkL" ?"ajNJŀS}OГej. Hl25wTvNNJ"ҟ*w?$ N4U 5a]Ӗ̜3s/ZpfnQjdn9ϖƿ/SXϾ6E+SY望JnG)u Dt{!b859zO@S^S u<=ﰤwX ۔2@Z.^Ұ`2hJ@G}Jw̿5%fdUږMzړzAGIpw ZJ;kv z 57\E&~qRM%ΑҭVP^Y3G*;}h%0vTis^ !ə*;"^zO1JCao|iK8&_*#ڵm,Ѱ!רbH5-Hf~«* +;#4JF5}?j ct`jStS= '@Ц6 RjEN2Y[K ;Ud:^ZҦڻp Oй;OˆV\M/KZ#'{S=H%L@F,bBt%19 LNjCK)n'ᥟO2trS]_DjA(`7 Ĭ(4vL Nl:_ _(Ej|G=\qu 4Ww v.혢<Ѣ.ꝩ9 )`bi t"v8ģi:@ Aw9ƪz$S/ZX 3e$A*hbS HhVM O˧+x͛{. g'z\ Qtol v"'@3m(t](F:q\&}9!-s!4h4A<*ME<6{bY߬.49i?¾KdJx4*Ǻ,vtL;9#Y*D/O7Z?$hu_FC#wG\[AT$12[1 >#,+?l֬to_YjGLMW;j-LVw͜JY+-gS#IA )E-AqȝpEEOZ}VYF=9Z*lW^l=K[y%#9wSQkQ?Ĝe.޾2.8/GvmB#ZVW4*ͭ(W|AЃ8r17p{IT%F'""$9l3ķ ޶·!}mxi:z xs!wih"#e^̝-m5"4cj Em} ؃o}5Ր[_ҝEW4t_q7U]CfBpͿw~Q|Ìj/>􆄋àb^f9߫'!ʹkeb:x5֮oX':gbU,O>T!\a?(:6avEbC (B#ZQʉQLK /i(xZ %?:yUڡ#촠YAٮCtX!$SK8xZ>E զHosWoSܶwcT⭉J}>7P;ӽDM6"s}s2=m;[}wɽ;Dz$ln!Mm΀dag,F /" Ҫsh6JUm>˅b Rl #:RLv62q Ħ 657;>>0Ɖ>-}~{Z*yӍUw%7glE{#Ab;[[yl?oڳa~ڧ .yMNmrnҥ{\;ACdܢkRΔu%ۯ|dd-_/LC?ve?  L˩|r:5LuE`in$\#h5Fߪ)n\ԩtn3>x/Rx$#C}sePfx!x<8}Yu8 \fMmHH Zjx͛fCRss)+Eҍmy`dO؎ޛLܞ^Sz'W,&UV?\*>A}GFBpDpw#LW.H"Hrj p˖UM eD-}vCC̳_9?!ܧ9*6Eg6aLwSv┋VmW> Uv,>icztDBlpG^Fj;<*m,hӄ!\ A/+7El/ph &%сV ƀSJsMsVh 5NC .g@Ϩ ǨY\{!XKhvrgfj~C<-T9/j[=uܢwe(>/_&wkKv xK&*fhu'Z2~KR';bo=wOIMxG]o=jٛY c9uJ0-;\,499҇<(}k@>-@K<ܑHUљOg^hfy+i6Iá_΢7yIJfHoR>E>-uSY<,fedD4xfוUĭmp#?IŸU l$ގpla6,g/ZY#V aݕ&PkG}rk6q*oHT^5jI1•*a|)0"-w$%K \qɦ|2e&:P+i43)Ajx4w(l]!O+IGwpGrL^Se+94zQ:V\% GMWMfBTԆ#.Fu/Fjr4ˆNo,`̢3rӒ3CT1jR(4f4ÒמJyo '@KZ68;Ie\<艽SYJ=j)hvORlOV_@o0mUdu&s6;wTֻL$и)@?ht m6o[T)_p8RYԴչ˯'~M&A'U& =G9넺YB-#𮟰1B|LpGtܱd9 \ӿIMA/x)ɬpb&Rs1CmGFȹ_12ySyG@h7z)&h @11u5z# BRH%C 5?*n,ĠԄ3$SS=L=S k XJ֣iLgZ'0.h 1A zsBǛܽE+m͒n _QxYYMb)Wn{J)2huL&&${{S2u8 z)6X.q:<Z: پ j fjHjdHXR3yi@CA!|ZDǀq6, co l޲WUړ8zPѓ)i/ym:,Xv̞k]eR˂A W@Gz >C\DS5\v`#M}VG*2x -5Y)rKx3R[ (xZP*ᴑC7z"Y9r}_F)Lĝ[#W_Σ[uK{*;T 8ڀ ɣf9`Xzq,ҌKQ}0Cwa xppaO~{+KD'cZ)Pd3)ˠlMɆ_PHq6qu;=loXr5O/醉iB4M*A6x`GԀv@6SPhU/ *j؈j) ul,1wA> 1(ʄ ?*U| L8Z~Rx Y2фzQ\٫1e0mi-xCs͸],钟Ai*!*mͷ !fG@KbC͝2LYHg$4j4+yrS~6`"vr"W@oAe=Yr+i!4YQc\U4c][δ3VsJ ;#~kBN`nY'9.;ɗ{;o g򲹋vgDzMGwni{ź_) {^xyUbD3.i#Y+c|Պ fM (}'ڕy?nG&-ж;JN`h)MF.~rdwٔlNς! f RM6bG^-WfkqU -u-$1`RqɺJu@K]ɰ*-)i3Sv̢]2J vv#Q"f"aHj+Ҡ/OW_:}g%ŻLX2̻dd~Mɶ[nȼڰ6VɌT<`Q;ǎq312fVjqa!=|ʋOCz ZM8u_$4 .DcL7~FdP9.SF;eݍ:5 =mUnOȌ0 ^"zwӮ'8{iVeK-oslOϐ`\ a1L]Z~_I6_wtg!E&@녍I#Gݘ7&xc+ޮ~rK[[7P/̬K& NSAj 8 zZYl],FwQEu}Ke}ș,c +_'*We덑%|&ڏ9e•p3(LqL[FMa+ lx?>Q6&cKl(.#!wJavw\L<֙m;丘Kyq3ԏ~$y}PKz -A}3V't:O6Fq=wtn4 sL?ui댐۬ʹ5Z#aO כ  ң;ύcBr ɿV"Zͪl_D=Sug 2o횤`ϳfKO]S| f\*9J&'S3əxA% \K;7Uv.O魊TP(9r#^短ì8nj>_/ݺkiO_$];T}5l;b1N|ۙ|_L?z>5R|ra&57;;vr'.GZVlmX;% zϘdpD=Fy7ljJ30ڻoN(+rҎwevu?3fƭ<4J[ٽ6@FHaHc 6O񩅩yt$LMtj6q7~ml{CgA_rOq&_8f-j(Z=끿USx)!+&x#~X7G>Q\j<+v6^+AV88SHh!+Fh-qL3gpL,GѸ0OO8V?%n؝qFŦ?uC8ދr&&п&{_J;1~G@(syb;Mhޤ啕):]L^J1un~>Fs?(l~7:|jzdɐ_D=/^5{k\z$mBsGynGd2h7N?H|W1ޥ*~zɜ"XN1zgCI{G$䮪'D_;!t}صo=ʧcU_g5Va2o䟯SX;rgE2/!!o r%D+G ͣn0hu+#hn[%j$x4?}LP> y[ NȄ0bn|$~g|U-w?̂QHo[aapF9\f&!L£9#)êP~(کզ6%]vJ) Q!} ^}IYd$5XsFC\4 ^twJi9QmUe[: D"AB5+J!MI;7G;َeKY{}[P -xCuFI:#5VZ5ҨijBڢ{PgTQWzN%EzJq3C:F_ׇ4=n~[+!:w+W4yf;~*#|Ꭺ43sfR׍}\_H J\,zӝe qZ#CP`[o^BlQlMpzn+m_͡g0cK,kh7 O(95ך6,~*U&p2XM,xo6w@XYJt 5dR3Q#2g!{1tj!zJET tFdg[f,XJQuAL\&@'Z?AS|3'-|~a1;k+p-EI 2}NT1k7vJU' #[!1iw{AC=ewzyzI4< Z?= er a3X9=2Sn83^I翢q5` tJŠQk 8zJYVt]%4 Zn{HLry\-e 6pǨ6!NqCcc^^}Y0d:D2Eȟp &կ>˴o.V_ Fd;lA!A/Du?rf+8C@K >H%eKE'cDuKb7^!_BWGZxfH^,^}c%kF}/_2_oBeR=9sß\'^2RV$0Z؜g> R&xkb8J!Fݤc\30 :wZ+:h)we+#<ҰD7@KiqGuQB.&S -\'Ogj8D(FNt#< ZRE\rr9!h7zSnd3g[c3sfm'h$GW+TAyi9gBjgYeܔ^#юBwHviW!9Qwsn Q9ث jٟM8u9Tހ2L`+ϙ**OG9ģF?c/Vؚ̜jM-)~IYGJcKc2ǞlߚJ墤AOvѾ ׅ.Tvt-] fL-C7cKƵ1\ NVC'iԓQy"l.HHHviBLj5;B/Z͂V?dvqO]< >2cQ7,UW[aXR]k Kj:x4}!iXra2gɐL7jG/C&F߅2]P7$8,!jg4ԍFmZmjjOt\yaJ<ԫLw4?(lNJI|V_0Y˜*)AhVoҗ1Iݘ4m3K靶 ta)u]/Tȯ}K|<O+| yZi/EPH`hx\vi5-?=TxC+5\#ơtP|8踲|\w/%'cC \~{il㸧.rElcNm{g_9]}wh1<˺F匷YsK&nC|8+DSqxw@yso*E],_ v)t{΁Eիp#!W9I_ _<1\?%8%1%*)0Gȅh[UB'

Ob&p>YB"9Rzs #Ă[{RW5oPa!I92t]W uPܶwcTrz%S'@NE*z@:}ǽ&p2?p8z0-ӃʡJZNq3+s7*ޝI-zr@7Րhm9$Þ=DG)5H,~/+[~¯dx6ʹY 5^FgK=]ZnWgBL!32y/%+ri3^+,7eig'k9͜A?o*%52fvg2-;e߷_2UI%'RBraqa>0=;+t)w+p:0`lN3kxؠ6w,G뭊SPG %f9cgG?[G 棍>V%®߭{v=O%_KW[ʱ;i;7dX,v&_8SOp-Tr~!9\INIˑ֦5|w@6728 B2Y `0`J~ /[\ |r(mdLZ6jDݮ*U؆]e;> Khpkԫ乏r! 6Qq@\pzJ#pıag3-@l*`=Gdr~LNΏ;k.=k7 ݍvpHMCkHԳ[J(GS+ۢb@C"9DLu>,ҝQ#*󸊷ifq3LK_xXœ@i atn%vo^-BB0N`tTYSQÎ~M$^(p6UR39s[!NA SFf0d F,@)uR廓2;+VnؓLm6otZ>cii Z31SPS~'@2op:c&NFg`i@/4}&Esܝ&TB^Ufc寿ޫL$ sϵam6o핞u3@f=v`xxI-.` ]jx0]dڕ p (4Rk[[tfmE'O8]6K%tY:PU =(hyEl::G:'W'Mg rrxg~ڤ۵S}-fI >{צt;" aÿ1!{sc8'sF~6X_*!YyN{5!#uV& $qW];v}صo=;+ߪjev{tF:vQL,ˬ2y#dpk}S-U! 6t?WR/'գ6~MVZ-j~CH/;+T$#q\vsaϾo;V{wx%D%i.^PaJ, WҲiTtߊx4ˇ''SOpkNWQuJh~H< r$KVLr{-Z;Uصī{瀋ټIٛoK8f荑- A~}M75J*͎kNȗo j薪[=-6-Ȝ/[fI!3˗sO1认fm\rIǀ$|iPVgV7kc 7GS{׍V9G)Bh5ڦ`T[C~n?O1-eH3Aֆ!$Z.-epD=)D ]^}M1렯++Y/K(K h)S w6v65XֱlYڻԅ۱4 xd@ٺ'6a Cb'RH%L0A?^oOB _oN` t 5?!37}7Q{ UD!B _H|MϽ%*6[OAOauʎ"5u>cJ˨WPMU瞴7 CNC @K5H}]>f9O$6lI=ڼa@pvm #M?QȶH[7kg=f*;Un[9Sy9Tt΂}WCBcJ@u>,,cf3}`ϗ-Yx? '8Au Tgݥ-f\~w z#f@Vϑ Ɩd$r@3_4ع!&|j r9$!.m:9ӊs)af H%"'S+TifAn0EeCY/B x<⩵i"Ee!&jDU\Z" t &䎛6'9釚-wڕ3TLL^-ku&viH梼{@ETί%ds-H6+R إbp F.^VQV,EZaV2sO24r-ȣtmvЮO]^,^ezʿK,X+y?ƪ ՞: V՞Z}[Yn'jxTMFǸ>eVkR\A>T>bmAw}||(dSe)Y3l/=cO~]wc`ES8(!= ͔uU_ڙchl4&g/>TB}ۏ&{a,J-.&]6v('THfŗG1eF?}c_6u7y;FQ2Q;\t$0n[yv`FG ,1_9+16D ŻQqwTG .Lں0Wlin![IV@ȇH<x4C!I.7_ZSdizG].h92I1 {l)g(gW=ut S,ضY.9},^$Y[Yfd !gzC85at\Yh8V!n[7ˠZ6}cOTC 1r]T@kҠf+ͧ7@KY^gjn-5#N3lp>@\SZ.VϩG/W@(!Aɳz<(&ݡMC/oR= "y Ix̷FČ]J9ך4؆7ϙMqۏ.E*WQs魂˹B1L3 Ϝ(q6>8T=nU=nKO_V]n͉oۅJo[q[ggn< m77!ΈJD޶4Gc!Q䘾)tS 8kex}7(Բ"mA릭wBЧb+ɪf.r4{?:;|]h7r~dw^7%S @5B9Z?DNlqP!Կɕ@bZʓ)䆡:E2OQEz ;w75!h]B\ ?:4\C)Pkw@tƅqh_M=q1r%c4fg%]Q'V3a6(йjkKFM5PرJ,upGr{,I>i;NEtn,:VZ8 sCuH3NUu{݉T=\'&{w(I]nTīt$;> țjȎ\[UKZz/^F1W*1TD lk=H $MGI7$r[V(ai #:RLv62q Ħ 657;>>0Ɖ>m}6~YDkmSD*,&ӳ}ܩ@veL2c0qh@s'3}.ު(5pĻbJȥvN; Z7Iwއt-cwv>o7`d5Hr)f67 n|g;*OyT6幼:Q2nQJ|`g^Ws>2{EK4o篗}@ԡ;^}bڅ׿Brf>9[H~}˺PK"0nu`y}zkpT96W}4uob$)ɘ!:AֶHK^2OjWxCZ}9ģI*݈# (frze_Xf HMV?!_EKU^[_{Q-"xW'8C<2NWVc=Y*Xi=)jY\]9aTG؇]Fӣ|θZua-QͿSi֊t֊+`w/E5˧ͮ,FsYs^S^ZFցwQ~Z-jx4S(҆d˻.j5>ͭ}KmNȏpFYR6huB~A?n,'⑔e*;sG,1F67^M,7epTp6B,o%/8.^R%0SI ik/ ;Vaϧi0k{ܨejHPihXc>Q|- PveX>&XthLˆu}_?i|YM*|f rg 墱&sm~zqUv?oE<xDa_ 4Da!q\vױs\Y6ޢ[t.8,HuⰨCO@#5Ѥ I?"<ԵDEg~ Gh2##4~YMs$}o_~ӠOOR9!mG}V?!mvKL O716oA:ꙄD{"9: 6 FB",CnPRٱm]$mE%懀A/zdv`Y^DA2V&@tv/]o6v/kcgi889.^(ws&#GF>|m ;O@ۙ^QWeҙ )r'M؊4!3RU+-5C<-_i v"\[v%ڞ:f n|y;?7-c."Q9sjL6 ˺ ůF,,k7%%2e=9B- vY=E#gRf*i'Ip'H:iȩH` ŕTNqC<^DOዄ\iEp=ZwXƖM(!4(%jg@Sf6)ҧR_`L拾k0fK)i(mзۨt1"}\bJG^Q߁!f[CuGe5k_O˭3Mp#\P܎LHg/63f|B!n@K` +,p!hW\(hL%2hNܓ=ڔ U ZXL1N1Vj8 bdqC<4h QގH/%,lŰܼ 3 F07AK]V=Mm+Ͷ7}fH8b2fG\eCj hu7J7E73LQ"GՕ\|W%/l%cG6a/hB Du@?=+P12rH0 :]VG˱XU@\12*u',Qh͘ %8C MF* \zIS N"S;=Zj.S.&7t5/J lxzuy\R1&ab j9G6?THzWhv\SI/rG.e8d>^?_ʰK}('!q&njQHRkf]"CJmM#ŜF+灗A_&̏vQJ-Y7:0gU IK| Z=qg({ੀAG-x"b)/$'F.$ ! gZEw]Cq#-C^m@?"ӬjVg ςK̴-i Oý-xTwF3.|'mNʒ8a@e6?w:l ;'"CySs}\ bVq{ zYYnx`woYd`PQfWst(iٚ0drB4hP J#Ag@C}<+s{Q2 ! '??Qs}OJOu/"iжR`l?]U&C'oUv7`ŒQffjMo-S&p嚆& 4x`&3gO=fžweU樧S|PBf"N@4満7¶?=~[CGjjUR_|3>BL^U6|NQD-@jq Gl*QQ| &lx`fKYH!i: !+5<ZjhNt7faˍqސc9U@Q#V:ڋ'q G%qp0bh 8Zq,hu! E V׬>O4UO O"蘳a Ws5w xoA"σV[Dtj%Ά- Rs}@u`}>咽)@ ^ThӄilFE11403qjʔu"nJg{増D{Z9'EIJ"ZL3!&Oy Z?SW8ɘ4V'cj~Ce,_|cI}A\c,;zYEqLۢ)(7!W9@FSP#dY󇝁FxNlȰ30!lQ!Kw4lYH7":-6uqGe>AgԸfxZ>kd LDut]Dʎ"ߢ_r?֛z]ycZ\K0C$qcr_fuXLAd$r)2=)h{2<2;h[[JȦyu76D޻3I^XC~:jH:V[7T{`xb%.c3HR<6 JG)ogd+rb[抶Az'\+t`kyi@IȺnlċ6W8SLv62q Ħ 6JOOMqޣO}i|Hؖo a/JNɍdrja15{h^wxn.U4:a!أOUWn9Y\Dsώ{: G}MӇK][>|z˵K~ݿcwҔ͉ #;Rng~1e w[J/d6 3tlaimZYWq-F6O6Xx6B}#TSf՛!gK77YW|T(]Urk;/e2{v8ElP-uY"r5<89v}r>?Gv7¹BeMgJ6Ɂ}#Z8`hFtوfǧFe+&ӳΚKxڏ&Gu-pHV64DVpg=WuSZuSԄNWvGA1\ix/mT0VwP!&yP_{v!h%=z+~0x4I)Qu)S-b /We ؊&}Q 5C*m&YѤ5v$.^RF'c1AF8 HNx4 a>Ԛݫ,%Ɋ;j3Q%7ixկq墋F*:E|AKV!Nu7o=nuuH*u} ^(;n(~bol/.w@AG$S`dXˡ\4,H.>0;'#B#ŷ"~$r* n,Zoz+Vҋ/vyf h;EՆ"S诜)BfOA#JQ)Ӌ~R傹S0fލ`6=ߟ z>P^\'} *xZ>`͟i u4`C{9)U O%; 4T]03fm]̡g&5:IGCStA.ΰe!o{\;8>`!WQRR17 Q=͘CƤdQ".Gw;g rt *@ȝTVD sD}BY<]"!qTX2 64K ^@ǔ3/6FOx -GxZ0zn@̚0'S-q \̍c2KZ~"]zdBLu#UZP͉~LRUx 2GmbH8D)R!wU0'CvάHLts7O@?QfR|@ބ ͡1PkRN_`ǔ s,@]\2oh 8z%=>#;VoEڰP z ĀZƷuLuzg6tZTsYlB^^=7wϠ6YifftpnWAo/%*F,X5 ]~r֣&];Yoַ S4jmwʓWƣ KT8p>IDEF(QHZitG=c@YRrd|] ;4*!|8kbPscn@K8p6)Ӥb9Z_gC^75:beLu;O 8|ZWMV{\iܟn율My"T}ʣ*6w#Mj]25xS +?Z<'%Giاƀ>Au' G}F^/wcwvߌvu ʴ70\|ZXfNAff!ŭF)q=|E溁CrD A؃% f2,L|Py%'®|сMl^V r>逋ƓFCv$Z_P}憀A'714:|CBo ckHr|& O|8\o E,'#'IdOF"꽄P"@ө+Ƹho /@ ˱jr-z\[7;ݜ1^b]T&~8#dsع!M/vx#R Whk ukcc1hWe#k|Q,iˠ>XŢE x}koyX%|.tPCG&ݡ<N7Fu{@Z&z呄\Nrr}JY.7*|Bzs!/|dOL}G[O $wZfq? v\*6q}#N7qNTm^eЗ˩롛At&on~5jmI*JDrOܛ=]8CWX_~JKJitO@XLI*hWm 5C[\mVoȸ:fqE$+Z\عak娵+@}r圑o\L8W@{xљ~jꙛfA 5뉠f;cifA cvd80:_.%2!e*R?塢\z][tx%KddAք'AKE6?qCL2ЬC^-r?=AS-\# YE3 Zg^bxZ Ymз5XlQxw@[Ǚ| )}+E>iPk&:i)_qחGVǮLTSǑJUOE|ⵚ38=fTK8d)T3%3ѩ&:FG&\5DQIB.Nil;Wl"^a(v5 +>o[םBoK| ]z_F\0}&:ao,ۑ!Ũkqo94qȧ+J/s}9ʜ/##tuvY,gKŻLyY[ݍmgnzyE{am+Dcx4>{woo.#f:kc.J-.̅^<[ZyiH׻ӠDN8ut}ӿ" -f{xJHo´LFB8N}j&W)~qǙݍ(s 1 ʼn1W;Vǖ] Zudj%cK'Qouz=ASD[_CL\jyfS}hN0Wb$xFB;un:wB~CNjLd-W,z4p5ԋ&‚,J j0w] C}yirŪ o;0,%dB_yBW# m/67Y(/ђ)|8&n)v@v(8HQvB^uU<9OQݫ%藩Xh!^Fd(満'A;'SsiL7P.7@"3soozXѱL,ŀ܍ޖ?좺QF&UIS|hB jx9\OO=r2h)sGm)qh)Ɍl$^Ux&>TnۦrC<*+w-kהyq2E S'AHE;q !U!zyK L*{zt-izDa;u;8TۺQeo3#{D~Q5##ƻGm{7ME}H=NFwؽ~D}M8 'L{,>Sq5VnTdޝIm zjHs[ 9hy7G=q'9Hԫy-)_^{閻T+в,fX>#kW[mOBH.T͕r!O%utdlLH vthIcfZ&T[9cgG?[ma7O/vn/~.wZVIG Ljnvv2?y9V#M+k6J@5瓽`+\M=끿58FvLS+蟣" ދ}o_{y>y4ߕ)OEw>Q\}ovmVr;ٚB8_ɥ$̭Qr%_bwƙQϕbX1 l*:y|VoƗCyy9 >Zy~@ZeU0MSe`q%V.V~: gc)6Xh72`xth:/(U=\qm z/&'2(vTQl9ξŖ^זMCx zNۤc \"ȨHryZmƄ8[`0 :v?^bK 3 F#GStl"ɔF,L8ݔEȫrb bɭO|˝$JfA'_{B}aS8,]b930 Sl[L`p 2+CI ql).d/!WKk@s/`w5k.U6Mͯ^e|LU2!WFF՜󧏠WwL[3;q) lp Vgfv ^-V- e^oY l^-e^Z7dm+D\+jCU@छK)}"&t6vBoIjTaȽ`ž6?%t;"A> ~_E粗!{Nɜ{_E`e NχlզUzed2%g+!JQ.Qfw?P/uUs-m'`GW[4P9#F,т= ROT0iD ,C 5?!MBQ|(x ztZF qt*lEC͟& @4i3_*VYlCeߑlCBI%>$O,/*S-Sjwb2Wg}gTkt;s#\Qξ:.[%Kj x$eyXz9ģV0GH4'Wkϟ ?t[_fu7"5 x{XiQ92Yߋ1pѾt'ٟ='pXLxp ^mz, N5lZ1jRi AA|P,ZR amg]E_~hEמ}'خimmRB{q %r/?_`g(I,}OW5a9ԇqUv>Q|+wE8_] ?V DǍu׿mM2Xb`.}A %{'AN!H6ERY>낉(xCs;P Ӡ5rC }HCRsGgAUk?!!s{dBN4{yދNS|V7o:AK|TӼFHӱVrv]E;6KôvՎN@Vwj\d=33سrYɛYOBA_ֶ2##1s xMfGjnPzXR^(IߦQΖp6Ml@/F6 Hl$H(+ǠՇɝi03ㅰz+}~RzPsGe2J JUdF|cy)ɴ7EMS>FNnCp<ӧ ~7 zt95X3)gAo0޼ϛ[{,ʙXX4ERIg843b8cmRs f;VK# ޻)P-EIW`x]~gck#"Uܜ- ]Aa}xz6q}K3Fۻd,c˦L5Eg H"ի@Sf6)ҧRuH`L拾fZ*Q:.WwP+yHeGQsw+W(rl8jdjdUg7s=@ukޕ>jU̒6;K2Cko򙾅 \sB.V;j~C<͞f.x CR4C?}zMG|x$ǤrB͡S-KY| 5.C7@8CL%j40:,+~EI)Y9<#Du؋@)9j#/ȇ5%"=MjR/-iCwp6󬩲CQQ|+G (y0f~ϵ,TԔiV5w@t\ hC+s,웋rZ=.?ן2qv9X쭇,_Lr")m247 ~ts2Z;^WhA1Ɗvx]Nrq˚wj^2Ьuh*H%>ZQJnH* LNj=x<©p\i/ߘD.:h)gѾ5xU.#PyDA;Cy.T_Zġm ~ \Ѵz6 i.o "`6gYk y2~71a@NVDƗ3/SX ݣL\Rmjxie 27{wLVMBI@%ӠlV׍tQP73#05?!E6ďB=C?Qc>id h*Mե/Uϯ4*;O?ϧ|T0cz^gfz \v2i{Yz.[ŹoC^Md^PǭoKV߳^x_ :iI.#ϸ}&ӿ= )Se^1c/lލe A+*&[zNw$]QD ZbE)jn!E)9O8"F0;mK[i4d;YQ|;HoK ͠UU3WW/mdRicz7Zxؘ)ELgfBbׅ2]̄0ϳic#p"~nF^iuSD[&2 C"qK"NNz;m!@{Zv<%Vȫ/MO3{nbyg.$nLzJ-зM@Q6hG=^,|>J&V'A6VB\(#VB(A+ߕ&V\^}MY(Ny'V2 L Nx=e8ҫOA̔sl̢%)O/\#ֻy~R0 anqVf]ew3;#NﶃRL-{jng7r:'d7XOzqMO6[ղ; u>(ovtR&߉&.=/[JU, ~ڿ.r]X邞UW w[uI<[m=jJOHtAfuէMw^]?k[VsVڒ1{G?O@ד'v~PQV;(3PQ޿kƿՄmwMǿ+wiu͹:U1d$+- U:}!7W@fC\\:hhҶY4XjB nVcGc#k咋\ghG=~5gNܶ dqfHJ1mTU;Vz;_fO*;rΗL/Mڦ_i+ko8k{ADF%G]BPk+m]zRiL$W0-gn^ 2:ٲߴ)O 6YC榕6g$؛>-u6ծ̏v>> sj~C`Q|!xnֳ>ZUxt vظqWIp iMZxi}u;/ڽIySx~j$!h)$QeBZ'@v+Zvf+lQ"OV V/N_EgƷ ߏfaMŊ/'<.r+bv{*f˕k'KU/)<݀E[rU%/B}2gc NU7n7`7IzRh|nƒ޶޷"'M 3Z>Dc&ChcZӕOp@x'X==Y`w/D`_c5W7pZeF9FfYJ=m9AqTӶfI΍VGBƨ'=9a ȇCU3eFcc3cs2nź=G|ġ&m@*81X=j+V*Qжˆat  ~Oe=+vӇ;Z>"lū՟JQÃKWsˉ9Ƕ34Z*n?w~beRȎ7ff'SSz5=ja}Ƙ2x*f"8x -ڂ-xk|\҇O3\ŗ'/h4={A;vMåͦk.2﮹!c|lgno˰ }{HD8 N?lH8q89Nh=D^!ss܁W`_27)5 eQ, 9A`A9W*CdmīWELاN٥ H}>7| pOX&^sR+8JiLMelvxx";C'ѯF&'g=4SDzcS#I\b|mVXmC4h+$ǠՄj&3 N>Cl~ȁ\B=od)N+;N0U=5v obKQ\/l]8þE}f:dKٲ,8͖ e_ҒJl_z֏:Fy"òS;$7Vsqpv|x<;59;5|L}1]۬]YbJJK#K)ԋW5UuhjTZe0_Bwx/$R x4ЉjQ:ޚ;d[Ż%|Gu5rģI+8"zkPŧx9e_Z>*>Ż8">BOs4ďt{xF}fR)KUW6!v[F='oKޏXep?\po tI2(!C/@*#E|p`K҇{c;PI +tgRyĘE틝^H}.b;! 1G^- F2\l5AhRڃ:+Vb %5J>X%ؒV(p FER!By܉f+?"[UšClu,Td"gfEY!aKde،#:`;@>BpRU[sQORqu_ 8_HR6~burG\@ӏ;02 )2,OKA]kQMv ~4(J Fɑu#R7cx4GxC}HlM(x ݯ>DJ)x5?k}^#ʿ!mJf1a|hۋ/^t"8 'LlnR d 7_MӥBt.ԯH, \-'m" #Qj s=g"lsiz6ڱ[t@NY'ԔAra8va z&+Ճ(A#Kec̳B}}{c7e%ގ>"E=G 4:;A]t.NJeejGFv:׎jG$*L gR5&o8q i3x_$<ZiL`Qd5g7el:tlpG !~,=2/|Y3~ޘ*ctgah\Y3.L1#vK"_O!ZFi7DW8to"uսΠiPD8 zRӾpƎpMA _O\NU6=Jor=f`:}"49b!>yCUsG}1sjܳt9ʰS2|KK@"!;@_S~i@&W߲}X+a||_٬6vhͥЯ[r P)&vbNSCPvA!zM#p\#eQ靍h:*D $|=A MX$~jLԲK1P;+o2څkaɇ O3Rsr!ʛGfKaHJ%0 7t*+W}iw =Ȱ/|\9 ۿdjp[ưtuXBK=Kl{v8y2Ad7&ܐiB>,a*q65Rƛ6?0I;/V$26.M :J[c(PE^O/C;FDE?ADߡw# }Q7ހZ=Tcf0l3Ú-r[$1cpOx6 |Z}߱'K|vaWԝ#Y~,IןAiB.Y-ߊ.A7l[7TTpQr9'~mkQ"QAo/n q&0+_67H.iЧ>Eƈa+ 3 b~}%hX`k~h'!/ ĭ8z\YSIJLBL@@w&ӝcXqC=@i3 Įxi8Į8)TC8G U7Q1n0#;*tio D} ު޳M޳{UuHbPO! £Jz }E㽕\*1p(|݄#HĮ xtzEE L^.^1@;v\ǁmt Dm?v0(NRfFeyA[%fDhuFyYW2xZ}H^ & uπZnB{csrxߠ W!^~j=\# ~ZJ?FlvК2Mð@,hI4 nWmxan+Ő Md>pLBeZGB#H=];S#T}{]AFa د~=^ I8$9VTTm j` Cސh)M: < !bwxV !AwQwB&`CL@isB&tB=OTxtBL® Җ]I^`bTwBZml?bYv(,j]$Eh5.qADv5l}7 h=_A*'jc$%*h⩚]P*0/Ykl w8zD% GA6gЧRL!{qC__xV_r.Lb,LPKbn@%$:Zj˵cȹ7n WSQ=ף1 W=tEĻ r-s0T#6Zdg#< Z߶~Cہg@'bnQj 7|EV,^Rvn+m`{ ~C")rwe[@hB'.X)) [O5ArbpRZs-\ȓl;t>eԈpB\cV0a*Eΰ߈6>@/aԄMl%3~afYA)rQ82;) #mvMq{xF=t$!ɹMUs1*J I:$E|Ox7V;*^ş!MQw.$gA6~]H>΁STsC<^{Nqh&I*D7X%43/\t?IeřZL _bO=ɡ.24Rtkj̋׌ݼ`vbu2nʞR1k=NkI>ȵ`PtR6h((IMM2x -565ZdQO3\W("-G_;?y12Xfff3c驩Q1G}itߕTbM,򒣅儞#撰梗]~4)#82_^F~vנ} ?JBhū՟J?8^eIJ+蠫O}[U0ʇ:/u"B#EM- 17\=2"Ň|/k8R &B^-e\ˈ[Zmz%..VT WrW`˺xh-8 }OKV7q }H9KTSQq#ģV2voLw F'*U,+XyS,B4;Ie$[w7w]hK]8nRYYm=2g揠,} uFMNa] kY;gyKP@ P&B61:%ǡT(ܒO AIzhHXV._RۤP͸IG NsDG;wC#ݩ}gB!Iw?:9#.V :qYI6\H7YTҟl.QMwCOA?Uִٲ'UDG$n MQz$P,2% z`ՇH)'d6y畛=Cװi{fzʹ3)as4a_ FYwN Lj=};+NlԌdrSY'b98.hN/0y4aqe;)ql|g4u W ' 6ۍ>5wuq>T<$΀Ѧ~fzQY7m4vka)UޭeоP1R yێ=\)84tL(yH:p7C =T qFظ qAZ} ζΎ$tBh{Q(5^gѳK"CT͊Wb,q=u㋄@K jd .sv|[ƦcA)RTtBx<|ȐuL$kvs`q33[Cft##\bFG@S$i7k1pB4μ6ڿD~uսX~|)\Ar}/] $=huTK즀Juf3~C<f·݌l0CsGS돯f9cxGygM[8#'JSN^z62`G;%3.AIܪAMom9ģi4U+A2 @iV69ݕTڀJҠq4u%v ꡬS\ $$p)Ub(;t̸hMe%3$+} j*[J[ f|נ3ohW$C=z' 茲.eƶ"#{N᝙(l)F FKUF>MZG֜y.yJoeC}ìCOjA8 Zjm`7]+p Tob?!E1sޙ50˜K}4ԑ8JpE q;ݧ%؍sxGw<'BikC&EMkp Q QV}ao튄vַdWĭG]5¹8`եt0V5©Zx~)㫷Ip ->4hwh[+WI83'ս wvvln`[50αp9:vDr]*iw =Ȱg}Ja o6yw[b7}QK/m1}cO5c;Rkl.-yN޻2~FL5=m[Mϼo-n}5ںw>4ugnzwgiBe|7Ghšzar~WVX5@p!.^c&Tga\A@ԭ>cIș?j|40DѶUʚx;4)*w5/f퉣s{Er^9%-&lZg;J1F .ֹ"v}'9}--2mW89% )L8ģ:9BEZ[2.o]aaXxᵙ 1erΝ%-Z9ڇWF4V+ЯD}Cn#:l_s{-Ӿlilm~5j|bVNN&8}嚰ѧO/ w#0Rm֫= -sme`|^W͘/ @)\.vAǽ"WhEz˪'Խly%2& ^z4  I4ׄL*Ca $L%/+0;?p$20snPwH5MhHo,ׄ!]O{M$vRtkZtC>ΖOJefūh;: WyMӣH8oO@Dw1Q㡝];vɏsxp`qt d'h#vه \`1hkX >}^F*"2C%u ܇vQXc+K>-0$"vSjdIF@?REμ$9TY4"-ҷNz-[$5m6V,qTVJ pd(HdSM[f 6+x7ˠ?XT-t 7/=:bo2q+$< kD8,X"5ig B{#W*/Vl6+K۠SdޖrClGiSZ'e'vJ |ꃃ b zUY%Ͻ,mm0&F韈׷ -$%R '*糐0.TRL <=4x3(+fhkk4,xxDMgBECY$$W'tF2[ l[`YnmK>C+s 97(Qi 65o I"IU 2MPTkmhkkZk,ׄ>-*nh5tF?ڬ G2t\"N%!~mmLophcRFݜĶc78NoL>RY8>XG c 9AKUEF5M_,$anGAKbBbw8zL}%\Qgǵ{[W)s6G)G)XRD{4_.^ [AW¥hJ:T_ (t$xGA~1jTll ;p$[/,&x Į Zj4o1lf%sti3.6T/7.p . 6]23#-1#Rr; -rSz)m`{u#<wO؄ aH^`Gb?|]zgQ"Q/A+lkCv5TB6C\F v\bCx~O6=xTT̃$v*F7@hj t"vZbxrsIr xh'<ǫ; 'q@p<&YY[OU($5MqͼLKL~A$ޱ7 q+dդ'_`lya~|/ 6@lyrAi>YIi+þJ>TI%jr~Q.;A_yL)'DZyl&ΕvE_J;׽QL⑸YRZUGͯ6_r/1hJ_K Wok m%p0SuocëugJ#qIr-:g~~p[}2[ ׶-A\*" v7@w1*S`C8G|=,p(W0C'ot$x_񼿊AxXyĮ ؟%/]F@3ZM8G/ l.xM# l@캀#=#ubdD%6\ȲXZ |o`h@_“!?ˡt6pa{xOgъρnK]󠥲btCLh^KEЋa8v} `FRMz~à ?#\$Y/f.RSE t[݋,+gnR~q瀋pTMǵJ[E敜m5SavSe؛VѶX0I+mA[N%a G&.%GKnzA}sBpQc9[a.*Қ=e} Nd^Du՝yg_O:ijeұrK<(<ȗO@')%h K?@FgE%/ 'AO*K~,\P"S ̦Y"8@$I 5];:h λ4B8vŦawV O#x5[@h¾UO+X`U=bC<ˏPϖ`p19-j1n;Ոo6zH#I (Qeן/C2VzOihF| "|.x*HNTjV`7ne\Oi&z@:o sam6wLHI+enXS\ɵ;pU%^h$Kut-A.«6)uzU*iΌ] U4TǎS<:[k@ zU9'}͗$Axzz߃uz-}z"v6#X}9 :!⋄MG̗kQzZ=âRaAA1;_:t?l|s˄x;GMpC 5YR稵;9  r1'Q!rUN-W S^.6pL1ZpX[f,혦]9:we).VRɂ []ɚ"Opwp~hc״jŗ+@!j;x6-9 G@˹ O$pΛ4n8zN}!!J8YjBVdLbe? .{-%=7Y"-"qaK[V2/C"Wə!MؿQx"Wg|cg\᧑>]ΰVث OrM[ܧ뮕grFJA ҇ Q("t&^y gMh= Iy3Je_ŲJu?j*|XU0mnpv+q4|Q 4sΦ\?~U2W!ƺS7M= ߷NL g'fG62ldݼ;VqٱXvzfgҳCsccs w[@K{cݯ}kB vW׾U [w*@+y?=? WlVQ {?ܱ m.%mUͻuv2G3ϞT6s[;dn̏r;ʧΖěr ~n_8C"ǩ@p*XYa=q8yR,B` :lS@.~ʫߤf$&pm;a\L.schGz]RBDՄuRP*r 6J#-* f183Wnn<`%T(yDqqpwj!W2تM+Cj`|␩rk'!ٹlvV2Zcc[??poC0==Dq!s۽2^;[EL]H'zǩ3/]۲R}j˴r\.{a_ly>?G'^9.eYp;5du]Ж[®gyH9a)%Ό _Cyh PT5٩٩~g tmqoƲ3?a-+7oem$`7./,^ؿhjTZe!0h?)ݭ㽐H--:UaZJ\Q?;x%n[HPi{eQ$^}QOU l;@_6vb ] +,:~WҮ AEX\ʛ=$j$cЏ}XZVC\։Q#3۬8J5:SW@ *4f?xxƉބ' =@5~ v렯7~ v!t!]Ś߱&90-˔ǡ]Ktռ*N*_Ox_ aqo05V`ß[9m؉W%6?^ W@(7ߘ-6Ee"yJ8?T@K9 w)0@(Y{A*s;%3&4>%efVΊ9J.ܚ- woCY7֭Bp]Y#H&?MJl ~nyv,DjRVkW& "O6 $ɭm-SHMx݌4b,9[mQ.縮YLRk/ϲ+!q N(8%_@NНtl¤%^ ;@w  w 'k,K{LTER>˶s|oPuп*uNoPI_}$A !jsߚ;NPs b!MZp3DKn9Tx9e_Zs#CI-4x^@|{N$ F}~w~w4}ߥ/W6!v[5G=o :w :cmY$$Ћn}pǖvΡđ+ք\o3隐3b _eE|dL(A4,EAC< Zr=f,#έj{h}'V & UfW⻂i6π>ʹBgAmf}?x4kYc9 mmfBGV3Gj )9ms_[ypKA]` !=QQ\ !FE?TšC3ƾX/MPz`8ۑIaOaa%'{ ¼E)AݤE>#g?Sn&ӉּrYg7^_Y>zmjWԄA{nVQS;nضk*2Th.R/d_˽0(~ao9 fXͳlκPs"eǡcv%[p&^YK]%g23̠QM؄AWno䭍݊#^yrŸ)J8Zj0=^LW &寷-Fp;(7bͿ3 e##$yWтQ*zT"8$5z^۸zlqh)"hD%-xTg[PVJ^(~burG\XqTnVCf~DB4h)'2;Y%6ڱ=nEosv̅io%P͎[ĹK^쒹ilz2̀F˴I`)Ѣja>XY4Uh;5*:^GZS;J!wxQlnR.jd:>L"QWyyAR;=5Q =%#v7s456M.@0wH'7i[ fܖ&DFx ^}IY?ahjjXw%]>-&q9ģImI!GW!<(Aӂ\^/bTPj,BB:SZ)-ΖyߋO]MmZtoO]M]#xE :9T[u׉3gD9)Q]F1B47yFɦ Q-'ˠn8i,(eh[kxmrҙ‡Hk)S}Xsif&e Zݚ5 JrB%Ok$̧$qjJ5h>%nm.]j;\4 cFaqVqhהenB) {ʆmSXJԬHJ"Mq12 >6H5luڱ`}#?xC8zL &'LęOMe3gUx &7"\MUZ}TئkގmR֬ٶll.ڐk%P7!` }f'! v 8 zXYq=BtCŒJ9[ >x&l§7u{R K@uB U3s%'ֲ{67ÛlA,$lFB~ǟxivshDꮜw ]f["< Ki'>l>1KrpG(%vY,hu[^0I9 hX؀IzҚ)x>`S6pDFBNZga)$&+ _2/$hp\[\6} :0 4v;͈pzGfx= 2h0Cs wԹe :9T H{dCسsː'=[$]4x5Hܦ@w$1AwKaT!-5q kmC貌?Oe4hڲ/r˲!9aeGܻdƞ9oYt=oO} bw 8z`|<a8ZQF2] $h(awP¢vHUЫ2OSD,(PrnN4 Ĺ! "hsZlRkAw^B]Ӡ2Brsؤ ꋻGE6 ֩3;ީ|?F;4!}i'lٵ|ӠX 2\#G9^ 6DYף:ZQ&q@[?&ZwM[7![0,z֝|*s:9#iDW*gϸ"QE7xT'GVgC< v[U6J5»`C@̪JlC pbbġtV`3'l`ӎRu{R鋍 ِt}!CzAgOW60 :nu'@kX2a5n/^&sy(2裡FԿ%*Zjb<Rpwka/A*q׀A=$_TAa`4!rCt{A7! u@)q{Hԟ$AN*Wx6ڟAl|_O,:a#ɰTp@uAd=0T2~3I·OJmq4h.%1a]TFA@Dx\| #Kkzp0++g, Ƒn J^>>ƟlFG{}Sa;sHF(hb&FrO[R? Zn*<~gMظT.A2u8Vt(f.lwW7(n+5AK- -9yC,I: _^4yz.!q:8r' ݩEӠxFaqUhVeWx|leǁOA;OƇkT9"X-!E`P| 2Uؚ.upn !ŗ?\H>4w"v'ASC:zímeɐ[oe%gOAY* #=X`PgAk\KEr\Pq~W5*P@\2Ь>a q_/R vB;$ TP焲d't7I̦*5uy9 p *΂V?d ?ެm2\ d,ml_WeNp >]Y{ev^P|+z*%)xw}C{n|8C嗏Envn6i^~kqb5H^ ѭR1)-v?51 L U L%O~)F ox'[yZ% F́C$ INtkFCz2TK>ʠyW;`G-g^'[5jV'[sʘ] R:4۰Sj}Weh^Un"^ lǴ6\L5NZ~#–rkuUd'\UuzV+mWs̗] 5͠N.Ysr=E+OZd aݿp =Ɩʮ_ f;Q*.ƶ̲ky%+1 *⿳рЄ\fuP[Z-m;gL o.u1|GzJjg{g=C9ȟڇNkT J'@U *I!M4& v'A8*Qk$ ZtPR+E onB=LVϵYdojmZW/.J e^ K[{?Lfh-rr% &y$6 M7?;muzfej7fwWvpu_c ]B/ڒ-HOPlxބ-[T0xba 0gl:(˭\"(g14@wE{I4xAhuWd!irVf"ِh'Q恏R:nZHD&O@6v|XءIsxm]~9$#w],jR$Z c!lݪp·%j8$q)Чo8(z쐿^܍"R:W^s}!ZEmV_X6U ^;\gE_D!:h[a60Uvܒvc Ccyf& s 5FL"*ڔ!>!WQ)] ~pHBY?f{`h(c `6+vN)2d͒醻| /Kp2xnϨ8!"sE; £JzS~K򝨉I < Į xTL%tJ-fZE^.^Ա4 DÝY̍Pmqʹ:ٱAC~ .RY؞-ej{{ұDiÓ\J < v]Nwj>` E_.Vd p 뛔j@Lp$:} lς;M"Nx(3>^>aGcY(9•xU)SWpD캀@Ki)x'Gō`l T@!#)O^΃W^PoaY}ު.x_s#e捼SW~#7$jh$`t-Lp$:( 4zP&cmIГx`Lk!MsI^`=DbT[^} #:  {Z}a@rgWOLRM]ҫh\ ݍuKKM$]/q+bT[[iGTlI{ ʠ;Et*ee[Ũ0/kz#̀o@BM6ŀH]f@K:7`ӄà<4zG?1,790`Mt&"Į >Ի1hٵDm\lc=mv#nO lO>n`QC|/o^p,ε\k=K~o+XVfkO;xhX c%fl.۪B/6Ư޴阉߅felv4 mu*?BNl? ep߃̄wA/gΓ iGA/@&jPDzEY1*f{#WA;ثa NY98B9Ţf͢0)B}.0z%eW(˸ \#j"^}Az +gihػ Ak #"u~]sD JQ"&OA[^J?Ӧߵ(V807N7N/yKU;u\ȎdfFffÓ3S3cScrb'kؖ&6N|]XwRAKH^K) wm{^ǗWQ+~vn~GOGd/x\)jxcjs9q?vx$iWwFKїO^ ܧl~cflv2;=55 nG[6Wbژ^PO7]T ׋W F/ѵVrm) nB';B\88)w / ?I.~ېx %[&L2*WxiYyNeMصnBv7 ilgtߠ:I.~vߠ7 YSAjgڱ晦Q9?M){@+>tC"NvghxJqwT58w*kk53-h%\o :w!Rz1a<9wg^$IBt^p;S].E'Ok=xO}W_Uc̐kŸ!dN=/r/ #gH,x]=2"r\QMjRZ`%l)*E~! ZJX֌e-͹zOqR ?!Mj|Eja,4\\d gm%/\g  ;A֨U*čm\FoPHYE3W8!ŷXh8VǨvh<,ڙloeجh3AzEe@*gp!MP,jxm_;kV.3a߾³W %J 5mslqRx?X9CPhc\[Oq,ŅR|burG\أ3*(i6mmq۵L/hX41k^ TqP`%*8"(Y* )8$ˠ gAU~!}CLY1Dcj_-]19 ŜOUd MQiE W+G{'^+\L94߇׼1N p0<Z}mȨ 4IFK\b3r2U/=٘K_p0sKAc =]էjRBiC5)!O؍~h\I*Ueq>a aֈ9asls /E [tIե:ns#áX?'v7à5v1]87\Ȳ7 \#'('>]j5$Oi;bWgѝ({FF[]o ZjRX[3#O/AŷYtyO8JEb ~,^e *KI i 'Էd.JC+綂Dmx@4qvBظiW.5Wo_ZK~Op@"9;A_oĝ[je2(lg[d(K(<#PO6Dݯ+Â*a%u2?ӕF)<_1?/E]vHݭnK9Gؓ(lT FjNoVrjWEg`NR'')*q&v'AO*k]w{P]h Se6R$LR0 6f?P4k;EdGK8zFM8iWC^< ZjEK nhVjXy0m~ua.t%mn;d#QY5- Z=vA^pzXTX 5״vVძ)΍u.\Wzr)2`Z EcP!4-n ݌[]?0 :,* G$.I VOp;{ÈYK֎~$15:Бطpx#qOr hFJ:K8:]~alyh\)yU2͂U'4srGV`Bym)uH!C2-W7`ZՌ/V- o|9rM(inh-=ͰngAqqUq6-(9.-,\1`1u`JՄ%!g1Z9 pY?o,;\Z*X zW%Խ CGS`ط*R}Cb(C<ҽ!ic?3 %eLP[x=B0qPE*>)C4D;x,-t56UH{ Q?j||a75TeH cuv7 Jbe_,7ԁ} jL PNJx_{v\z*ò6a'2v6&OrG#)%9dqkoB]M:g!a,Aaf&eXlgg记#TZdj4Jb/s$̯Lewĥd@_6v &`MK[8Tt'\/&tJB&t5wwG}TvwB7 =<&씌E-Qw?t5Cw6GD*%G+%kӃW*xE/J֙t)A/߹t]U5i("|m$b{:]^LJ7Z BKiBI붧2LZ?ҦKhKaQUܖm}]vӠ5. _nLuGg9#;XQ|+QA4zn wF'S0&mM iA҂Fj]G`%$N!;֌ػCiRCAmg4zfs紽+w4U-52}O)=AlU}O)=r܍ {a/2l5oyϞ^}_gn{1PLwE`yx$G4&W+0r|WDU#TT9(̤hquIBcru>.#WhC,VQSKFU[G![8A@ ?LBNh2sI%p'oݰZAЃͷ`bCL#itt.Bb<>TYj[a J8%<ļ ̋`QƏjZSdTfqGNC><PP>-(uTm@y(QnRK<]tNX:onBiHt"R&[S &5iD^H !LDa^Q([t&MDnxp/Uzp-C2(]&NDĮ x(&3a'"Tlp""9<{J{Ꚉ]5 :QgnpL%-6(#,lGbO{G#lDi _OL@orLF_  ldDڀ&sH,{f>4k*o$^?#4m.4 ̂nB&vջt_&f S,R$pap宕K(%(_ gFIvhC~~]{%#IHT`JrfWàߛqHv,IQ}J2#_U;9E*ڞC9TCϡJ{k^}UDBXrL%_f@K v)O a&r3 -QL'(E# t!TsJg{u6Niz{`k xۢ6 -eu^Iԏu)KAZ;ݤ5`OPetKQ'敜mY%RބWU=ݼy!ّڰnvL3əGY@2G@f]Si@NFӠ5 aʬJ.^0|K0 |nRA]Бh-$Y9o-|ϫ‡yER[ jSBz/"[]7YgSwL("FU9E:%Ɨ5 KA!nReA V\yW&$YMEP#=n2 0$2"- ]/;^ uW7j*aR[G8- :SBjSIlBTZdpYg6jxL>"Ee_34gZRݜi7/!qhRC#V:+{bӠ6-Ć<-},b_32ռbxGz"޺ v{␓rC]1`^m-[B3Car;i}8Hh'z팤*Z=^rFh/S!9*gZ}i;-S$A-Nނj CNZ'e%0 Z*v-;9pj@}R&n8q Nber;2vJ]>XYillõ  Z#Hm > -=&J>FFf@?RegQ")OA'=`(xNepvfpEid6]U4Jfa7񞛤6pVķ$k״a%f_bN٦D|GM$VeRJT$ hJ% @iRrmtM)3og@KdTu?͠a]QITeh-3xTðuGv|ho%1dY㍇؝^-װ֒vBF-qQ-`W/Ip*pBƤ!.ML@Ș].lI9Q9Iaל[G󕶚?AYdmBBrݸh8.^Tn]1 (oRnȹ,J\Tvu9,@fBnv{rpoN&HUš!C=l_cl!<#faau,W0fhcCǔ͌؍@ϩ/þO۰|UIs Z=2'>_?aj$ :]KOJ n@}Ճ{D{;Z zx@0WJb D(6) !Bë,:Ȝ e+Kռx%cӜ_/[8ݻ/o{o֭uoI f9by;sK[rofg//,"렅XwZ#دѭRQI>`@޶a3+??#ӥ w("32_idQwgmmJEr\[׬F2Q U~a'/X2$'eq=ܱ oU 4E-pWf~vcݶfq񂰍Ζ~f]va;vP)WB|LۄrGS*CqVʧaOK=K)5 tCy/pv} ~{icG}*)]VJ;E;ѾRU7JUf?$n}kFKїO^ l~cflv2;=55WsEou{y0j韆?ҏoQڴWgmY۳W~/^l9Y4_7k oMWY^_-{w߻b7]4󎕯mk\|5\ކp}ak7P0JtT{/Ws}l̬ '=|‹/+ƓJclue/man/0000755000175100001440000000000015144357404012002 5ustar hornikusersclue/man/sumt.Rd0000644000175100001440000000650215144341431013255 0ustar hornikusers\name{sumt} \alias{sumt} \title{Sequential Unconstrained Minimization Technique} \description{ Solve constrained optimization problems via the Sequential Unconstrained Minimization Technique (\acronym{SUMT}). } \usage{ sumt(x0, L, P, grad_L = NULL, grad_P = NULL, method = NULL, eps = NULL, q = NULL, verbose = NULL, control = list()) } \arguments{ \item{x0}{a list of starting values, or a single starting value.} \item{L}{a function to minimize.} \item{P}{a non-negative penalty function such that \eqn{P(x)} is zero iff the constraints are satisfied.} \item{grad_L}{a function giving the gradient of \code{L}, or \code{NULL} (default).} \item{grad_P}{a function giving the gradient of \code{P}, or \code{NULL} (default).} \item{method}{a character string, or \code{NULL}. If not given, \code{"CG"} is used. If equal to \code{"nlm"}, minimization is carried out using \code{\link[stats]{nlm}}. Otherwise, \code{\link[stats]{optim}} is used with \code{method} as the given method.} \item{eps}{the absolute convergence tolerance. The algorithm stops if the (maximum) distance between successive \code{x} values is less than \code{eps}. Defaults to \code{sqrt(.Machine$double.eps)}.} \item{q}{a double greater than one controlling the growth of the \eqn{\rho_k} as described in \bold{Details}. Defaults to 10.} \item{verbose}{a logical indicating whether to provide some output on minimization progress. Defaults to \code{getOption("verbose")}.} \item{control}{a list of control parameters to be passed to the minimization routine in case \code{optim} is used.} } \details{ The Sequential Unconstrained Minimization Technique is a heuristic for constrained optimization. To minimize a function \eqn{L} subject to constraints, one employs a non-negative function \eqn{P} penalizing violations of the constraints, such that \eqn{P(x)} is zero iff \eqn{x} satisfies the constraints. One iteratively minimizes \eqn{L(x) + \rho_k P(x)}, where the \eqn{\rho} values are increased according to the rule \eqn{\rho_{k+1} = q \rho_k} for some constant \eqn{q > 1}, until convergence is obtained in the sense that the Euclidean distance between successive solutions \eqn{x_k} and \eqn{x_{k+1}} is small enough. Note that the \dQuote{solution} \eqn{x} obtained does not necessarily satisfy the constraints, i.e., has zero \eqn{P(x)}. Note also that there is no guarantee that global (approximately) constrained optima are found. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the algorithm. The unconstrained minimizations are carried out by either \code{\link[stats]{optim}} or \code{\link[stats]{nlm}}, using analytic gradients if both \code{grad_L} and \code{grad_P} are given, and numeric ones otherwise. If more than one starting value is given, the solution with the minimal augmented criterion function value is returned. } \value{ A list inheriting from class \code{"sumt"}, with components \code{x}, \code{L}, \code{P}, and \code{rho} giving the solution obtained, the value of the criterion and penalty function at \code{x}, and the final \eqn{\rho} value used in the augmented criterion function. } \references{ \bibshow{Fiacco+McCormick:1968} } \keyword{optimize} clue/man/cl_membership.Rd0000644000175100001440000000462312211412501015065 0ustar hornikusers\name{cl_membership} \alias{cl_membership} \alias{as.cl_membership} \title{Memberships of Partitions} \description{ Compute the memberships values for objects representing partitions. } \usage{ cl_membership(x, k = n_of_classes(x)) as.cl_membership(x) } \arguments{ \item{x}{an R object representing a partition of objects (for \code{cl_membership}) or raw memberships or class ids (for \code{as.cl_membership}).} \item{k}{an integer giving the number of columns (corresponding to class ids) to be used in the membership matrix. Must not be less, and default to, the number of classes in the partition.} } \value{ An object of class \code{"cl_membership"} with the matrix of membership values. } \details{ \code{cl_membership} is a generic function. The methods provided in package \pkg{clue} handle the partitions obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself). \code{as.cl_membership} can be used for coercing \dQuote{raw} class ids (given as atomic vectors) or membership values (given as numeric matrices) to membership objects. } \seealso{ \code{\link{is.cl_partition}} } \examples{ ## Getting the memberships of a single soft partition. d <- dist(USArrests) hclust_methods <- c("ward", "single", "complete", "average", "mcquitty") hclust_results <- lapply(hclust_methods, function(m) hclust(d, m)) names(hclust_results) <- hclust_methods ## Now create an ensemble from the results. hens <- cl_ensemble(list = hclust_results) ## And add the results of agnes and diana. require("cluster") hens <- c(hens, list(agnes = agnes(d), diana = diana(d))) ## Create a dissimilarity object from this. d1 <- cl_dissimilarity(hens) ## And compute a soft partition. party <- fanny(d1, 2) round(cl_membership(party), 5) ## The "nearest" hard partition to this: as.cl_hard_partition(party) ## (which has the same class ids as cl_class_ids(party)). ## Extracting the memberships from the elements of an ensemble of ## partitions. pens <- cl_boot(USArrests, 30, 3) pens mems <- lapply(pens, cl_membership) ## And turning these raw memberships into an ensemble of partitions. pens <- cl_ensemble(list = lapply(mems, as.cl_partition)) pens pens[[length(pens)]] } \keyword{cluster} clue/man/fit_ultrametric_target.Rd0000644000175100001440000000621411304023137017022 0ustar hornikusers\name{fit_ultrametric_target} \alias{ls_fit_ultrametric_target} \alias{l1_fit_ultrametric_target} \title{Fit Dissimilarities to a Hierarchy} \description{ Find the ultrametric from a target equivalence class of hierarchies which minimizes weighted Euclidean or Manhattan dissimilarity to a given dissimilarity object. } \usage{ ls_fit_ultrametric_target(x, y, weights = 1) l1_fit_ultrametric_target(x, y, weights = 1) } \arguments{ \item{x}{a dissimilarity object inheriting from class \code{"\link{dist}"}.} \item{y}{a target hierarchy.} \item{weights}{a numeric vector or matrix with non-negative weights for obtaining a weighted fit. If a matrix, its numbers of rows and columns must be the same as the number of objects in \code{x}. Otherwise, it is recycled to the number of elements in \code{x}.} } \value{ An object of class \code{"\link{cl_ultrametric}"} containing the optimal ultrametric distances. } \details{ The target equivalence class consists of all dendrograms for which the corresponding \eqn{n}-trees are the same as the one corresponding to \code{y}. I.e., all splits are the same as for \code{y}, and optimization is over the height of the splits. The criterion function to be optimized over all ultrametrics from the equivalence class is \eqn{\sum w_{ij} |x_{ij} - u_{ij}|^p}, where \eqn{p = 2} in the Euclidean and \eqn{p = 1} in the Manhattan case, respectively. The optimum can be computed as follows. Suppose split \eqn{s} joins object classes \eqn{A} and \eqn{B}. As the ultrametric dissimilarities of all objects in \eqn{A} to all objects in \eqn{B} must be the same value, say, \eqn{u_{A,B} = u_s}, the contribution from the split to the criterion function is of the form \eqn{f_s(u_s) = \sum_{i \in A, j \in B} w_{ij} |x_{ij} - u_s|^p}. We need to minimize \eqn{\sum_s f_s(u_s)} under the constraint that the \eqn{u_s} form a non-decreasing sequence, which is accomplished by using the Pool Adjacent Violator Algorithm (\acronym{PAVA}) using the weighted mean (\eqn{p = 2}) or weighted median (\eqn{p = 1}) for solving the blockwise optimization problems. } \seealso{ \code{\link{ls_fit_ultrametric}} for finding the ultrametric minimizing Euclidean dissimilarity (without fixing the splits). } \examples{ data("Phonemes") ## Note that the Phonemes data set has the consonant misclassification ## probabilities, i.e., the similarities between the phonemes. d <- as.dist(1 - Phonemes) ## Find the maximal dominated and miminal dominating ultrametrics by ## hclust() with single and complete linkage: y1 <- hclust(d, "single") y2 <- hclust(d, "complete") ## Note that these are quite different: cl_dissimilarity(y1, y2, "gamma") ## Now find the L2 optimal members of the respective dendrogram ## equivalence classes. u1 <- ls_fit_ultrametric_target(d, y1) u2 <- ls_fit_ultrametric_target(d, y2) ## Compute the L2 optimal ultrametric approximation to d. u <- ls_fit_ultrametric(d) ## And compare ... cl_dissimilarity(cl_ensemble(Opt = u, Single = u1, Complete = u2), d) ## The solution obtained via complete linkage is quite close: cl_agreement(u2, u, "cophenetic") } \keyword{cluster} \keyword{optimize} clue/man/cl_classes.Rd0000644000175100001440000000126711304023137014375 0ustar hornikusers\name{cl_classes} \alias{cl_classes} \title{Cluster Classes} \description{ Extract the classes in a partition or hierarchy. } \usage{ cl_classes(x) } \arguments{ \item{x}{an R object representing a partition or hierarchy of objects.} } \value{ A list inheriting from \code{"cl_classes_of_objects"} of vectors indicating the classes. } \details{ For partitions, the classes are the equivalence classes (\dQuote{clusters}) of the partition; for soft partitions, the classes of the nearest hard partition are used. For hierarchies represented by trees, the classes are the sets of objects corresponding to (joined at or split by) the nodes of the tree. } \keyword{cluster} clue/man/cl_ultrametric.Rd0000644000175100001440000000440311304023137015266 0ustar hornikusers\name{cl_ultrametric} \alias{cl_ultrametric} \alias{as.cl_ultrametric} \title{Ultrametrics of Hierarchies} \description{ Compute the ultrametric distances for objects representing (total indexed) hierarchies. } \usage{ cl_ultrametric(x, size = NULL, labels = NULL) as.cl_ultrametric(x) } \arguments{ \item{x}{an R object representing a (total indexed) hierarchy of objects.} \item{size}{an integer giving the number of objects in the hierarchy.} \item{labels}{a character vector giving the names of the objects in the hierarchy.} } \value{ An object of class \code{"cl_ultrametric"} containing the ultrametric distances. } \details{ If \code{x} is not an ultrametric or a hierarchy with an ultrametric representation, \code{cl_ultrametric} uses \code{\link[stats]{cophenetic}} to obtain the ultrametric (also known as cophenetic) distances from the hierarchy, which in turn by default calls the S3 generic \code{\link[stats]{as.hclust}} on the hierarchy. Support for a class which represents hierarchies can thus be added by providing \code{as.hclust} methods for this class. In R 2.1.0 or better, \code{cophenetic} is an S3 generic as well, and one can also more directly provide methods for this if necessary. \code{as.cl_ultrametric} is a generic function which can be used for coercing \emph{raw} (non-classed) ultrametrics, represented as numeric vectors (of the lower-half entries) or numeric matrices, to ultrametric objects. Ultrametric objects are implemented as symmetric proximity objects with a dissimilarity interpretation so that self-proximities are zero, and inherit from classes \code{"\link{cl_dissimilarity}"} and \code{"cl_proximity"}. See section \bold{Details} in the documentation for \code{\link{cl_dissimilarity}} for implications. Ultrametric objects can also be coerced to classes \code{"\link[stats]{dendrogram}"} and \code{"\link[stats]{hclust}"}, and hence in particular use the \code{plot} methods for these classes. By default, plotting an ultrametric object uses the plot method for dendrograms. } \seealso{ \code{\link{is.cl_hierarchy}} } \examples{ hc <- hclust(dist(USArrests)) u <- cl_ultrametric(hc) ## Subscripting. u[1 : 5, 1 : 5] u[1 : 5, 6 : 7] ## Plotting. plot(u) } \keyword{cluster} clue/man/solve_LSAP.Rd0000644000175100001440000000314215144342435014236 0ustar hornikusers\name{solve_LSAP} \encoding{UTF-8} \alias{solve_LSAP} \title{Solve Linear Sum Assignment Problem} \description{ Solve the linear sum assignment problem using the Hungarian method. } \usage{ solve_LSAP(x, maximum = FALSE) } \arguments{ \item{x}{a matrix with nonnegative entries and at least as many columns as rows.} \item{maximum}{a logical indicating whether to minimize of maximize the sum of assigned costs.} } \details{ If \eqn{nr} and \eqn{nc} are the numbers of rows and columns of \code{x}, \code{solve_LSAP} finds an optimal \emph{assignment} of rows to columns, i.e., a one-to-one map \code{p} of the numbers from 1 to \eqn{nr} to the numbers from 1 to \eqn{nc} (a permutation of these numbers in case \code{x} is a square matrix) such that \eqn{\sum_{i=1}^{nr} x[i, p[i]]} is minimized or maximized. This assignment can be found using a linear program (and package \pkg{lpSolve} provides a function \code{lp.assign} for doing so), but typically more efficiently and provably in polynomial time \eqn{O(n^3)} using primal-dual methods such as the so-called Hungarian method (see the references). } \value{ An object of class \code{"solve_LSAP"} with the optimal assignment of rows to columns. } \references{ \bibshow{Papadimitriou+Steiglitz:1982} } \author{ Walter Böhm \email{Walter.Boehm@wu.ac.at} kindly provided C code implementing the Hungarian method. } \examples{ x <- matrix(c(5, 1, 4, 3, 5, 2, 2, 4, 4), nrow = 3) solve_LSAP(x) solve_LSAP(x, maximum = TRUE) ## To get the optimal value (for now): y <- solve_LSAP(x) sum(x[cbind(seq_along(y), y)]) } \keyword{optimize} clue/man/cl_bag.Rd0000644000175100001440000000571415144340740013502 0ustar hornikusers\name{cl_bag} \alias{cl_bag} \title{Bagging for Clustering} \description{ Construct partitions of objects by running a base clustering algorithm on bootstrap samples from a given data set, and \dQuote{suitably} aggregating these primary partitions. } \usage{ cl_bag(x, B, k = NULL, algorithm = "kmeans", parameters = NULL, method = "DFBC1", control = NULL) } \arguments{ \item{x}{the data set of objects to be clustered, as appropriate for the base clustering algorithm.} \item{B}{an integer giving the number of bootstrap replicates.} \item{k}{\code{NULL} (default), or an integer giving the number of classes to be used for a partitioning base algorithm.} \item{algorithm}{a character string or function specifying the base clustering algorithm.} \item{parameters}{a named list of additional arguments to be passed to the base algorithm.} \item{method}{a character string indicating the bagging method to use. Currently, only method \code{"DFBC1"} is available, which implements algorithm \emph{BagClust1} in \bibcitet{Dudoit+Fridlyand:2003}.} \item{control}{a list of control parameters for the aggregation. Currently, not used.} } \value{ An R object representing a partition of the objects given in \code{x}. } \details{ Bagging for clustering is really a rather general conceptual framework than a specific algorithm. If the primary partitions generated in the bootstrap stage form a cluster ensemble (so that class memberships of the objects in \code{x} can be obtained), consensus methods for cluster ensembles (as implemented, e.g., in \code{\link{cl_consensus}} and \code{\link{cl_medoid}}) can be employed for the aggregation stage. In particular, (possibly new) bagging algorithms can easily be realized by directly running \code{\link{cl_consensus}} on the results of \code{\link{cl_boot}}. In BagClust1, aggregation proceeds by generating a reference partition by running the base clustering algorithm on the whole given data set, and averaging the ensemble memberships after optimally matching them to the reference partition (in fact, by minimizing Euclidean dissimilarity, see \code{\link{cl_dissimilarity}}). If the base clustering algorithm yields prototypes, aggregation can be based on clustering these. This is the idea underlying the \dQuote{Bagged Clustering} algorithm introduced in \bibcitet{Leisch:1999} and implemented by function \code{\link[e1071]{bclust}} in package \pkg{e1071}. } \references{ \bibshow{Dudoit+Fridlyand:2003, Leisch:1999} } \examples{ set.seed(1234) ## Run BagClust1 on the Cassini data. data("Cassini") party <- cl_bag(Cassini$x, 50, 3) plot(Cassini$x, col = cl_class_ids(party), xlab = "", ylab = "") ## Actually, using fuzzy c-means as a base learner works much better: if(require("e1071", quietly = TRUE)) { party <- cl_bag(Cassini$x, 20, 3, algorithm = "cmeans") plot(Cassini$x, col = cl_class_ids(party), xlab = "", ylab = "") } } \keyword{cluster} clue/man/cl_prototypes.Rd0000644000175100001440000000265311304023137015170 0ustar hornikusers\name{cl_prototypes} \alias{cl_prototypes} \title{Partition Prototypes} \description{ Determine prototypes for the classes of an R object representing a partition. } \usage{ cl_prototypes(x) } \arguments{ \item{x}{an R object representing a partition of objects.} } \details{ Many partitioning methods are based on prototypes (\dQuote{centers}, \dQuote{centroids}, \dQuote{medoids}, \dots). In typical cases, these are points in the feature space for the measurements on the objects to be partitioned, such that one can quantify the distance between the objects and the prototypes, and, e.g., classify objects to their closest prototype. This is a generic function. The methods provided in package \pkg{clue} handle the partitions obtained from clustering functions in the base R distribution, as well as packages \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{kernlab}, and \pkg{mclust} (and of course, \pkg{clue} itself). } \examples{ ## Show how prototypes ("centers") vary across k-means runs on ## bootstrap samples from the Cassini data. data("Cassini") nr <- NROW(Cassini$x) out <- replicate(50, { kmeans(Cassini$x[sample(nr, replace = TRUE), ], 3) }, simplify = FALSE) ## Plot the data points in light gray, and the prototypes found. plot(Cassini$x, col = gray(0.8)) points(do.call("rbind", lapply(out, cl_prototypes)), pch = 19) } \keyword{cluster} clue/man/Kinship82_Consensus.Rd0000644000175100001440000000226415144337313016111 0ustar hornikusers\name{Kinship82_Consensus} \alias{Kinship82_Consensus} \title{Gordon-Vichi Kinship82 Consensus Partition Data} \description{ The soft (\dQuote{fuzzy}) consensus partitions for the Rosenberg-Kim kinship terms partition data given in \bibcitet{Gordon+Vichi:2001}. } \usage{data("Kinship82_Consensus")} \format{ A named cluster ensemble of three soft partitions of the 15 kinship terms into three classes. } \details{ The elements of the ensemble are named \code{"MF1"}, \code{"MF2"}, and \code{"JMF"}, and correspond to the consensus partitions obtained by applying models 1, 2, and 3 in \bibcitet{Gordon+Vichi:2001} to the kinship terms partition data in \bibcitet{Rosenberg:1982}, which are available as data set \code{\link{Kinship82}}. } \source{ Table 6 in \bibcitet{Gordon+Vichi:2001}. } \references{ \bibshow{Gordon+Vichi:2001, Rosenberg:1982} } \examples{ ## Load the consensus partitions. data("Kinship82_Consensus") ## Fuzziness using the Partition Coefficient. cl_fuzziness(Kinship82_Consensus) ## (Corresponds to 1 - F in the source.) ## Dissimilarities: cl_dissimilarity(Kinship82_Consensus) cl_dissimilarity(Kinship82_Consensus, method = "comem") } \keyword{datasets} clue/man/partition.Rd0000644000175100001440000000525312734174303014304 0ustar hornikusers\name{partition} \alias{cl_partition} % class ... \alias{is.cl_partition} \alias{is.cl_hard_partition} \alias{is.cl_soft_partition} \alias{cl_hard_partition} % class ... \alias{as.cl_partition} \alias{as.cl_hard_partition} \title{Partitions} \description{ Determine whether an R object represents a partition of objects, or coerce to an R object representing such.} \usage{ is.cl_partition(x) is.cl_hard_partition(x) is.cl_soft_partition(x) as.cl_partition(x) as.cl_hard_partition(x) } \arguments{ \item{x}{an R object.} } \value{ For the testing functions, a logical indicating whether the given object represents a clustering of objects of the respective kind. For the coercion functions, a container object inheriting from \code{"cl_partition"}, with a suitable representation of the partition given by \code{x}. } \details{ \code{is.cl_partition} and \code{is.cl_hard_partition} are generic functions. The methods provided in package \pkg{clue} handle the partitions obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself). \code{is.cl_soft_partition} gives true iff \code{is.cl_partition} is true and \code{is.cl_hard_partition} is false. \code{as.cl_partition} returns an object of class \code{"cl_partition"} \dQuote{containing} the given object \code{x} if this already represents a partition (i.e., \code{is.cl_partition(x)} is true), or the memberships obtained from \code{x} via \code{\link{as.cl_membership}}. \code{as.cl_hard_partition(x)} returns an object which has class \code{"cl_hard_partition"} and inherits from \code{"cl_partition"}, and contains \code{x} if it already represents a hard partition (i.e., provided that \code{is.cl_hard_partition(x)} is true), or the class ids obtained from \code{x}, using \code{x} if this is an atomic vector of raw class ids, or, if \code{x} represents a soft partition or is a raw matrix of membership values, using the class ids of the \emph{nearest hard partition}, defined by taking the class ids of the (first) maximal membership values. Conceptually, partitions and hard partitions are \emph{virtual} classes, allowing for a variety of representations. There are group methods for comparing partitions and computing their minimum, maximum, and range based on the meet and join operations, see \code{\link{cl_meet}}. } \examples{ data("Cassini") pcl <- kmeans(Cassini$x, 3) is.cl_partition(pcl) is.cl_hard_partition(pcl) is.cl_soft_partition(pcl) } \keyword{cluster} clue/man/cl_tabulate.Rd0000644000175100001440000000105311304023137014532 0ustar hornikusers\name{cl_tabulate} \alias{cl_tabulate} \title{Tabulate Vector Objects} \description{Tabulate the unique values in vector objects.} \usage{ cl_tabulate(x) } \arguments{ \item{x}{a vector.} } \value{ A data frame with components: \item{values}{the unique values.} \item{counts}{an integer vector with the number of times each of the unique values occurs in \code{x}.} } \examples{ data("Kinship82") tab <- cl_tabulate(Kinship82) ## The counts: tab$counts ## The most frequent partition: tab$values[[which.max(tab$counts)]] } \keyword{utilities} clue/man/CKME.Rd0000644000175100001440000000071011304023137012771 0ustar hornikusers\name{CKME} \alias{CKME} \title{Cassini Data Partitions Obtained by K-Means} \description{ A cluster ensemble of 50 \eqn{k}-means partitions of the Cassini data into three classes. } \usage{data("CKME")} \format{ A cluster ensemble of 50 (\eqn{k}-means) partitions. } \details{ The ensemble was generated via \preformatted{ require("clue") data("Cassini") set.seed(1234) CKME <- cl_boot(Cassini$x, 50, 3) } } \keyword{datasets} clue/man/cl_consensus.Rd0000644000175100001440000003567115144341204014772 0ustar hornikusers\name{cl_consensus} \alias{cl_consensus} \title{Consensus Partitions and Hierarchies} \description{ Compute the consensus clustering of an ensemble of partitions or hierarchies. } \usage{ cl_consensus(x, method = NULL, weights = 1, control = list()) } \arguments{ \item{x}{an ensemble of partitions or hierarchies, or something coercible to that (see \code{\link{cl_ensemble}}).} \item{method}{a character string specifying one of the built-in methods for computing consensus clusterings, or a function to be taken as a user-defined method, or \code{NULL} (default value). If a character string, its lower-cased version is matched against the lower-cased names of the available built-in methods using \code{\link{pmatch}}. See \bold{Details} for available built-in methods and defaults.} \item{weights}{a numeric vector with non-negative case weights. Recycled to the number of elements in the ensemble given by \code{x} if necessary.} \item{control}{a list of control parameters. See \bold{Details}.} } \value{ The consensus partition or hierarchy. } \details{ Consensus clusterings \dQuote{synthesize} the information in the elements of a cluster ensemble into a single clustering, often by minimizing a criterion function measuring how dissimilar consensus candidates are from the (elements of) the ensemble (the so-called \dQuote{optimization approach} to consensus clustering). The most popular criterion functions are of the form \eqn{L(x) = \sum w_b d(x_b, x)^p}, where \eqn{d} is a suitable dissimilarity measure (see \code{\link{cl_dissimilarity}}), \eqn{w_b} is the case weight given to element \eqn{x_b} of the ensemble, and \eqn{p \ge 1}. If \eqn{p = 1} and minimization is over all possible base clusterings, a consensus solution is called a \emph{median} of the ensemble; if minimization is restricted to the elements of the ensemble, a consensus solution is called a \emph{medoid} (see \code{\link{cl_medoid}}). For \eqn{p = 2}, we obtain \emph{least squares} consensus partitions and hierarchies (generalized means). See also \bibcitet{R:Gordon:1999} for more information. If all elements of the ensemble are partitions, the built-in consensus methods compute consensus partitions by minimizing a criterion of the form \eqn{L(x) = \sum w_b d(x_b, x)^p} over all hard or soft partitions \eqn{x} with a given (maximal) number \eqn{k} of classes. Available built-in methods are as follows. \describe{ \item{\code{"SE"}}{a fixed-point algorithm for obtaining \emph{soft} least squares Euclidean consensus partitions (i.e., for minimizing \eqn{L} with Euclidean dissimilarity \eqn{d} and \eqn{p = 2} over all soft partitions with a given maximal number of classes). This iterates between individually matching all partitions to the current approximation to the consensus partition, and computing the next approximation as the membership matrix closest to a suitable weighted average of the memberships of all partitions after permuting their columns for the optimal matchings of class ids. The following control parameters are available for this method. \describe{ \item{\code{k}}{an integer giving the number of classes to be used for the least squares consensus partition. By default, the maximal number of classes in the ensemble is used.} \item{\code{maxiter}}{an integer giving the maximal number of iterations to be performed. Defaults to 100.} \item{\code{nruns}}{an integer giving the number of runs to be performed. Defaults to 1.} \item{\code{reltol}}{the relative convergence tolerance. Defaults to \code{sqrt(.Machine$double.eps)}.} \item{\code{start}}{a matrix with number of rows equal to the number of objects of the cluster ensemble, and \eqn{k} columns, to be used as a starting value, or a list of such matrices. By default, suitable random membership matrices are used.} \item{\code{verbose}}{a logical indicating whether to provide some output on minimization progress. Defaults to \code{getOption("verbose")}.} } In the case of multiple runs, the first optimum found is returned. This method can also be referred to as \code{"soft/euclidean"}. } \item{\code{"GV1"}}{the fixed-point algorithm for the \dQuote{first model} in \bibcitet{Gordon+Vichi:2001} for minimizing \eqn{L} with \eqn{d} being GV1 dissimilarity and \eqn{p = 2} over all soft partitions with a given maximal number of classes. This is similar to \code{"SE"}, but uses GV1 rather than Euclidean dissimilarity. Available control parameters are the same as for \code{"SE"}. } \item{\code{"DWH"}}{an extension of the greedy algorithm in \bibcitet{Dimitriadou+Weingessel+Hornik:2002} for (approximately) obtaining soft least squares Euclidean consensus partitions. The reference provides some structure theory relating finding the consensus partition to an instance of the multiple assignment problem, which is known to be NP-hard, and suggests a simple heuristic based on successively matching an individual partition \eqn{x_b} to the current approximation to the consensus partition, and compute the memberships of the next approximation as a weighted average of those of the current one and of \eqn{x_b} after permuting its columns for the optimal matching of class ids. The following control parameters are available for this method. \describe{ \item{\code{k}}{an integer giving the number of classes to be used for the least squares consensus partition. By default, the maximal number of classes in the ensemble is used.} \item{\code{order}}{a permutation of the integers from 1 to the size of the ensemble, specifying the order in which the partitions in the ensemble should be aggregated. Defaults to using a random permutation (unlike the reference, which does not permute at all).} } } \item{\code{"HE"}}{a fixed-point algorithm for obtaining \emph{hard} least squares Euclidean consensus partitions (i.e., for minimizing \eqn{L} with Euclidean dissimilarity \eqn{d} and \eqn{p = 2} over all hard partitions with a given maximal number of classes.) Available control parameters are the same as for \code{"SE"}. This method can also be referred to as \code{"hard/euclidean"}. } \item{\code{"SM"}}{a fixed-point algorithm for obtaining \emph{soft} median Manhattan consensus partitions (i.e., for minimizing \eqn{L} with Manhattan dissimilarity \eqn{d} and \eqn{p = 1} over all soft partitions with a given maximal number of classes). Available control parameters are the same as for \code{"SE"}. This method can also be referred to as \code{"soft/manhattan"}. } \item{\code{"HM"}}{a fixed-point algorithm for obtaining \emph{hard} median Manhattan consensus partitions (i.e., for minimizing \eqn{L} with Manhattan dissimilarity \eqn{d} and \eqn{p = 1} over all hard partitions with a given maximal number of classes). Available control parameters are the same as for \code{"SE"}. This method can also be referred to as \code{"hard/manhattan"}. } \item{\code{"GV3"}}{a \acronym{SUMT} algorithm for the \dQuote{third model} in \bibcitet{Gordon+Vichi:2001} for minimizing \eqn{L} with \eqn{d} being co-membership dissimilarity and \eqn{p = 2}. (See \code{\link{sumt}} for more information on the \acronym{SUMT} approach.) This optimization problem is equivalent to finding the membership matrix \eqn{m} for which the sum of the squared differences between \eqn{C(m) = m m'} and the weighted average co-membership matrix \eqn{\sum_b w_b C(m_b)} of the partitions is minimal. Available control parameters are \code{method}, \code{control}, \code{eps}, \code{q}, and \code{verbose}, which have the same roles as for \code{\link{sumt}}, and the following. \describe{ \item{\code{k}}{an integer giving the number of classes to be used for the least squares consensus partition. By default, the maximal number of classes in the ensemble is used.} \item{\code{nruns}}{an integer giving the number of runs to be performed. Defaults to 1.} \item{\code{start}}{a matrix with number of rows equal to the size of the cluster ensemble, and \eqn{k} columns, to be used as a starting value, or a list of such matrices. By default, a membership based on a rank \eqn{k} approximation to the weighted average co-membership matrix is used.} } In the case of multiple runs, the first optimum found is returned. } \item{\code{"soft/symdiff"}}{a \acronym{SUMT} approach for minimizing \eqn{L = \sum w_b d(x_b, x)} over all soft partitions with a given maximal number of classes, where \eqn{d} is the Manhattan dissimilarity of the co-membership matrices (coinciding with symdiff partition dissimilarity in the case of hard partitions). Available control parameters are the same as for \code{"GV3"}. } \item{\code{"hard/symdiff"}}{an exact solver for minimizing \eqn{L = \sum w_b d(x_b, x)} over all hard partitions (possibly with a given maximal number of classes as specified by the control parameter \code{k}), where \eqn{d} is symdiff partition dissimilarity (so that soft partitions in the ensemble are replaced by their closest hard partitions), or equivalently, Rand distance or pair-bonds (Boorman-Arabie \eqn{D}) distance. The consensus solution is found via mixed linear or quadratic programming. } } By default, method \code{"SE"} is used for ensembles of partitions. If all elements of the ensemble are hierarchies, the following built-in methods for computing consensus hierarchies are available. \describe{ \item{\code{"euclidean"}}{an algorithm for minimizing \eqn{L(x) = \sum w_b d(x_b, x) ^ 2} over all dendrograms, where \eqn{d} is Euclidean dissimilarity. This is equivalent to finding the best least squares ultrametric approximation of the weighted average \eqn{d = \sum w_b u_b} of the ultrametrics \eqn{u_b} of the hierarchies \eqn{x_b}, which is attempted by calling \code{\link{ls_fit_ultrametric}} on \eqn{d} with appropriate control parameters. This method can also be referred to as \code{"cophenetic"}. } \item{\code{"manhattan"}}{a \acronym{SUMT} for minimizing \eqn{L = \sum w_b d(x_b, x)} over all dendrograms, where \eqn{d} is Manhattan dissimilarity. Available control parameters are the same as for \code{"euclidean"}. } \item{\code{"majority"}}{a hierarchy obtained from an extension of the majority consensus tree of \bibcitet{Margush+McMorris:1981}, which minimizes \eqn{L(x) = \sum w_b d(x_b, x)} over all dendrograms, where \eqn{d} is the symmetric difference dissimilarity. The unweighted \eqn{p}-majority tree is the \eqn{n}-tree (hierarchy in the strict sense) consisting of all subsets of objects contained in more than \eqn{100 p} percent of the \eqn{n}-trees \eqn{T_b} induced by the dendrograms, where \eqn{1/2 \le p < 1} and \eqn{p = 1/2} (default) corresponds to the standard majority tree. In the weighted case, it consists of all subsets \eqn{A} for which \eqn{\sum_{b: A \in T_b} w_b > W p}, where \eqn{W = \sum_b w_b}. We also allow for \eqn{p = 1}, which gives the \emph{strict consensus tree} consisting of all subsets contained in each of the \eqn{n}-trees. The majority dendrogram returned is a representation of the majority tree where all splits have height one. The fraction \eqn{p} can be specified via the control parameter \code{p}. } } By default, method \code{"euclidean"} is used for ensembles of hierarchies. If a user-defined consensus method is to be employed, it must be a function taking the cluster ensemble, the case weights, and a list of control parameters as its arguments, with formals named \code{x}, \code{weights}, and \code{control}, respectively. Most built-in methods use heuristics for solving hard optimization problems, and cannot be guaranteed to find a global minimum. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the methods. } \references{ \bibshow{Dimitriadou+Weingessel+Hornik:2002, Gordon+Vichi:2001, R:Gordon:1999, Margush+McMorris:1981} } \seealso{ \code{\link{cl_medoid}}, \code{\link[ape]{consensus}} } \examples{ ## Consensus partition for the Rosenberg-Kim kinship terms partition ## data based on co-membership dissimilarities. data("Kinship82") m1 <- cl_consensus(Kinship82, method = "GV3", control = list(k = 3, verbose = TRUE)) ## (Note that one should really use several replicates of this.) ## Value for criterion function to be minimized: sum(cl_dissimilarity(Kinship82, m1, "comem") ^ 2) ## Compare to the consensus solution given in Gordon & Vichi (2001). data("Kinship82_Consensus") m2 <- Kinship82_Consensus[["JMF"]] sum(cl_dissimilarity(Kinship82, m2, "comem") ^ 2) ## Seems we get a better solution ... ## How dissimilar are these solutions? cl_dissimilarity(m1, m2, "comem") ## How "fuzzy" are they? cl_fuzziness(cl_ensemble(m1, m2)) ## Do the "nearest" hard partitions fully agree? cl_dissimilarity(as.cl_hard_partition(m1), as.cl_hard_partition(m2)) ## Consensus partition for the Gordon and Vichi (2001) macroeconomic ## partition data based on Euclidean dissimilarities. data("GVME") set.seed(1) ## First, using k = 2 classes. m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 2, verbose = TRUE)) ## (Note that one should really use several replicates of this.) ## Value of criterion function to be minimized: sum(cl_dissimilarity(GVME, m1, "GV1") ^ 2) ## Compare to the consensus solution given in Gordon & Vichi (2001). data("GVME_Consensus") m2 <- GVME_Consensus[["MF1/2"]] sum(cl_dissimilarity(GVME, m2, "GV1") ^ 2) ## Seems we get a slightly better solution ... ## But note that cl_dissimilarity(m1, m2, "GV1") ## and that the maximal deviation of the memberships is max(abs(cl_membership(m1) - cl_membership(m2))) ## so the differences seem to be due to rounding. ## Do the "nearest" hard partitions fully agree? table(cl_class_ids(m1), cl_class_ids(m2)) ## And now for k = 3 classes. m1 <- cl_consensus(GVME, method = "GV1", control = list(k = 3, verbose = TRUE)) sum(cl_dissimilarity(GVME, m1, "GV1") ^ 2) ## Compare to the consensus solution given in Gordon & Vichi (2001). m2 <- GVME_Consensus[["MF1/3"]] sum(cl_dissimilarity(GVME, m2, "GV1") ^ 2) ## This time we look much better ... ## How dissimilar are these solutions? cl_dissimilarity(m1, m2, "GV1") ## Do the "nearest" hard partitions fully agree? table(cl_class_ids(m1), cl_class_ids(m2)) } \keyword{cluster} clue/man/n_of_objects.Rd0000644000175100001440000000221212211412717014707 0ustar hornikusers\name{n_of_objects} \alias{n_of_objects} \title{Number of Objects in a Partition or Hierarchy} \description{Determine the number of objects from which a partition or hierarchy was obtained.} \usage{ n_of_objects(x) } \arguments{ \item{x}{an \R object representing a (hard of soft) partition or a hierarchy of objects, or dissimilarities between objects.} } \value{ An integer giving the number of objects. } \details{ This is a generic function. The methods provided in package \pkg{clue} handle the partitions and hierarchies obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{ape}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself). There is also a method for object dissimilarities which inherit from class \code{"\link{dist}"}. } \seealso{ \code{\link{is.cl_partition}}, \code{\link{is.cl_hierarchy}} } \examples{ data("Cassini") pcl <- kmeans(Cassini$x, 3) n_of_objects(pcl) hcl <- hclust(dist(USArrests)) n_of_objects(hcl) } \keyword{cluster} clue/man/cl_ensemble.Rd0000644000175100001440000000462311547637750014556 0ustar hornikusers\name{cl_ensemble} \alias{cl_ensemble} \alias{as.cl_ensemble} \alias{is.cl_ensemble} \title{Cluster Ensembles} \description{Creation and manipulation of cluster ensembles.} \usage{ cl_ensemble(..., list = NULL) as.cl_ensemble(x) is.cl_ensemble(x) } \arguments{ \item{\dots}{R objects representing clusterings of or dissimilarities between the same objects.} \item{list}{a list of R objects as in \code{\dots}.} \item{x}{for \code{as.cl_ensemble}, an R object as in \code{\dots}; for \code{is.cl_ensemble}, an arbitrary R object.} } \details{ \code{cl_ensemble} creates \dQuote{cluster ensembles}, which are realized as lists of clusterings (or dissimilarities) with additional class information, always inheriting from \code{"cl_ensemble"}. All elements of the ensemble must have the same number of objects. If all elements are partitions, the ensemble has class \code{"cl_partition_ensemble"}; if all elements are dendrograms, it has class \code{"cl_dendrogram_ensemble"} and inherits from \code{"cl_hierarchy_ensemble"}; if all elements are hierarchies (but not always dendrograms), it has class \code{"cl_hierarchy_ensemble"}. Note that empty or \dQuote{mixed} ensembles cannot be categorized according to the kind of elements they contain, and hence only have class \code{"cl_ensemble"}. The list representation makes it possible to use \code{lapply} for computations on the individual clusterings in (i.e., the components of) a cluster ensemble. Available methods for cluster ensembles include those for subscripting, \code{c}, \code{rep}, and \code{print}. There is also a \code{plot} method for ensembles for which all elements can be plotted (currently, additive trees, dendrograms and ultrametrics). } \value{ \code{cl_ensemble} returns a list of the given clusterings or dissimilarities, with additional class information (see \bold{Details}). } \examples{ d <- dist(USArrests) hclust_methods <- c("ward", "single", "complete", "average", "mcquitty") hclust_results <- lapply(hclust_methods, function(m) hclust(d, m)) names(hclust_results) <- hclust_methods ## Now create an ensemble from the results. hens <- cl_ensemble(list = hclust_results) hens ## Subscripting. hens[1 : 3] ## Replication. rep(hens, 3) ## Plotting. plot(hens, main = names(hens)) ## And continue to analyze the ensemble, e.g. round(cl_dissimilarity(hens, method = "gamma"), 4) } \keyword{cluster} clue/man/cl_margin.Rd0000644000175100001440000000165011304023137014211 0ustar hornikusers\name{cl_margin} \alias{cl_margin} \title{Membership Margins} \description{ Compute the \emph{margin} of the memberships of a partition, i.e., the difference between the largest and second largest membership values of the respective objects. } \usage{ cl_margin(x) } \arguments{ \item{x}{an \R object representing a partition of objects.} } \details{ For hard partitions, the margins are always 1. For soft partitions, the margins may be taken as an indication of the \dQuote{sureness} of classifying an object to the class with maximum membership value. } \examples{ data("GVME") ## Look at the classes obtained for 1980: split(cl_object_names(GVME[["1980"]]), cl_class_ids(GVME[["1980"]])) ## Margins: x <- cl_margin(GVME[["1980"]]) ## Add names, and sort: names(x) <- cl_object_names(GVME[["1980"]]) sort(x) ## Note the "uncertainty" of assigning Egypt to the "intermediate" class ## of nations. } \keyword{cluster} clue/man/l1_fit_ultrametric.Rd0000644000175100001440000001006415144355541016063 0ustar hornikusers\name{l1_fit_ultrametric} \alias{l1_fit_ultrametric} \title{Least Absolute Deviation Fit of Ultrametrics to Dissimilarities} \description{ Find the ultrametric with minimal absolute distance (Manhattan dissimilarity) to a given dissimilarity object. } \usage{ l1_fit_ultrametric(x, method = c("SUMT", "IRIP"), weights = 1, control = list()) } \arguments{ \item{x}{a dissimilarity object inheriting from or coercible to class \code{"\link{dist}"}.} \item{method}{a character string indicating the fitting method to be employed. Must be one of \code{"SUMT"} (default) or \code{"IRIP"}, or a unique abbreviation thereof.} \item{weights}{a numeric vector or matrix with non-negative weights for obtaining a weighted least squares fit. If a matrix, its numbers of rows and columns must be the same as the number of objects in \code{x}, and the lower diagonal part is used. Otherwise, it is recycled to the number of elements in \code{x}.} \item{control}{a list of control parameters. See \bold{Details}.} } \value{ An object of class \code{"\link{cl_ultrametric}"} containing the fitted ultrametric distances. } \details{ The problem to be solved is minimizing \deqn{L(u) = \sum_{i,j} w_{ij} |x_{ij} - u_{ij}|} over all \eqn{u} satisfying the ultrametric constraints (i.e., for all \eqn{i, j, k}, \eqn{u_{ij} \le \max(u_{ik}, u_{jk})}). This problem is known to be NP hard \bibcitep{Křivánek+Morávek:1986}. We provide two heuristics for solving this problem. Method \code{"SUMT"} implements a \acronym{SUMT} (Sequential Unconstrained Minimization Technique, see \code{\link{sumt}}) approach using the sign function for the gradients of the absolute value function. Available control parameters are \code{method}, \code{control}, \code{eps}, \code{q}, and \code{verbose}, which have the same roles as for \code{\link{sumt}}, and the following. \describe{ \item{\code{nruns}}{an integer giving the number of runs to be performed. Defaults to 1.} \item{\code{start}}{a single dissimilarity, or a list of dissimilarities to be employed as starting values.} } Method \code{"IRIP"} implements a variant of the Iteratively Reweighted Iterative Projection approach of \bibcitet{Smith:2001}, which attempts to solve the \eqn{L_1} problem via a sequence of weighted \eqn{L_2} problems, determining \eqn{u(t+1)} by minimizing the criterion function \deqn{\sum_{i,j} w_{ij} (x_{ij} - u_{ij})^2 / \max(|x_{ij} - u_{ij}(t)|, m)} with \eqn{m} a \dQuote{small} non-zero value to avoid zero divisors. We use the \acronym{SUMT} method of \code{\link{ls_fit_ultrametric}} for solving the weighted least squares problems. Available control parameters are as follows. \describe{ \item{\code{maxiter}}{an integer giving the maximal number of iteration steps to be performed. Defaults to 100.} \item{\code{eps}}{a nonnegative number controlling the iteration, which stops when the maximal change in \eqn{u} is less than \code{eps}. Defaults to \eqn{10^{-6}}.} \item{\code{reltol}}{the relative convergence tolerance. Iteration stops when the relative change in the \eqn{L_1} criterion is less than \code{reltol}. Defaults to \eqn{10^{-6}}.} \item{\code{MIN}}{the cutoff \eqn{m}. Defaults to \eqn{10^{-3}}.} \item{\code{start}}{a dissimilarity object to be used as the starting value for \eqn{u}.} \item{\code{control}}{a list of control parameters to be used by the method of \code{\link{ls_fit_ultrametric}} employed for solving the weighted \eqn{L_2} problems.} } One may need to adjust the default control parameters to achieve convergence. It should be noted that all methods are heuristics which can not be guaranteed to find the global minimum. } \seealso{ \code{\link{cl_consensus}} for computing least absolute deviation (Manhattan) consensus hierarchies; \code{\link{ls_fit_ultrametric}}. } \references{ \bibshow{Křivánek+Morávek:1986, Smith:2001} } \keyword{cluster} \keyword{optimize} clue/man/cl_fuzziness.Rd0000644000175100001440000000465215144342730015012 0ustar hornikusers\name{cl_fuzziness} \alias{cl_fuzziness} \title{Partition Fuzziness} \description{ Compute the fuzziness of partitions. } \usage{ cl_fuzziness(x, method = NULL, normalize = TRUE) } \arguments{ \item{x}{a cluster ensemble of partitions, or an R object coercible to such.} \item{method}{a character string indicating the fuzziness measure to be employed, or \code{NULL} (default), or a function to be taken as a user-defined method. Currently available built-in methods are \code{"PC"} (Partition Coefficient) and \code{"PE"} (Partition Entropy), with the default corresponding to the first one. If \code{method} is a character string, its lower-cased version is matched against the lower-cased names of the available built-in methods using \code{\link{pmatch}}.} \item{normalize}{a logical indicating whether the fuzziness measure should be normalized in a way that hard partitions have value 0, and \dQuote{completely fuzzy} partitions (where for all objects, all classes get the same membership) have value 1.} } \details{ If \eqn{m} contains the membership values of a partition, the (unnormalized) Partition Coefficient and Partition Entropy are given by \eqn{\sum_{n,i} m_{n,i}^2} and \eqn{\sum_{n,i} H(m_{n,i})}, respectively, where \eqn{H(u) = u \log u - (1-u) \log(1-u)}{u log(u) - (1-u) log(1-u)}. Note that the normalization used here is different from the normalizations typically found in the literature. If a user-defined fuzziness method is to be employed, is must be a function taking a matrix of membership values and a logical to indicate whether normalization is to be performed as its arguments (in that order; argument names are not used). } \value{ An object of class \code{"cl_fuzziness"} giving the fuzziness values. } \references{ \bibshow{Bezdek:1981} } \seealso{ Function \code{\link[e1071]{fclustIndex}} in package \pkg{e1071}, which also computes several other \dQuote{fuzzy cluster indexes} (typically based on more information than just the membership values). } \examples{ if(require("e1071", quietly = TRUE)) { ## Use an on-line version of fuzzy c-means from package e1071 if ## available. data("Cassini") pens <- cl_boot(Cassini$x, B = 15, k = 3, algorithm = "cmeans", parameters = list(method = "ufcl")) pens summary(cl_fuzziness(pens, "PC")) summary(cl_fuzziness(pens, "PE")) } } \keyword{cluster} clue/man/cl_medoid.Rd0000644000175100001440000000321215144343015014177 0ustar hornikusers\name{cl_medoid} \alias{cl_medoid} \title{Medoid Partitions and Hierarchies} \description{ Compute the medoid of an ensemble of partitions or hierarchies, i.e., the element of the ensemble minimizing the sum of dissimilarities to all other elements. } \usage{ cl_medoid(x, method = "euclidean") } \arguments{ \item{x}{an ensemble of partitions or hierarchies, or something coercible to that (see \code{\link{cl_ensemble}}).} \item{method}{a character string or a function, as for argument \code{method} of function \code{\link{cl_dissimilarity}}.} } \value{ The medoid partition or hierarchy. } \details{ Medoid clusterings are special cases of \dQuote{consensus} clusterings characterized as the solutions of an optimization problem. See \bibcitet{R:Gordon:1999} for more information. The dissimilarities \code{d} for determining the medoid are obtained by calling \code{cl_dissimilarity} with arguments \code{x} and \code{method}. The medoid can then be found as the (first) row index for which the row sum of \code{as.matrix(d)} is minimal. Modulo possible differences in the case of ties, this gives the same results as (the medoid obtained by) \code{\link[cluster]{pam}} in package \pkg{cluster}. } \references{ \bibshow{R:Gordon:1999} } \seealso{ \code{\link{cl_consensus}} } \examples{ ## An ensemble of partitions. data("CKME") pens <- CKME[1 : 20] m1 <- cl_medoid(pens) diss <- cl_dissimilarity(pens) require("cluster") m2 <- pens[[pam(diss, 1)$medoids]] ## Agreement of medoid consensus partitions. cl_agreement(m1, m2) ## Or, more straightforwardly: table(cl_class_ids(m1), cl_class_ids(m2)) } \keyword{cluster} clue/man/cl_pclust.Rd0000644000175100001440000001000215144343467014256 0ustar hornikusers\name{cl_pclust} \alias{cl_pclust} \title{Prototype-Based Partitions of Clusterings} \description{ Compute prototype-based partitions of a cluster ensemble by minimizing \eqn{\sum w_b u_{bj}^m d(x_b, p_j)^e}, the sum of the case-weighted and membership-weighted \eqn{e}-th powers of the dissimilarities between the elements \eqn{x_b} of the ensemble and the prototypes \eqn{p_j}, for suitable dissimilarities \eqn{d} and exponents \eqn{e}. } \usage{ cl_pclust(x, k, method = NULL, m = 1, weights = 1, control = list()) } \arguments{ \item{x}{an ensemble of partitions or hierarchies, or something coercible to that (see \code{\link{cl_ensemble}}).} \item{k}{an integer giving the number of classes to be used in the partition.} \item{method}{the consensus method to be employed, see \code{\link{cl_consensus}}.} \item{m}{a number not less than 1 controlling the softness of the partition (as the \dQuote{fuzzification parameter} of the fuzzy \eqn{c}-means algorithm). The default value of 1 corresponds to hard partitions obtained from a generalized \eqn{k}-means problem; values greater than one give partitions of increasing softness obtained from a generalized fuzzy \eqn{c}-means problem.} \item{weights}{a numeric vector of non-negative case weights. Recycled to the number of elements in the ensemble given by \code{x} if necessary.} \item{control}{a list of control parameters. See \bold{Details}.} } \value{ An object of class \code{"cl_partition"} representing the obtained \dQuote{secondary} partition by an object of class \code{"cl_pclust"}, which is a list containing at least the following components. \item{prototypes}{a cluster ensemble with the \eqn{k} prototypes.} \item{membership}{an object of class \code{"\link{cl_membership}"} with the membership values \eqn{u_{bj}}.} \item{cluster}{the class ids of the nearest hard partition.} \item{silhouette}{Silhouette information for the partition, see \code{\link[cluster]{silhouette}}.} \item{validity}{precomputed validity measures for the partition.} \item{m}{the softness control argument.} \item{call}{the matched call.} \item{d}{the dissimilarity function \eqn{d = d(x, p)} employed.} \item{e}{the exponent \eqn{e} employed.} } \details{ Partitioning is performed using \code{\link{pclust}} via a family constructed from \code{method}. The dissimilarities \eqn{d} and exponent \eqn{e} are implied by the consensus method employed, and inferred via a registration mechanism currently only made available to built-in consensus methods. The default methods compute Least Squares Euclidean consensus clusterings, i.e., use Euclidean dissimilarity \eqn{d} and \eqn{e = 2}. For \eqn{m = 1}, the partitioning procedure was introduced by \bibcitet{Gaul+Schader:1988} for \dQuote{Clusterwise Aggregation of Relations} (with the same domains), containing equivalence relations, i.e., hard partitions, as a special case. Available control parameters are as for \code{\link{pclust}}. The fixed point approach employed is a heuristic which cannot be guaranteed to find the global minimum (as this is already true for the computation of consensus clusterings). Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the base algorithm. } \references{ \bibshow{Bezdek:1981, Gaul+Schader:1988} } \examples{ ## Use a precomputed ensemble of 50 k-means partitions of the ## Cassini data. data("CKME") CKME <- CKME[1 : 30] # for saving precious time ... diss <- cl_dissimilarity(CKME) hc <- hclust(diss) plot(hc) ## This suggests using a partition with three classes, which can be ## obtained using cutree(hc, 3). Could use cl_consensus() to compute ## prototypes as the least squares consensus clusterings of the classes, ## or alternatively: set.seed(123) x1 <- cl_pclust(CKME, 3, m = 1) x2 <- cl_pclust(CKME, 3, m = 2) ## Agreement of solutions. cl_dissimilarity(x1, x2) table(cl_class_ids(x1), cl_class_ids(x2)) } \keyword{cluster} clue/man/lattice.Rd0000644000175100001440000001207115144355632013720 0ustar hornikusers\name{lattice} \encoding{UTF-8} \alias{cl_meet} \alias{cl_join} \alias{Ops.cl_partition} \alias{Summary.cl_partition} \alias{Ops.cl_dendrogram} \alias{Ops.cl_hierarchy} \alias{Summary.cl_hierarchy} \title{Cluster Lattices} \description{ Computations on the lattice of all (hard) partitions, or the lattice of all dendrograms, or the meet semilattice of all hierarchies (\eqn{n}-trees) of/on a set of objects: meet, join, and comparisons. } \usage{ cl_meet(x, y) cl_join(x, y) } \arguments{ \item{x}{an ensemble of partitions or dendrograms or hierarchies, or an R object representing a partition or dendrogram or hierarchy.} \item{y}{an R object representing a partition or dendrogram or hierarchy. Ignored if \code{x} is an ensemble.} } \details{ For a given finite set of objects \eqn{X}, the set \eqn{H(X)} of all (hard) partitions of \eqn{X} can be partially ordered by defining a partition \eqn{P} to be \dQuote{finer} than a partition \eqn{Q}, i.e., \eqn{P \le Q}, if each class of \eqn{P} is contained in some class of \eqn{Q}. With this partial order, \eqn{H(X)} becomes a bounded \dfn{lattice}, with intersection and union of two elements given by their greatest lower bound (\dfn{meet}) and their least upper bound (\dfn{join}), respectively. Specifically, the meet of two partitions computed by \code{cl_meet} is the partition obtained by intersecting the classes of the partitions; the classes of the join computed by \code{cl_join} are obtained by joining all elements in the same class in at least one of the partitions. Obviously, the least and greatest elements of the partition lattice are the partitions where each object is in a single class (sometimes referred to as the \dQuote{splitter} partition) or in the same class (the \dQuote{lumper} partition), respectively. Meet and join of an arbitrary number of partitions can be defined recursively. In addition to computing the meet and join, the comparison operations corresponding to the above partial order as well as \code{min}, \code{max}, and \code{range} are available at least for R objects representing partitions inheriting from \code{"\link{cl_partition}"}. The summary methods give the meet and join of the given partitions (for \code{min} and \code{max}), or a partition ensemble with the meet and join (for \code{range}). If the partitions specified by \code{x} and \code{y} are soft partitions, the corresponding nearest hard partitions are used. Future versions may optionally provide suitable \dQuote{soft} (fuzzy) extensions for computing meets and joins. The set of all dendrograms on \eqn{X} can be ordered using pointwise inequality of the associated ultrametric dissimilarities: i.e., if \eqn{D} and \eqn{E} are the dendrograms with ultrametrics \eqn{u} and \eqn{v}, respectively, then \eqn{D \le E} if \eqn{u_{ij} \le v_{ij}} for all pairs \eqn{(i, j)} of objects. This again yields a lattice (of dendrograms). The join of \eqn{D} and \eqn{E} is the dendrogram with ultrametrics given by \eqn{\max(u_{ij}, v_{ij})} (as this gives an ultrametric); the meet is the dendrogram with the maximal ultrametric dominated by \eqn{\min(u_{ij}, v_{ij})}, and can be obtained by applying single linkage hierarchical clustering to the minima. The set of all hierarchies on \eqn{X} can be ordered by set-wise inclusion of the classes: i.e., if \eqn{H} and \eqn{G} are two hierarchies, then \eqn{H \le G} if all classes of \eqn{H} are also classes of \eqn{G}. This yields a meet semilattice, with meet given by the classes contained in both hierarchies. The join only exists if the union of the classes is a hierarchy. In each case, a modular semilattice is obtained, which allows for a natural metrization via least element (semi)lattice move distances, see \bibcitet{Barthélemy+Leclerc+Monjardet:1986}. These latticial metrics are given by the BA/C (partitions), Manhattan (dendrograms), and symdiff (hierarchies) dissimilarities, respectively (see \code{\link{cl_dissimilarity}}). } \value{ For \code{cl_meet} and \code{cl_join}, an object of class \code{"\link{cl_partition}"} or \code{"\link{cl_dendrogram}"} with the class ids or ultrametric dissimilarities of the meet and join of the partitions or dendrograms, respectively. } \references{ \bibshow{Barthélemy+Leclerc+Monjardet:1986} } \examples{ ## Two simple partitions of 7 objects. A <- as.cl_partition(c(1, 1, 2, 3, 3, 5, 5)) B <- as.cl_partition(c(1, 2, 2, 3, 4, 5, 5)) ## These disagree on objects 1-3, A splits objects 4 and 5 into ## separate classes. Objects 6 and 7 are always in the same class. (A <= B) || (B <= A) ## (Neither partition is finer than the other.) cl_meet(A, B) cl_join(A, B) ## Meeting with the lumper (greatest) or joining with the splitter ## (least) partition does not make a difference: C_lumper <- as.cl_partition(rep(1, n_of_objects(A))) cl_meet(cl_ensemble(A, B, C_lumper)) C_splitter <- as.cl_partition(seq_len(n_of_objects(A))) cl_join(cl_ensemble(A, B, C_splitter)) ## Another way of computing the join: range(A, B, C_splitter)$max } \keyword{cluster} clue/man/GVME.Rd0000644000175100001440000000167415144332746013041 0ustar hornikusers\name{GVME} \alias{GVME} \title{Gordon-Vichi Macroeconomic Partition Ensemble Data} \description{ Soft partitions of 21 countries based on macroeconomic data for the years 1975, 1980, 1985, 1990, and 1995. } \usage{data("GVME")} \format{ A named cluster ensemble of 5 soft partitions of 21 countries into 2 or 3 classes. The names are the years to which the partitions correspond. } \details{ The partitions were obtained using fuzzy \eqn{c}-means on measurements of the following variables: the annual per capita gross domestic product (GDP) in USD (converted to 1987 prices); the percentage of GDP provided by agriculture; the percentage of employees who worked in agriculture; and gross domestic investment, expressed as a percentage of the GDP. See \bibcitet{|Gordon+Vichi:2001|page 230}, for more details. } \source{ Table 1 in \bibcitet{Gordon+Vichi:2001}. } \references{ \bibshow{Gordon+Vichi:2001} } \keyword{datasets} clue/man/cl_pam.Rd0000644000175100001440000000462515144343361013530 0ustar hornikusers\name{cl_pam} \alias{cl_pam} \title{K-Medoids Partitions of Clusterings} \description{ Compute \eqn{k}-medoids partitions of clusterings. } \usage{ cl_pam(x, k, method = "euclidean", solver = c("pam", "kmedoids")) } \arguments{ \item{x}{an ensemble of partitions or hierarchies, or something coercible to that (see \code{\link{cl_ensemble}}).} \item{k}{an integer giving the number of classes to be used in the partition.} \item{method}{a character string or a function, as for argument \code{method} of function \code{\link{cl_dissimilarity}}.} \item{solver}{a character string indicating the \eqn{k}-medoids solver to be employed. May be abbreviated. If \code{"pam"} (default), the Partitioning Around Medoids \bibcitep{|Kaufman+Rousseeuw:1990|Chapter 2} heuristic \code{\link[cluster]{pam}} of package \pkg{cluster} is used. Otherwise, the exact algorithm of \code{\link{kmedoids}} is employed.} } \value{ An object of class \code{"cl_pam"} representing the obtained \dQuote{secondary} partition, which is a list with the following components. \item{cluster}{the class ids of the partition.} \item{medoid_ids}{the indices of the medoids.} \item{prototypes}{a cluster ensemble with the \eqn{k} prototypes (medoids).} \item{criterion}{the value of the criterion function of the partition.} \item{description}{a character string indicating the dissimilarity method employed.} } \details{ An optimal \eqn{k}-medoids partition of the given cluster ensemble is defined as a partition of the objects \eqn{x_i} (the elements of the ensemble) into \eqn{k} classes \eqn{C_1, \ldots, C_k} such that the criterion function \eqn{L = \sum_{l=1}^k \min_{j \in C_l} \sum_{i \in C_l} d(x_i, x_j)} is minimized. Such secondary partitions \bibcitep{e.g.|Gordon+Vichi:1998|} are obtained by computing the dissimilarities \eqn{d} of the objects in the ensemble for the given dissimilarity method, and applying a dissimilarity-based \eqn{k}-medoids solver to \eqn{d}. } \references{ \bibshow{Kaufman+Rousseeuw:1990, Gordon+Vichi:1998} } \seealso{ \code{\link{cl_pclust}} for more general prototype-based partitions of clusterings. } \examples{ data("Kinship82") party <- cl_pam(Kinship82, 3, "symdiff") ## Compare results with tables 5 and 6 in Gordon & Vichi (1998). party lapply(cl_prototypes(party), cl_classes) table(cl_class_ids(party)) } \keyword{cluster} clue/man/ls_fit_ultrametric.Rd0000644000175100001440000001670615144360364016175 0ustar hornikusers\name{ls_fit_ultrametric} \encoding{UTF-8} \alias{ls_fit_ultrametric} \title{Least Squares Fit of Ultrametrics to Dissimilarities} \description{ Find the ultrametric with minimal square distance (Euclidean dissimilarity) to given dissimilarity objects. } \usage{ ls_fit_ultrametric(x, method = c("SUMT", "IP", "IR"), weights = 1, control = list()) } \arguments{ \item{x}{a dissimilarity object inheriting from or coercible to class \code{"\link{dist}"}, or an ensemble of such objects.} \item{method}{a character string indicating the fitting method to be employed. Must be one of \code{"SUMT"} (default), \code{"IP"}, or \code{"IR"}, or a unique abbreviation thereof.} \item{weights}{a numeric vector or matrix with non-negative weights for obtaining a weighted least squares fit. If a matrix, its numbers of rows and columns must be the same as the number of objects in \code{x}, and the lower diagonal part is used. Otherwise, it is recycled to the number of elements in \code{x}.} \item{control}{a list of control parameters. See \bold{Details}.} } \value{ An object of class \code{"\link{cl_ultrametric}"} containing the fitted ultrametric distances. } \details{ For a single dissimilarity object \code{x}, the problem to be solved is minimizing \deqn{L(u) = \sum_{i,j} w_{ij} (x_{ij} - u_{ij})^2} over all \eqn{u} satisfying the ultrametric constraints (i.e., for all \eqn{i, j, k}, \eqn{u_{ij} \le \max(u_{ik}, u_{jk})}). This problem is known to be NP hard \bibcitep{Křivánek+Morávek:1986}. For an ensemble of dissimilarity objects, the criterion function is \deqn{L(u) = \sum_b w_b \sum_{i,j} w_{ij} (x_{ij}(b) - u_{ij})^2,} where \eqn{w_b} is the weight given to element \eqn{x_b} of the ensemble and can be specified via control parameter \code{weights} (default: all ones). This problem reduces to the above basic problem with \eqn{x} as the \eqn{w_b}-weighted mean of the \eqn{x_b}. We provide three heuristics for solving the basic problem. Method \code{"SUMT"} implements the \acronym{SUMT} \bibcitep{Sequential Unconstrained Minimization Technique|Fiacco+McCormick:1968|} approach of \bibcitet{De_Soete:1984a} which in turn simplifies the suggestions in \bibcitet{Carroll+Pruzansky:1980}. (See \code{\link{sumt}} for more information on the \acronym{SUMT} approach.) We then use a final single linkage hierarchical clustering step to ensure that the returned object exactly satisfies the ultrametric constraints. The starting value \eqn{u_0} is obtained by \dQuote{random shaking} of the given dissimilarity object (if not given). If there are missing values in \code{x}, i.e., the given dissimilarities are \emph{incomplete}, we follow a suggestion of \bibcitet{De_Soete:1984a}, imputing the missing values by the weighted mean of the non-missing ones, and setting the corresponding weights to zero. Available control parameters are \code{method}, \code{control}, \code{eps}, \code{q}, and \code{verbose}, which have the same roles as for \code{\link{sumt}}, and the following. \describe{ \item{\code{nruns}}{an integer giving the number of runs to be performed. Defaults to 1.} \item{\code{start}}{a single dissimilarity, or a list of dissimilarities to be employed as starting values.} } The default optimization using conjugate gradients should work reasonably well for medium to large size problems. For \dQuote{small} ones, using \code{nlm} is usually faster. Note that the number of ultrametric constraints is of the order \eqn{n^3}, where \eqn{n} is the number of objects in the dissimilarity object, suggesting to use the \acronym{SUMT} approach in favor of \code{\link[stats]{constrOptim}}. If starting values for the \acronym{SUMT} are provided via \code{start}, the number of starting values gives the number of runs to be performed, and control option \code{nruns} is ignored. Otherwise, \code{nruns} starting values are obtained by random shaking of the dissimilarity to be fitted. In the case of multiple \acronym{SUMT} runs, the (first) best solution found is returned. Method \code{"IP"} implements the Iterative Projection approach of \bibcitet{Hubert+Arabie:1995}. This iteratively projects the current dissimilarities to the closed convex set given by the ultrametric constraints (3-point conditions) for a single index triple \eqn{(i, j, k)}, in fact replacing the two largest values among \eqn{d_{ij}, d_{ik}, d_{jk}} by their mean. The following control parameters can be provided via the \code{control} argument. \describe{ \item{\code{nruns}}{an integer giving the number of runs to be performed. Defaults to 1.} \item{\code{order}}{a permutation of the numbers from 1 to the number of objects in \code{x}, specifying the order in which the ultrametric constraints are considered, or a list of such permutations.} \item{\code{maxiter}}{an integer giving the maximal number of iterations to be employed.} \item{\code{tol}}{a double indicating the maximal convergence tolerance. The algorithm stops if the total absolute change in the dissimilarities in an iteration is less than \code{tol}.} \item{\code{verbose}}{a logical indicating whether to provide some output on minimization progress. Defaults to \code{getOption("verbose")}.} } If permutations are provided via \code{order}, the number of these gives the number of runs to be performed, and control option \code{nruns} is ignored. Otherwise, \code{nruns} randomly generated orders are tried. In the case of multiple runs, the (first) best solution found is returned. Non-identical weights and incomplete dissimilarities are currently not supported. Method \code{"IR"} implements the Iterative Reduction approach suggested by \bibcitet{Roux:1988}, see also \bibcitet{Barthélemy+Guénoche:1991}. This is similar to the Iterative Projection method, but modifies the dissimilarities between objects proportionally to the aggregated change incurred from the ultrametric projections. Available control parameters are identical to those of method \code{"IP"}. Non-identical weights and incomplete dissimilarities are currently not supported. It should be noted that all methods are heuristics which can not be guaranteed to find the global minimum. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the base algorithm. } \references{ \bibinfo{De_Soete:1984a}{year}{1984a} \bibinfo{De_Soete:1984b}{year}{1984b} \bibshow{Barthélemy+Guénoche:1991, Carroll+Pruzansky:1980, Fiacco+McCormick:1968, Hubert+Arabie:1995, Křivánek+Morávek:1986, Roux:1988, De_Soete:1984a, De_Soete:1984b} } \seealso{ \code{\link{cl_consensus}} for computing least squares (Euclidean) consensus hierarchies by least squares fitting of average ultrametric distances; \code{\link{l1_fit_ultrametric}}. } \examples{ ## Least squares fit of an ultrametric to the Miller-Nicely consonant ## phoneme confusion data. data("Phonemes") ## Note that the Phonemes data set has the consonant misclassification ## probabilities, i.e., the similarities between the phonemes. d <- as.dist(1 - Phonemes) u <- ls_fit_ultrametric(d, control = list(verbose = TRUE)) ## Cophenetic correlation: cor(d, u) ## Plot: plot(u) ## ("Basically" the same as Figure 1 in de Soete (1984b).) } \keyword{cluster} \keyword{optimize} clue/man/ls_fit_addtree.Rd0000644000175100001440000000541515144356311015242 0ustar hornikusers\name{ls_fit_addtree} \encoding{UTF-8} \alias{ls_fit_addtree} \alias{ls_fit_centroid} \title{Least Squares Fit of Additive Tree Distances to Dissimilarities} \description{ Find the additive tree distance or centroid distance minimizing least squares distance (Euclidean dissimilarity) to a given dissimilarity object. } \usage{ ls_fit_addtree(x, method = c("SUMT", "IP", "IR"), weights = 1, control = list()) ls_fit_centroid(x) } \arguments{ \item{x}{a dissimilarity object inheriting from class \code{"\link{dist}"}.} \item{method}{a character string indicating the fitting method to be employed. Must be one of \code{"SUMT"} (default), \code{"IP"}, or \code{"IR"}, or a unique abbreviation thereof.} \item{weights}{a numeric vector or matrix with non-negative weights for obtaining a weighted least squares fit. If a matrix, its numbers of rows and columns must be the same as the number of objects in \code{x}, and the lower diagonal part is used. Otherwise, it is recycled to the number of elements in \code{x}.} \item{control}{a list of control parameters. See \bold{Details}.} } \value{ An object of class \code{"cl_addtree"} containing the optimal additive tree distances. } \details{ See \code{\link{as.cl_addtree}} for details on additive tree distances and centroid distances. With \eqn{L(d) = \sum w_{ij} (x_{ij} - d_{ij})^2}, the problem to be solved by \code{ls_fit_addtree} is minimizing \eqn{L} over all additive tree distances \eqn{d}. This problem is known to be NP hard. We provide three heuristics for solving this problem. Method \code{"SUMT"} implements the \acronym{SUMT} \bibcitep{Sequential Unconstrained Minimization Technique|Fiacco+McCormick:1968|} approach of \bibcitet{De_Soete:1983}. Incomplete dissimilarities are currently not supported. Methods \code{"IP"} and \code{"IR"} implement the Iterative Projection and Iterative Reduction approaches of \bibcitet{Hubert+Arabie:1995} and \bibcitet{Roux:1988}, respectively. Non-identical weights and incomplete dissimilarities are currently not supported. See \code{\link{ls_fit_ultrametric}} for details on these methods and available control parameters. It should be noted that all methods are heuristics which can not be guaranteed to find the global minimum. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the base algorithm. \code{ls_fit_centroid} finds the centroid distance \eqn{d} minimizing \eqn{L(d)} (currently, only for the case of identical weights). This optimization problem has a closed-form solution. } \references{ \bibshow{Fiacco+McCormick:1968, Hubert+Arabie:1995, Roux:1988, De_Soete:1983} } \keyword{cluster} \keyword{optimize} clue/man/hierarchy.Rd0000644000175100001440000000735412211412651014244 0ustar hornikusers\name{hierarchy} \alias{cl_hierarchy} % class ... \alias{is.cl_hierarchy} \alias{as.cl_hierarchy} \alias{cl_dendrogram} % class ... \alias{is.cl_dendrogram} \alias{as.cl_dendrogram} \alias{plot.cl_dendrogram} \title{Hierarchies} \description{ Determine whether an R object represents a hierarchy of objects, or coerce to an R object representing such.} \usage{ is.cl_hierarchy(x) is.cl_dendrogram(x) as.cl_hierarchy(x) as.cl_dendrogram(x) } \arguments{ \item{x}{an R object.} } \value{ For the testing functions, a logical indicating whether the given object represents a clustering of objects of the respective kind. For the coercion functions, a container object inheriting from \code{"cl_hierarchy"}, with a suitable representation of the hierarchy given by \code{x}. } \details{ These functions are generic functions. The methods provided in package \pkg{clue} handle the partitions and hierarchies obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{ape}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself). The hierarchies considered by \pkg{clue} are \emph{\eqn{n}-trees} (hierarchies in the strict sense) and \emph{dendrograms} (also known as valued \eqn{n}-trees or total indexed hierarchies), which are represented by the virtual classes \code{"cl_hierarchy"} and \code{"cl_dendrogram"} (which inherits from the former), respectively. \eqn{n}-trees on a set \eqn{X} of objects correspond to collections \eqn{H} of subsets of \eqn{X}, usually called \emph{classes} of the hierarchy, which satisfy the following properties: \itemize{ \item \eqn{H} contains all singletons with objects of \eqn{X}, \eqn{X} itself, but not the empty set; \item The intersection of two sets \eqn{A} and \eqn{B} in \eqn{H} is either empty or one of the sets. } The classes of a hierarchy can be obtained by \code{\link{cl_classes}}. Dendrograms are \eqn{n}-trees where additionally a height \eqn{h} is associated with each of the classes, so that for two classes \eqn{A} and \eqn{B} with non-empty intersection we have \eqn{h(A) \le h(B)} iff \eqn{A} is a subset of \eqn{B}. For each pair of objects one can then define \eqn{u_{ij}} as the height of the smallest class containing both \eqn{i} and \eqn{j}: this results in a dissimilarity on \eqn{X} which satisfies the ultrametric (3-point) conditions \eqn{u_{ij} \le \max(u_{ik}, u_{jk})} for all triples \eqn{(i, j, k)} of objects. Conversely, an ultrametric dissimilarity induces a unique dendrogram. The ultrametric dissimilarities of a dendrogram can be obtained by \code{\link{cl_ultrametric}}. \code{as.cl_hierarchy} returns an object of class \code{"cl_hierarchy"} \dQuote{containing} the given object \code{x} if this already represents a hierarchy (i.e., \code{is.cl_hierarchy(x)} is true), or the ultrametric obtained from \code{x} via \code{\link{as.cl_ultrametric}}. \code{as.cl_dendrogram} returns an object which has class \code{"cl_dendrogram"} and inherits from \code{"cl_hierarchy"}, and contains \code{x} if it represents a dendrogram (i.e., \code{is.cl_dendrogram(x)} is true), or the ultrametric obtained from \code{x}. Conceptually, hierarchies and dendrograms are \emph{virtual} classes, allowing for a variety of representations. There are group methods for comparing dendrograms and computing their minimum, maximum, and range based on the meet and join operations, see \code{\link{cl_meet}}. There is also a \code{plot} method. } \examples{ hcl <- hclust(dist(USArrests)) is.cl_dendrogram(hcl) is.cl_hierarchy(hcl) } \keyword{cluster} clue/man/cl_object_names.Rd0000644000175100001440000000176712211412557015404 0ustar hornikusers\name{cl_object_names} \alias{cl_object_names} \title{Find Object Names} \description{ Find the names of the objects from which a taxonomy (partition or hierarchy) or proximity was obtained. } \usage{ cl_object_names(x) } \arguments{ \item{x}{an \R object representing a taxonomy or proximity.} } \value{ A character vector of length \code{\link{n_of_objects}(x)} in case the names of the objects could be determined, or \code{NULL}. } \details{ This is a generic function. The methods provided in package \pkg{clue} handle the partitions and hierarchies obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{ape}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself), in as much as possible. There is also a method for object dissimilarities which inherit from class \code{"\link{dist}"}. } \keyword{cluster} clue/man/cl_dissimilarity.Rd0000644000175100001440000003235615144354472015650 0ustar hornikusers\name{cl_dissimilarity} \encoding{UTF-8} \alias{cl_dissimilarity} \title{Dissimilarity Between Partitions or Hierarchies} \description{Compute the dissimilarity between (ensembles) of partitions or hierarchies.} \usage{ cl_dissimilarity(x, y = NULL, method = "euclidean", \dots) } \arguments{ \item{x}{an ensemble of partitions or hierarchies and dissimilarities, or something coercible to that (see \code{\link{cl_ensemble}}).} \item{y}{\code{NULL} (default), or as for \code{x}.} \item{method}{a character string specifying one of the built-in methods for computing dissimilarity, or a function to be taken as a user-defined method. If a character string, its lower-cased version is matched against the lower-cased names of the available built-in methods using \code{\link{pmatch}}. See \bold{Details} for available built-in methods.} \item{\dots}{further arguments to be passed to methods.} } \value{ If \code{y} is \code{NULL}, an object of class \code{"cl_dissimilarity"} containing the dissimilarities between all pairs of components of \code{x}. Otherwise, an object of class \code{"cl_cross_dissimilarity"} with the dissimilarities between the components of \code{x} and the components of \code{y}. } \details{ If \code{y} is given, its components must be of the same kind as those of \code{x} (i.e., components must either all be partitions, or all be hierarchies or dissimilarities). If all components are partitions, the following built-in methods for measuring dissimilarity between two partitions with respective membership matrices \eqn{u} and \eqn{v} (brought to a common number of columns) are available: \describe{ \item{\code{"euclidean"}}{the Euclidean dissimilarity of the memberships, i.e., the square root of the minimal sum of the squared differences of \eqn{u} and all column permutations of \eqn{v}. See \bibcitet{Dimitriadou+Weingessel+Hornik:2002}.} \item{\code{"manhattan"}}{the Manhattan dissimilarity of the memberships, i.e., the minimal sum of the absolute differences of \eqn{u} and all column permutations of \eqn{v}.} \item{\code{"comemberships"}}{the Euclidean dissimilarity of the elements of the co-membership matrices \eqn{C(u) = u u'} and \eqn{C(v)}, i.e., the square root of the sum of the squared differences of \eqn{C(u)} and \eqn{C(v)}.} \item{\code{"symdiff"}}{the cardinality of the symmetric set difference of the sets of co-classified pairs of distinct objects in the partitions. I.e., the number of distinct pairs of objects in the same class in exactly one of the partitions. (Alternatively, the cardinality of the symmetric set difference between the (binary) equivalence relations corresponding to the partitions.) For soft partitions, (currently) the symmetric set difference of the corresponding nearest hard partitions is used.} \item{\code{"Rand"}}{the Rand distance, i.e., the rate of distinct pairs of objects in the same class in exactly one of the partitions. (Related to the Rand index \eqn{a} via the linear transformation \eqn{d = (1 - a) / 2}.) For soft partitions, (currently) the Rand distance of the corresponding nearest hard partitions is used.} \item{\code{"GV1"}}{the square root of the dissimilarity \eqn{\Delta_1}{Delta_1} used for the first model in \bibcitet{Gordon+Vichi:2001}, i.e., the square root of the minimal sum of the squared differences of the \emph{matched} non-zero columns of \eqn{u} and \eqn{v}.} \item{\code{"BA/\var{d}"}}{distance measures for hard partitions discussed in \bibcitet{Boorman+Arabie:1972}, with \var{d} one of \samp{A}, \samp{C}, \samp{D}, or \samp{E}. For soft partitions, the distances of the corresponding nearest hard partitions are used. \code{"BA/A"} is the minimum number of single element moves (move from one class to another or a new one) needed to transform one partition into the other. Introduced in \bibcitet{Rubin:1967}. \code{"BA/C"} is the minimum number of lattice moves for transforming one partition into the other, where partitions are said to be connected by a lattice move if one is \emph{just} finer than the other (i.e., there is no other partition between them) in the partition lattice (see \code{\link{cl_meet}}). Equivalently, with \eqn{z} the join of \code{x} and \code{y} and \eqn{S} giving the number of classes, this can be written as \eqn{S(x) + S(y) - 2 S(z)}. Attributed to David Pavy. \code{"BA/D"} is the \dQuote{pair-bonds} distance, which can be defined as \eqn{S(x) + S(y) - 2 S(z)}, with \eqn{z} the meet of \code{x} and \code{y} and \eqn{S} the \emph{supervaluation} (i.e., non-decreasing with respect to the partial order on the partition lattice) function \eqn{\sum_i (n_i (n_i - 1)) / (n (n - 1))}, where the \eqn{n_i} are the numbers of objects in the respective classes of the partition (such that \eqn{n_i (n_i - 1) / 2} are the numbers of pair bonds in the classes), and \eqn{n} the total number of objects. \code{"BA/E"} is the normalized information distance, defined as \eqn{1 - I / H}, where \eqn{I} is the average mutual information between the partitions, and \eqn{H} is the average entropy of the meet \eqn{z} of the partitions. Introduced in \bibcitet{Rajski:1961}. (Boorman and Arabie also discuss a distance measure (\eqn{B}) based on the minimum number of set moves needed to transform one partition into the other, which, differently from the \eqn{A} and \eqn{C} distance measures is hard to compute \bibcitep{Day:1981} and (currently) not provided.)} \item{\code{"VI"}}{Variation of Information, see \bibcitet{Meila:2003}. If \code{\dots} has an argument named \code{weights}, it is taken to specify case weights.} \item{\code{"Mallows"}}{the Mallows-type distance by \bibcitet{Zhou+Li+Zha:2005}, which is related to the Monge-Kantorovich mass transfer problem, and given as the \eqn{p}-th root of the minimal value of the transportation problem \eqn{\sum w_{jk} \sum_i |u_{ij} - v_{ik}| ^ p} with constraints \eqn{w_{jk} \ge 0}, \eqn{\sum_j w_{jk} = \alpha_j}, \eqn{\sum_k w_{jk} = \beta_k}, where \eqn{\sum_j \alpha_j = \sum_k \beta_k}. The parameters \eqn{p}, \eqn{\alpha} and \eqn{\beta} all default to one (in this case, the Mallows distance coincides with the Manhattan dissimilarity), and can be specified via additional arguments named \code{p}, \code{alpha}, and \code{beta}, respectively.} \item{\code{"CSSD"}}{the Cluster Similarity Sensitive Distance of \bibcitet{Zhou+Li+Zha:2005}, which is given as the minimal value of \eqn{\sum_{k,l} (1 - 2 w_{kl} / (\alpha_k + \beta_l)) L_{kl}}, where \eqn{L_{kl} = \sum_i u_{ik} v_{il} d(p_{x;k}, p_{y;l})} with \eqn{p_{x;k}} and \eqn{p_{y;l}} the prototype of the \eqn{k}-th class of \code{x} and the \eqn{l}-th class of \code{y}, respectively, \eqn{d} is the distance between these, and the \eqn{w_{kl}} as for Mallows distance. If prototypes are matrices, the Euclidean distance between these is used as default. Using the additional argument \code{L}, one can give a matrix of \eqn{L_{kl}} values, or the function \eqn{d}. Parameters \eqn{\alpha} and \eqn{\beta} all default to one, and can be specified via additional arguments named \code{alpha} and \code{beta}, respectively.} } For hard partitions, both Manhattan and squared Euclidean dissimilarity give twice the \emph{transfer distance} \bibcitep{Charon+Denoeud+Guénoche:2006}, which is the minimum number of objects that must be removed so that the implied partitions (restrictions to the remaining objects) are identical. This is also known as the \emph{\eqn{R}-metric} in \bibcitet{Day:1981}, i.e., the number of augmentations and removals of single objects needed to transform one partition into the other, and the \emph{partition-distance} in \bibcitet{Gusfield:2002}, and equals twice the number of single element moves distance of Boorman and Arabie. For hard partitions, the pair-bonds (Boorman-Arabie \eqn{D}) distance is identical to the Rand distance, and can also be written as the Manhattan distance between the co-membership matrices corresponding to the partitions, or equivalently, their symdiff distance, normalized by \eqn{n (n - 1)}. If all components are hierarchies, available built-in methods for measuring dissimilarity between two hierarchies with respective ultrametrics \eqn{u} and \eqn{v} are as follows. \describe{ \item{\code{"euclidean"}}{the Euclidean dissimilarity of the ultrametrics (i.e., the square root of the sum of the squared differences of \eqn{u} and \eqn{v}).} \item{\code{"manhattan"}}{the Manhattan dissimilarity of the ultrametrics (i.e., the sum of the absolute differences of \eqn{u} and \eqn{v}).} \item{\code{"cophenetic"}}{\eqn{1 - c^2}, where \eqn{c} is the cophenetic correlation coefficient (i.e., the product-moment correlation of the ultrametrics).} \item{\code{"gamma"}}{the rate of inversions between the ultrametrics (i.e., the rate of pairs \eqn{(i,j)} and \eqn{(k,l)} for which \eqn{u_{ij} < u_{kl}} and \eqn{v_{ij} > v_{kl}}).} \item{\code{"symdiff"}}{the cardinality of the symmetric set difference of the sets of classes (hierarchies in the strict sense) induced by the dendrograms. I.e., the number of sets of objects obtained by a split in exactly one of the hierarchies.} \item{\code{"Chebyshev"}}{the Chebyshev (maximal) dissimilarity of the ultrametrics (i.e., the maximum of the absolute differences of \eqn{u} and \eqn{v}).} \item{\code{"Lyapunov"}}{the logarithm of the product of the maximal and minimal ratios of the ultrametrics. This is also known as the \dQuote{Hilbert projective metric} on the cone represented by the ultrametrics \bibcitep{e.g.|Jardine+Sibson:1971|page 107}, and only defined for \emph{strict} ultrametrics (which are strictly positive for distinct objects).} \item{\code{"BO"}}{the \eqn{m_\delta} family of tree metrics by \bibcitet{Boorman+Olivier:1973}, which are of the form \eqn{m_\delta = \int_0^\infty \delta(p(h), q(h)) dh}, where \eqn{p(h)} and \eqn{q(h)} are the hard partitions obtaining by cutting the trees (dendrograms) at height \eqn{h}, and \eqn{\delta} is a suitably dissimilarity measure for partitions. In particular, when taking \eqn{\delta} as symdiff or Rand dissimilarity, \eqn{m_\delta} is the Manhattan dissimilarity of the hierarchies. If \code{\dots} has an argument named \code{delta} it is taken to specify the partition dissimilarity \eqn{\delta} to be employed.} \item{\code{"spectral"}}{the spectral norm (2-norm) of the differences of the ultrametrics, suggested in \bibcitet{Mérigot+Durbec+Gaertner:2010}.} } The measures based on ultrametrics also allow computing dissimilarity with \dQuote{raw} dissimilarities on the underlying objects (R objects inheriting from class \code{"dist"}). If a user-defined dissimilarity method is to be employed, it must be a function taking two clusterings as its arguments. Symmetric dissimilarity objects of class \code{"cl_dissimilarity"} are implemented as symmetric proximity objects with self-proximities identical to zero, and inherit from class \code{"cl_proximity"}. They can be coerced to dense square matrices using \code{as.matrix}. It is possible to use 2-index matrix-style subscripting for such objects; unless this uses identical row and column indices, this results in a (non-symmetric dissimilarity) object of class \code{"cl_cross_dissimilarity"}. Symmetric dissimilarity objects also inherit from class \code{"\link{dist}"} (although they currently do not \dQuote{strictly} extend this class), thus making it possible to use them directly for clustering algorithms based on dissimilarity matrices of this class, see the examples. } \references{ \bibshow{Boorman+Arabie:1972, Boorman+Olivier:1973, Charon+Denoeud+Guénoche:2006, Day:1981, Dimitriadou+Weingessel+Hornik:2002, Gordon+Vichi:2001, Gusfield:2002, Jardine+Sibson:1971, Meila:2003, Mérigot+Durbec+Gaertner:2010, Rajski:1961, Rubin:1967, Zhou+Li+Zha:2005} } \seealso{ \code{\link{cl_agreement}} } \examples{ ## An ensemble of partitions. data("CKME") pens <- CKME[1 : 30] diss <- cl_dissimilarity(pens) summary(c(diss)) cl_dissimilarity(pens[1:5], pens[6:7]) ## Equivalently, using subscripting. diss[1:5, 6:7] ## Can use the dissimilarities for "secondary" clustering ## (e.g. obtaining hierarchies of partitions): hc <- hclust(diss) plot(hc) ## Example from Boorman and Arabie (1972). P1 <- as.cl_partition(c(1, 2, 2, 2, 3, 3, 2, 2)) P2 <- as.cl_partition(c(1, 1, 2, 2, 3, 3, 4, 4)) cl_dissimilarity(P1, P2, "BA/A") cl_dissimilarity(P1, P2, "BA/C") ## Hierarchical clustering. d <- dist(USArrests) x <- hclust(d) cl_dissimilarity(x, d, "cophenetic") cl_dissimilarity(x, d, "gamma") } \keyword{cluster} clue/man/kmedoids.Rd0000644000175100001440000000361715144357574014107 0ustar hornikusers\name{kmedoids} \alias{kmedoids} \title{K-Medoids Clustering} \description{ Compute a \eqn{k}-medoids partition of a dissimilarity object. } \usage{ kmedoids(x, k) } \arguments{ \item{x}{a dissimilarity object inheriting from class \code{"\link{dist}"}, or a square matrix of pairwise object-to-object dissimilarity values.} \item{k}{an integer giving the number of classes to be used in the partition.} } \value{ An object of class \code{"kmedoids"} representing the obtained partition, which is a list with the following components. \item{cluster}{the class ids of the partition.} \item{medoid_ids}{the indices of the medoids.} \item{criterion}{the value of the criterion function of the partition.} } \details{ Let \eqn{d} denote the pairwise object-to-object dissimilarity matrix corresponding to \code{x}. A \eqn{k}-medoids partition of \code{x} is defined as a partition of the numbers from 1 to \eqn{n}, the number of objects in \code{x}, into \eqn{k} classes \eqn{C_1, \ldots, C_k} such that the criterion function \eqn{L = \sum_l \min_{j \in C_l} \sum_{i \in C_l} d_{ij}} is minimized. This is an NP-hard optimization problem. PAM (Partitioning Around Medoids, see \bibcitet{|Kaufman+Rousseeuw:1990|Chapter 2}) is a very popular heuristic for obtaining optimal \eqn{k}-medoids partitions, and provided by \code{\link[cluster]{pam}} in package \pkg{cluster}. \code{kmedoids} is an exact algorithm based on a binary linear programming formulation of the optimization problem \bibcitep{e.g.|Gordon+Vichi:1998|[P4']}, using \code{\link[lpSolve]{lp}} from package \pkg{lpSolve} as solver. Depending on available hardware resources (the number of constraints of the program is of the order \eqn{n^2}), it may not be possible to obtain a solution. } \references{ \bibshow{Kaufman+Rousseeuw:1990, Gordon+Vichi:1998} } \keyword{cluster} \keyword{optimize} clue/man/Kinship82.Rd0000644000175100001440000000247715144360562014061 0ustar hornikusers\name{Kinship82} \alias{Kinship82} \title{Rosenberg-Kim Kinship Terms Partition Data} \description{ Partitions of 15 kinship terms given by 85 female undergraduates at Rutgers University who were asked to sort the terms into classes \dQuote{on the basis of some aspect of meaning}. } \usage{data("Kinship82")} \format{ A cluster ensemble of 85 hard partitions of the 15 kinship terms. } \details{ \bibcitet{Rosenberg+Kim:1975} describe an experiment where perceived similarities of the kinship terms were obtained from six different \dQuote{sorting} experiments. These \dQuote{original} Rosenberg-Kim kinship terms data were published in \bibcitet{Arabie+Carroll+DeSarbo:1987}, and are also contained in file \file{indclus.data} in the shell archive \url{https://netlib.org/mds/indclus.shar}. For one of the experiments, partitions of the terms were printed in \bibcitet{Rosenberg:1982}. Comparison with the original data indicates that the partition data have the \dQuote{nephew} and \dQuote{niece} columns interchanged, which is corrected in the data set at hand. } \source{ Table 7.1 in \bibcitet{Rosenberg:1982}, with the \dQuote{nephew} and \dQuote{niece} columns interchanged. } \references{ \bibshow{Arabie+Carroll+DeSarbo:1987, Rosenberg+Kim:1975, Rosenberg:1982} } \keyword{datasets} clue/man/cl_boot.Rd0000644000175100001440000000455211304023137013703 0ustar hornikusers\name{cl_boot} \alias{cl_boot} \title{Bootstrap Resampling of Clustering Algorithms} \description{ Generate bootstrap replicates of the results of applying a \dQuote{base} clustering algorithm to a given data set. } \usage{ cl_boot(x, B, k = NULL, algorithm = if (is.null(k)) "hclust" else "kmeans", parameters = list(), resample = FALSE) } \arguments{ \item{x}{the data set of objects to be clustered, as appropriate for the base clustering algorithm.} \item{B}{an integer giving the number of bootstrap replicates.} \item{k}{\code{NULL} (default), or an integer giving the number of classes to be used for a partitioning base algorithm.} \item{algorithm}{a character string or function specifying the base clustering algorithm.} \item{parameters}{a named list of additional arguments to be passed to the base algorithm.} \item{resample}{a logical indicating whether the data should be resampled in addition to \dQuote{sampling from the algorithm}. If resampling is used, the class memberships of the objects given in \code{x} are predicted from the results of running the base algorithm on bootstrap samples of \code{x}.} } \value{ A cluster ensemble of length \eqn{B}, with either (if resampling is not used, default) the results of running the base algorithm on the given data set, or (if resampling is used) the memberships for the given data predicted from the results of running the base algorithm on bootstrap samples of the data. } \details{ This is a rather simple-minded function with limited applicability, and mostly useful for studying the effect of (uncontrolled) random initializations of fixed-point partitioning algorithms such as \code{\link[stats]{kmeans}} or \code{\link[e1071]{cmeans}}, see the examples. To study the effect of varying control parameters or explicitly providing random starting values, the respective cluster ensemble has to be generated explicitly (most conveniently by using \code{\link{replicate}} to create a list \code{lst} of suitable instances of clusterings obtained by the base algorithm, and using \code{cl_ensemble(list = lst)} to create the ensemble). } \examples{ ## Study e.g. the effect of random kmeans() initializations. data("Cassini") pens <- cl_boot(Cassini$x, 15, 3) diss <- cl_dissimilarity(pens) summary(c(diss)) plot(hclust(diss)) } \keyword{cluster} clue/man/n_of_classes.Rd0000644000175100001440000000347612211412701014721 0ustar hornikusers\name{n_of_classes} \alias{n_of_classes} \alias{cl_class_ids} \alias{as.cl_class_ids} \title{Classes in a Partition} \description{Determine the number of classes and the class ids in a partition of objects.} \usage{ n_of_classes(x) cl_class_ids(x) as.cl_class_ids(x) } \arguments{ \item{x}{an object representing a (hard or soft) partition (for \code{n_of_classes} and \code{cl_class_ids}), or raw class ids (for \code{as.cl_class_ids}).} } \value{ For \code{n_of_classes}, an integer giving the number of classes in the partition. For \code{cl_class_ids}, a vector of integers with the corresponding class ids. For soft partitions, the class ids returned are those of the \emph{nearest hard partition} obtained by taking the class ids of the (first) maximal membership values. } \details{ These function are generic functions. The methods provided in package \pkg{clue} handle the partitions obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself). Note that the number of classes is taken as the number of distinct class ids actually used in the partition, and may differ from the number of columns in a membership matrix representing the partition. \code{as.cl_class_ids} can be used for coercing \dQuote{raw} class ids (given as atomic vectors) to class id objects. } \seealso{ \code{\link{is.cl_partition}} } \examples{ data("Cassini") party <- kmeans(Cassini$x, 3) n_of_classes(party) ## A simple confusion matrix: table(cl_class_ids(party), Cassini$classes) ## For an "oversize" membership matrix representation: n_of_classes(cl_membership(party, 6)) } \keyword{cluster} clue/man/cl_predict.Rd0000644000175100001440000000411412211412617014367 0ustar hornikusers\name{cl_predict} \alias{cl_predict} \title{Predict Memberships} \description{ Predict class ids or memberships from R objects representing partitions. } \usage{ cl_predict(object, newdata = NULL, type = c("class_ids", "memberships"), ...) } \arguments{ \item{object}{an R object representing a partition of objects.} \item{newdata}{an optional data set giving the objects to make predictions for. This must be of the same \dQuote{kind} as the data set employed for obtaining the partition. If omitted, the original data are used.} \item{type}{a character string indicating whether class ids or memberships should be returned. May be abbreviated.} \item{\dots}{arguments to be passed to and from methods.} } \value{ Depending on \code{type}, an object of class \code{"cl_class_ids"} with the predicted class ids, or of class \code{"cl_membership"} with the matrix of predicted membership values. } \details{ Many algorithms resulting in partitions of a given set of objects can be taken to induce a partition of the underlying feature space for the measurements on the objects, so that class memberships for \dQuote{new} objects can be obtained from the induced partition. Examples include partitions based on assigning objects to their \dQuote{closest} prototypes, or providing mixture models for the distribution of objects in feature space. This is a generic function. The methods provided in package \pkg{clue} handle the partitions obtained from clustering functions in the base R distribution, as well as packages \pkg{RWeka}, \pkg{cba}, \pkg{cclust}, \pkg{cluster}, \pkg{e1071}, \pkg{flexclust}, \pkg{flexmix}, \pkg{kernlab}, \pkg{mclust}, \pkg{movMF} and \pkg{skmeans} (and of course, \pkg{clue} itself). } \examples{ ## Run kmeans on a random subset of the Cassini data, and predict the ## memberships for the "test" data set. data("Cassini") nr <- NROW(Cassini$x) ind <- sample(nr, 0.9 * nr, replace = FALSE) party <- kmeans(Cassini$x[ind, ], 3) table(cl_predict(party, Cassini$x[-ind, ]), Cassini$classes[-ind]) } \keyword{cluster} clue/man/cl_validity.Rd0000644000175100001440000000572115144355254014602 0ustar hornikusers\name{cl_validity} \alias{cl_validity} \alias{cl_validity.default} \title{Validity Measures for Partitions and Hierarchies} \description{ Compute validity measures for partitions and hierarchies, attempting to measure how well these clusterings capture the underlying structure in the data they were obtained from. } \usage{ cl_validity(x, ...) \method{cl_validity}{default}(x, d, ...) } \arguments{ \item{x}{an object representing a partition or hierarchy.} \item{d}{a dissimilarity object from which \code{x} was obtained.} \item{\dots}{arguments to be passed to or from methods.} } \value{ A list of class \code{"cl_validity"} with the computed validity measures. } \details{ \code{cl_validity} is a generic function. For partitions, its default method gives the \dQuote{dissimilarity accounted for}, defined as \eqn{1 - a_w / a_t}, where \eqn{a_t} is the average total dissimilarity, and the \dQuote{average within dissimilarity} \eqn{a_w} is given by \deqn{\frac{\sum_{i,j} \sum_k m_{ik}m_{jk} d_{ij}}{ \sum_{i,j} \sum_k m_{ik}m_{jk}}}{% \sum_{i,j} \sum_k m_{ik}m_{jk} d_{ij} / \sum_{i,j} \sum_k m_{ik}m_{jk}} where \eqn{d} and \eqn{m} are the dissimilarities and memberships, respectively, and the sums are over all pairs of objects and all classes. For hierarchies, the validity measures computed by default are \dQuote{variance accounted for} (VAF, e.g., \bibcitet{Hubert+Arabie+Meulman:2006}) and \dQuote{deviance accounted for} (DEV, e.g., \bibcitet{Smith:2001}). If \code{u} is the ultrametric corresponding to the hierarchy \code{x} and \code{d} the dissimilarity \code{x} was obtained from, these validity measures are given by \deqn{\mathrm{VAF} = \max\left(0, 1 - \frac{\sum_{i,j} (d_{ij} - u_{ij})^2}{ \sum_{i,j} (d_{ij} - \mathrm{mean}(d)) ^ 2}\right)}{ max(0, 1 - sum_{i,j} (d_{ij} - u_{ij})^2 / sum_{i,j} (d_{ij} - mean(d))^2)} and \deqn{\mathrm{DEV} = \max\left(0, 1 - \frac{\sum_{i,j} |d_{ij} - u_{ij}|}{ \sum_{i,j} |d_{ij} - \mathrm{median}(d)|}\right)}{ max(0, 1 - sum_{i,j} |d_{ij} - u_{ij}| / sum_{i,j} |d_{ij} - median(d)|)} respectively. Note that VAF and DEV are not invariant under rescaling \code{u}, and may be \dQuote{arbitrarily small} (i.e., 0 using the above definitions) even though \code{u} and \code{d} are \dQuote{structurally close} in some sense. For the results of using \code{\link[cluster]{agnes}} and \code{\link[cluster]{diana}}, the agglomerative and divisive coefficients are provided in addition to the default ones. } \references{ \bibshow{Hubert+Arabie+Meulman:2006, Smith:2001} } \seealso{ \code{\link[fpc]{cluster.stats}} in package \pkg{fpc} for a variety of cluster validation statistics; \code{\link[e1071]{fclustIndex}} in package \pkg{e1071} for several fuzzy cluster indexes; \code{\link[cclust]{clustIndex}} in package \pkg{cclust}; \code{\link[cluster]{silhouette}} in package \pkg{cluster}. } \keyword{cluster} clue/man/cl_agreement.Rd0000644000175100001440000002130515144360610014710 0ustar hornikusers\name{cl_agreement} \alias{cl_agreement} \title{Agreement Between Partitions or Hierarchies} \description{Compute the agreement between (ensembles) of partitions or hierarchies. } \usage{ cl_agreement(x, y = NULL, method = "euclidean", \dots) } \arguments{ \item{x}{an ensemble of partitions or hierarchies and dissimilarities, or something coercible to that (see \code{\link{cl_ensemble}}).} \item{y}{\code{NULL} (default), or as for \code{x}.} \item{method}{a character string specifying one of the built-in methods for computing agreement, or a function to be taken as a user-defined method. If a character string, its lower-cased version is matched against the lower-cased names of the available built-in methods using \code{\link{pmatch}}. See \bold{Details} for available built-in methods.} \item{\dots}{further arguments to be passed to methods.} } \value{ If \code{y} is \code{NULL}, an object of class \code{"cl_agreement"} containing the agreements between the all pairs of components of \code{x}. Otherwise, an object of class \code{"cl_cross_agreement"} with the agreements between the components of \code{x} and the components of \code{y}. } \details{ If \code{y} is given, its components must be of the same kind as those of \code{x} (i.e., components must either all be partitions, or all be hierarchies or dissimilarities). If all components are partitions, the following built-in methods for measuring agreement between two partitions with respective membership matrices \eqn{u} and \eqn{v} (brought to a common number of columns) are available: \describe{ \item{\code{"euclidean"}}{\eqn{1 - d / m}, where \eqn{d} is the Euclidean dissimilarity of the memberships, i.e., the square root of the minimal sum of the squared differences of \eqn{u} and all column permutations of \eqn{v}, and \eqn{m} is an upper bound for the maximal Euclidean dissimilarity. See \bibcitet{Dimitriadou+Weingessel+Hornik:2002}.} \item{\code{"manhattan"}}{\eqn{1 - d / m}, where \eqn{d} is the Manhattan dissimilarity of the memberships, i.e., the minimal sum of the absolute differences of \eqn{u} and all column permutations of \eqn{v}, and \eqn{m} is an upper bound for the maximal Manhattan dissimilarity.} \item{\code{"Rand"}}{the Rand index (the rate of distinct pairs of objects both in the same class or both in different classes in both partitions), see \bibcitet{Rand:1971} or \bibcitet{|R:Gordon:1999|page 198}. For soft partitions, (currently) the Rand index of the corresponding nearest hard partitions is used.} \item{\code{"cRand"}}{the Rand index corrected for agreement by chance, see \bibcitet{Hubert+Arabie:1985} or \bibcitet{|R:Gordon:1999|page 198}. Can only be used for hard partitions.} \item{\code{"NMI"}}{Normalized Mutual Information, see \bibcitet{Strehl+Ghosh:2002}. For soft partitions, (currently) the NMI of the corresponding nearest hard partitions is used.} \item{\code{"KP"}}{the Katz-Powell index, i.e., the product-moment correlation coefficient between the elements of the co-membership matrices \eqn{C(u) = u u'} and \eqn{C(v)}, respectively, see \bibcitet{Katz+Powell:1953}. For soft partitions, (currently) the Katz-Powell index of the corresponding nearest hard partitions is used. (Note that for hard partitions, the \eqn{(i,j)} entry of \eqn{C(u)} is one iff objects \eqn{i} and \eqn{j} are in the same class.)} \item{\code{"angle"}}{the maximal cosine of the angle between the elements of \eqn{u} and all column permutations of \eqn{v}.} \item{\code{"diag"}}{the maximal co-classification rate, i.e., the maximal rate of objects with the same class ids in both partitions after arbitrarily permuting the ids.} \item{\code{"FM"}}{the index of \bibcitet{Fowlkes+Mallows:1983}, i.e., the ratio \eqn{N_{xy} / \sqrt{N_x N_y}}{N_xy / sqrt(N_x N_y)} of the number \eqn{N_{xy}}{N_xy} of distinct pairs of objects in the same class in both partitions and the geometric mean of the numbers \eqn{N_x} and \eqn{N_y} of distinct pairs of objects in the same class in partition \eqn{x} and partition \eqn{y}, respectively. For soft partitions, (currently) the Fowlkes-Mallows index of the corresponding nearest hard partitions is used.} \item{\code{"Jaccard"}}{the Jaccard index, i.e., the ratio of the numbers of distinct pairs of objects in the same class in both partitions and in at least one partition, respectively. For soft partitions, (currently) the Jaccard index of the corresponding nearest hard partitions is used.} \item{\code{"purity"}}{the purity of the classes of \code{x} with respect to those of \code{y}, i.e., \eqn{\sum_j \max_i n_{ij} / n}, where \eqn{n_{ij}} is the joint frequency of objects in class \eqn{i} for \code{x} and in class \eqn{j} for \code{y}, and \eqn{n} is the total number of objects.} \item{\code{"PS"}}{Prediction Strength, see \bibcitet{Tibshirani+Walther:2005}: the minimum, over all classes \eqn{j} of \code{y}, of the maximal rate of objects in the same class for \code{x} and in class \eqn{j} for \code{y}.} } If all components are hierarchies, available built-in methods for measuring agreement between two hierarchies with respective ultrametrics \eqn{u} and \eqn{v} are as follows. \describe{ \item{\code{"euclidean"}}{\eqn{1 / (1 + d)}, where \eqn{d} is the Euclidean dissimilarity of the ultrametrics (i.e., the square root of the sum of the squared differences of \eqn{u} and \eqn{v}).} \item{\code{"manhattan"}}{\eqn{1 / (1 + d)}, where \eqn{d} is the Manhattan dissimilarity of the ultrametrics (i.e., the sum of the absolute differences of \eqn{u} and \eqn{v}).} \item{\code{"cophenetic"}}{The cophenetic correlation coefficient. (I.e., the product-moment correlation of the ultrametrics.)} \item{\code{"angle"}}{the cosine of the angle between the ultrametrics.} \item{\code{"gamma"}}{\eqn{1 - d}, where \eqn{d} is the rate of inversions between the associated ultrametrics (i.e., the rate of pairs \eqn{(i,j)} and \eqn{(k,l)} for which \eqn{u_{ij} < u_{kl}} and \eqn{v_{ij} > v_{kl}}). (This agreement measure is a linear transformation of Kruskal's \eqn{\gamma}{gamma}.)} } The measures based on ultrametrics also allow computing agreement with \dQuote{raw} dissimilarities on the underlying objects (R objects inheriting from class \code{"dist"}). If a user-defined agreement method is to be employed, it must be a function taking two clusterings as its arguments. Symmetric agreement objects of class \code{"cl_agreement"} are implemented as symmetric proximity objects with self-proximities identical to one, and inherit from class \code{"cl_proximity"}. They can be coerced to dense square matrices using \code{as.matrix}. It is possible to use 2-index matrix-style subscripting for such objects; unless this uses identical row and column indices, this results in a (non-symmetric agreement) object of class \code{"cl_cross_agreement"}. } \references{ \bibshow{Dimitriadou+Weingessel+Hornik:2002, Fowlkes+Mallows:1983, R:Gordon:1999, Hubert+Arabie:1985, Katz+Powell:1953, Rand:1971, Strehl+Ghosh:2002, Tibshirani+Walther:2005} } \seealso{ \code{\link{cl_dissimilarity}}; \code{\link[e1071]{classAgreement}} in package \pkg{e1071}. } \examples{ ## An ensemble of partitions. data("CKME") pens <- CKME[1 : 20] # for saving precious time ... summary(c(cl_agreement(pens))) summary(c(cl_agreement(pens, method = "Rand"))) summary(c(cl_agreement(pens, method = "diag"))) cl_agreement(pens[1:5], pens[6:7], method = "NMI") ## Equivalently, using subscripting. cl_agreement(pens, method = "NMI")[1:5, 6:7] ## An ensemble of hierarchies. d <- dist(USArrests) hclust_methods <- c("ward", "single", "complete", "average", "mcquitty") hclust_results <- lapply(hclust_methods, function(m) hclust(d, m)) names(hclust_results) <- hclust_methods hens <- cl_ensemble(list = hclust_results) summary(c(cl_agreement(hens))) ## Note that the Euclidean agreements are *very* small. ## This is because the ultrametrics differ substantially in height: u <- lapply(hens, cl_ultrametric) round(sapply(u, max), 3) ## Rescaling the ultrametrics to [0, 1] gives: u <- lapply(u, function(x) (x - min(x)) / (max(x) - min(x))) shens <- cl_ensemble(list = lapply(u, as.cl_dendrogram)) summary(c(cl_agreement(shens))) ## Au contraire ... summary(c(cl_agreement(hens, method = "cophenetic"))) cl_agreement(hens[1:3], hens[4:5], method = "gamma") } \keyword{cluster} clue/man/pclust.Rd0000644000175100001440000001537515144342657013622 0ustar hornikusers\name{pclust} \alias{pclust} \alias{pclust_family} \alias{pclust_object} \title{Prototype-Based Partitioning} \description{ Obtain prototype-based partitions of objects by minimizing the criterion \eqn{\sum w_b u_{bj}^m d(x_b, p_j)^e}, the sum of the case-weighted and membership-weighted \eqn{e}-th powers of the dissimilarities between the objects \eqn{x_b} and the prototypes \eqn{p_j}, for suitable dissimilarities \eqn{d} and exponents \eqn{e}. } \usage{ pclust(x, k, family, m = 1, weights = 1, control = list()) pclust_family(D, C, init = NULL, description = NULL, e = 1, .modify = NULL, .subset = NULL) pclust_object(prototypes, membership, cluster, family, m = 1, value, ..., classes = NULL, attributes = NULL) } \arguments{ \item{x}{the object to be partitioned.} \item{k}{an integer giving the number of classes to be used in the partition.} \item{family}{an object of class \code{"pclust_family"} as generated by \code{pclust_family}, containing the information about \eqn{d} and \eqn{e}.} \item{m}{a number not less than 1 controlling the softness of the partition (as the \dQuote{fuzzification parameter} of the fuzzy \eqn{c}-means algorithm). The default value of 1 corresponds to hard partitions obtained from a generalized \eqn{k}-means problem; values greater than one give partitions of increasing softness obtained from a generalized fuzzy \eqn{c}-means problem.} \item{weights}{a numeric vector of non-negative case weights. Recycled to the number of elements given by \code{x} if necessary.} \item{control}{a list of control parameters. See \bold{Details}.} \item{D}{a function for computing dissimilarities \eqn{d} between objects and prototypes.} \item{C}{a \sQuote{consensus} function with formals \code{x}, \code{weights} and \code{control} for computing a consensus prototype \eqn{p} minimizing \eqn{\sum_b w_b d(x_b, p) ^ e}.} \item{init}{a function with formals \code{x} and \code{k} initializing an object with \eqn{k} prototypes from the object \code{x} to be partitioned.} \item{description}{a character string describing the family.} \item{e}{a number giving the exponent \eqn{e} of the criterion.} \item{.modify}{a function with formals \code{x}, \code{i} and \code{value} for modifying a single prototype, or \code{NULL} (default).} \item{.subset}{a function with formals \code{x} and \code{i} for subsetting prototypes, or \code{NULL} (default).} \item{prototypes}{an object representing the prototypes of the partition.} \item{membership}{an object of class \code{"\link{cl_membership}"} with the membership values \eqn{u_{bj}}.} \item{cluster}{the class ids of the nearest hard partition.} \item{value}{the value of the criterion to be minimized.} \item{...}{further elements to be included in the generated pclust object.} \item{classes}{a character vector giving further classes to be given to the generated pclust object in addition to \code{"pclust"}, or \code{NULL} (default).} \item{attributes}{a list of attributes, or \code{NULL} (default).} } \value{ \code{pclust} returns the partition found as an object of class \code{"pclust"} (as obtained by calling \code{pclust_object}) which in addition to the \emph{default} components contains \code{call} (the matched call) and a \code{converged} attribute indicating convergence status (i.e., whether the maximal number of iterations was reached). \code{pclust_family} returns an object of class \code{"pclust_family"}, which is a list with components corresponding to the formals of \code{pclust_family}. \code{pclust_object} returns an object inheriting from class \code{"pclust"}, which is a list with components corresponding to the formals (up to and including \code{...}) of \code{pclust_object}, and additional classes and attributes specified by \code{classes} and \code{attributes}, respectively. } \details{ For \eqn{m = 1}, a generalization of the Lloyd-Forgy variant of the \eqn{k}-means algorithm is used, which iterates between reclassifying objects to their closest prototypes (according to the dissimilarities given by \code{D}), and computing new prototypes as the consensus for the classes (using \code{C}). For \eqn{m > 1}, a generalization of the fuzzy \eqn{c}-means recipe (e.g., \bibcitet{Bezdek:1981}) is used, which alternates between computing optimal memberships for fixed prototypes, and computing new prototypes as the suitably weighted consensus clusterings for the classes. This procedure is repeated until convergence occurs, or the maximal number of iterations is reached. Currently, no local improvement heuristics are provided. It is possible to perform several runs of the procedure via control arguments \code{nruns} or \code{start} (the default is to perform a single run), in which case the first partition with the smallest value of the criterion is returned. The dissimilarity and consensus functions as well as the exponent \eqn{e} are specified via \code{family}. In principle, arbitrary representations of objects to be partitioned and prototypes (which do not necessarily have to be \dQuote{of the same kind}) can be employed. In addition to \code{D} and \code{C}, what is needed are means to obtain an initial collection of \eqn{k} prototypes (\code{init}), to modify a single prototype (\code{.modify}), and subset the prototypes (\code{.subset}). By default, list and (currently, only dense) matrix (with the usual convention that the rows correspond to the objects) are supported. Otherwise, the family has to provide the functions needed. Available control parameters are as follows. \describe{ \item{\code{maxiter}}{an integer giving the maximal number of iterations to be performed. Defaults to 100.} \item{\code{nruns}}{an integer giving the number of runs to be performed. Defaults to 1.} \item{\code{reltol}}{the relative convergence tolerance. Defaults to \code{sqrt(.Machine$double.eps)}.} \item{\code{start}}{a list of prototype objects to be used as starting values.} \item{\code{verbose}}{a logical indicating whether to provide some output on minimization progress. Defaults to \code{getOption("verbose")}.} \item{\code{control}}{control parameters to be used in the consensus function.} } The fixed point approach employed is a heuristic which cannot be guaranteed to find the global minimum, in particular if \code{C} is not an exact minimizer. Standard practice would recommend to use the best solution found in \dQuote{sufficiently many} replications of the base algorithm. } \references{ \bibshow{Bezdek:1981} } \seealso{ \code{\link[stats]{kmeans}}, \code{\link[e1071]{cmeans}}. } clue/man/Phonemes.Rd0000644000175100001440000000167615144337361014061 0ustar hornikusers\name{Phonemes} \alias{Phonemes} \title{Miller-Nicely Consonant Phoneme Confusion Data} \description{ Miller-Nicely data on the auditory confusion of 16 consonant phonemes. } \usage{data("Phonemes")} \format{ A symmetric matrix of the misclassification probabilities of 16 English consonant phonemes. } \details{ \bibcitet{Miller+Nicely:1955} obtained the confusions by exposing female subjects to a series of syllables consisting of one of the 16 consonants followed by the vowel \samp{a} under 17 different experimental conditions. The data provided are obtained from aggregating the six so-called flat-noise conditions in which only the speech-to-noise ratio was varied into a single matrix of misclassification frequencies. } \source{ The data set is also contained in file \file{mapclus.data} in the shell archive \url{https://netlib.org/mds/mapclus.shar}. } \references{ \bibshow{Miller+Nicely:1955} } \keyword{datasets} clue/man/addtree.Rd0000644000175100001440000000347315144337676013721 0ustar hornikusers\name{addtree} \encoding{UTF-8} \alias{as.cl_addtree} \title{Additive Tree Distances} \description{ Objects representing additive tree distances. } \usage{ as.cl_addtree(x) } \arguments{ \item{x}{an R object representing additive tree distances.} } \value{ An object of class \code{"cl_addtree"} containing the additive tree distances. } \details{ Additive tree distances are object dissimilarities \eqn{d} satisfying the so-called \emph{additive tree conditions}, also known as \emph{four-point conditions} \eqn{d_{ij} + d_{kl} \le \max(d_{ik} + d_{jl}, d_{il} + d_{jk})} for all quadruples \eqn{i, j, k, l}. Equivalently, for each such quadruple, the largest two values of the sums \eqn{d_{ij} + d_{kl}}, \eqn{d_{ik} + d_{jl}}, and \eqn{d_{il} + d_{jk}} must be equal. Centroid distances are additive tree distances where the inequalities in the four-point conditions are strengthened to equalities (such that all three sums are equal), and can be represented as \eqn{d_{ij} = g_i + g_j}, i.e., as sums of distances from a \dQuote{centroid}. See, e.g., \bibcitet{Barthélemy+Guénoche:1991} for more details on additive tree distances. \code{as.cl_addtree} is a generic function. Its default method can handle objects representing ultrametric distances and raw additive distance matrices. In addition, there is a method for coercing objects of class \code{"\link[ape:as.phylo]{phylo}"} from package \pkg{ape}. Functions \code{\link{ls_fit_addtree}} and \code{\link{ls_fit_centroid}} can be used to find the additive tree distance or centroid distance minimizing least squares distance (Euclidean dissimilarity) to a given dissimilarity object. There is a \code{\link{plot}} method for additive tree distances. } \references{ \bibshow{Barthélemy+Guénoche:1991} } \keyword{cluster} clue/man/Cassini.Rd0000644000175100001440000000237411304023137013653 0ustar hornikusers\name{Cassini} \alias{Cassini} \title{Cassini Data} \description{ A Cassini data set with 1000 points in 2-dimensional space which are drawn from the uniform distribution on 3 structures. The two outer structures are banana-shaped; the \dQuote{middle} structure in between them is a circle. } \usage{data("Cassini")} \format{ A classed list with components \describe{ \item{\code{x}}{a matrix with 1000 rows and 2 columns giving the coordinates of the points.} \item{\code{classes}}{a factor indicating which structure the respective points belong to.} } } \details{ Instances of Cassini data sets can be created using function \code{\link[mlbench]{mlbench.cassini}} in package \pkg{mlbench}. The data set at hand was obtained using \preformatted{ library("mlbench") set.seed(1234) Cassini <- mlbench.cassini(1000) } } \examples{ data("Cassini") op <- par(mfcol = c(1, 2)) ## Plot the data set: plot(Cassini$x, col = as.integer(Cassini$classes), xlab = "", ylab = "") ## Create a "random" k-means partition of the data: set.seed(1234) party <- kmeans(Cassini$x, 3) ## And plot that. plot(Cassini$x, col = cl_class_ids(party), xlab = "", ylab = "") ## (We can see the problem ...) par(op) } \keyword{datasets} clue/man/ls_fit_sum_of_ultrametrics.Rd0000644000175100001440000000561515144356552017731 0ustar hornikusers\name{ls_fit_sum_of_ultrametrics} \alias{ls_fit_sum_of_ultrametrics} \title{Least Squares Fit of Sums of Ultrametrics to Dissimilarities} \description{ Find a sequence of ultrametrics with sum minimizing square distance (Euclidean dissimilarity) to a given dissimilarity object. } \usage{ ls_fit_sum_of_ultrametrics(x, nterms = 1, weights = 1, control = list()) } \arguments{ \item{x}{a dissimilarity object inheriting from or coercible to class \code{"\link{dist}"}.} \item{nterms}{an integer giving the number of ultrametrics to be fitted.} \item{weights}{a numeric vector or matrix with non-negative weights for obtaining a weighted least squares fit. If a matrix, its numbers of rows and columns must be the same as the number of objects in \code{x}, and the lower diagonal part is used. Otherwise, it is recycled to the number of elements in \code{x}.} \item{control}{a list of control parameters. See \bold{Details}.} } \details{ The problem to be solved is minimizing the criterion function \deqn{L(u(1), \dots, u(n)) = \sum_{i,j} w_{ij} \left(x_{ij} - \sum_{k=1}^n u_{ij}(k)\right)^2}{ L(u(1), \dots, u(n)) = \sum_{i,j} w_{ij} \left(x_{ij} - \sum_{k=1}^n u_{ij}(k)\right)^2} over all \eqn{u(1), \ldots, u(n)} satisfying the ultrametric constraints. We provide an implementation of the iterative heuristic suggested in \bibcitet{Carroll+Pruzansky:1980} which in each step \eqn{t} sequentially refits the \eqn{u(k)} as the least squares ultrametric fit to the \dQuote{residuals} \eqn{x - \sum_{l \ne k} u(l)} using \code{\link{ls_fit_ultrametric}}. Available control parameters include \describe{ \item{\code{maxiter}}{an integer giving the maximal number of iteration steps to be performed. Defaults to 100.} \item{\code{eps}}{a nonnegative number controlling the iteration, which stops when the maximal change in all \eqn{u(k)} is less than \code{eps}. Defaults to \eqn{10^{-6}}.} \item{\code{reltol}}{the relative convergence tolerance. Iteration stops when the relative change in the criterion function is less than \code{reltol}. Defaults to \eqn{10^{-6}}.} \item{\code{method}}{a character string indicating the fitting method to be employed by the individual least squares fits.} \item{\code{control}}{a list of control parameters to be used by the method of \code{\link{ls_fit_ultrametric}} employed. By default, if the \acronym{SUMT} method method is used, 10 inner \acronym{SUMT} runs are performed for each refitting.} } It should be noted that the method used is a heuristic which can not be guaranteed to find the global minimum. } \value{ A list of objects of class \code{"\link{cl_ultrametric}"} containing the fitted ultrametric distances. } \references{ \bibshow{Carroll+Pruzansky:1980} } \keyword{cluster} \keyword{optimize} clue/man/GVME_Consensus.Rd0000644000175100001440000000271615144357461015100 0ustar hornikusers\name{GVME_Consensus} \alias{GVME_Consensus} \title{Gordon-Vichi Macroeconomic Consensus Partition Data} \description{ The soft (\dQuote{fuzzy}) consensus partitions for the macroeconomic partition data given in \bibcitet{Gordon+Vichi:2001}. } \usage{data("GVME_Consensus")} \format{ A named cluster ensemble of eight soft partitions of 21 countries terms into two or three classes. } \details{ The elements of the ensemble are consensus partitions for the macroeconomic partition data in \bibcitet{Gordon+Vichi:2001}, which are available as data set \code{\link{GVME}}. Element names are of the form \code{"\var{m}/\var{k}"}, where \var{m} indicates the consensus method employed (one of \samp{MF1}, \samp{MF2}, \samp{JMF}, and \samp{S&S}, corresponding to the application of models 1, 2, and 3 in \bibcitet{Gordon+Vichi:2001} and the approach in \bibcitet{Sato+Sato:1994}, respectively), and \var{k} denotes the number classes (2 or 3). } \source{ Tables 4 and 5 in \bibcitet{Gordon+Vichi:2001}. } \references{ \bibshow{Gordon+Vichi:2001, Sato+Sato:1994} } \examples{ ## Load the consensus partitions. data("GVME_Consensus") ## Pick the partitions into 2 classes. GVME_Consensus_2 <- GVME_Consensus[1 : 4] ## Fuzziness using the Partition Coefficient. cl_fuzziness(GVME_Consensus_2) ## (Corresponds to 1 - F in the source.) ## Dissimilarities: cl_dissimilarity(GVME_Consensus_2) cl_dissimilarity(GVME_Consensus_2, method = "comem") } \keyword{datasets} clue/DESCRIPTION0000644000175100001440000000152215145307115012730 0ustar hornikusersPackage: clue Version: 0.3-67 Encoding: UTF-8 Title: Cluster Ensembles Description: CLUster Ensembles. Authors@R: c(person("Kurt", "Hornik", role = c("aut", "cre"), email = "Kurt.Hornik@R-project.org", comment = c(ORCID = "0000-0003-4198-9911")), person("Walter", "Böhm", role = "ctb")) License: GPL-2 Depends: R (>= 3.2.0) Imports: stats, cluster, graphics, methods Suggests: e1071, lpSolve (>= 5.5.7), quadprog (>= 1.4-8), relations Enhances: RWeka, ape, cba, cclust, flexclust, flexmix, kernlab, mclust, movMF, modeltools NeedsCompilation: yes Packaged: 2026-02-18 09:25:28 UTC; hornik Author: Kurt Hornik [aut, cre] (ORCID: ), Walter Böhm [ctb] Maintainer: Kurt Hornik Repository: CRAN Date/Publication: 2026-02-18 10:02:53 UTC