GPArotation/0000755000176200001440000000000014557762672012466 5ustar liggesusersGPArotation/NAMESPACE0000644000176200001440000000144514557551526013703 0ustar liggesusersimportFrom("stats", "rnorm") export( "GPFoblq", "GPForth") export( "GPFRSoblq", "GPFRSorth") export("Random.Start") export("oblimin") export("quartimin") export("targetT") export("targetQ") export("pstT") export("pstQ") export("oblimax") export("entropy") export("quartimax") export("Varimax") export("simplimax") export("bentlerT") export("bentlerQ") export("tandemI") export("tandemII") export("geominT") export("geominQ") export("bigeominT") export("bigeominQ") export("cfT") export("cfQ") export("infomaxT") export("infomaxQ") export("mccammon") export("bifactorT") export("bifactorQ") export("equamax") export("parsimax") export("varimin") export( "eiv", "echelon") S3method("print", "GPArotation") S3method("summary", "GPArotation") S3method("print", "summary.GPArotation") GPArotation/data/0000755000176200001440000000000014323662111013351 5ustar liggesusersGPArotation/data/Thurstone.rda0000644000176200001440000000142314323662111016034 0ustar liggesusersmSiHTQ~$6HI{VDI|FiCD",lZ  3QX:3:F6 Eta}w+f5ML^[<mff8=pfq'6samZ\ [7v.7ъlKГB_lu%F@oC^W|y}yJpkje:tέqc7ݫ{Lb890mzlpzJ15#ߛ 2OMlÉ` 6Q^YڃN-*R[J̧ϨGME}CG2`5xPc@C|p` 3^)Z1䜔a7xPi9Q9ՏPy]!߁-!>E.YC& ֿKuf~Nzn7\ 4;@?4-@yd{$N%n 6v貅=p ttDJ}iJL[0_JHEJ%̫R:1Us=&Tzԧ0`ԡyf7Ua^V+h7Jv-[׊W|L=\҅ ~{`CEsJf.((J8JsSla::~>0fHBd 1Ab K-LN; C3GPArotation/man/0000755000176200001440000000000014557733461013233 5ustar liggesusersGPArotation/man/Thurstone.Rd0000644000176200001440000000110014323662111015465 0ustar liggesusers\name{Thurstone} \docType{data} \alias{Thurstone} \alias{box20} \alias{box26} \title{Example Data from Thurstone} \description{ box20 and box26 are initial factor loading matrices. } \usage{ data(Thurstone) } \details{ The objects box20 and box26 are loaded from the data file Thurstone. } \format{ The objects box20 and box26 are matrices. } \source{ Thurstone, L.L. (1947). \emph{Multiple Factor Analysis}. Chicago: University of Chicago Press. } \seealso{ \code{\link{GPForth}}, \code{\link{Harman}}, \code{\link{WansbeekMeijer}} } \keyword{datasets} GPArotation/man/eiv.Rd0000644000176200001440000001013614323662111014266 0ustar liggesusers\encoding{latin1} \name{eiv} \alias{eiv} \title{Errors-in-Variables Rotation} \usage{ eiv(L, identity=seq(NCOL(L)), ...) } \arguments{ \item{L}{a factor loading matrix} \item{identity}{indicates rows which should be identity matrix.} \item{...}{additional arguments discarded.} } \value{A list (which includes elements used by \code{factanal}) with: \item{loadings}{The new loadings matrix.} \item{Th}{The rotation.} \item{method}{A string indicating the rotation objective function ("eiv").} \item{orthogonal}{For consistency with other rotation results. Always FALSE.} \item{convergence}{For consistency with other rotation results. Always TRUE.} \item{Phi}{The covariance matrix of the rotated factors.} } \description{ Rotate to errors-in-variables representation. } \details{ This function rotates to an errors-in-variables representation. The optimization is not iterative and does not use the GPA algorithm. The function can be used directly or the function name can be passed to factor analysis functions like \code{factanal}. The loadings matrix is rotated so the \eqn{k}{k} rows indicated by \code{identity} form an identity matrix, and the remaining \eqn{M-k}{M-k} rows are free parameters. \eqn{\Phi}{Phi} is also free. The default makes the first \eqn{k}{k} rows the identity. If inverting the matrix of the rows indicated by \code{identity} fails, the rotation will fail and the user needs to supply a different choice of rows. Not all authors consider this representation to be a rotation. Viewed as a rotation method, it is oblique, with an explicit solution: given an initial loadings matrix \eqn{L}{L} partitioned as \eqn{L = (L_1^T, L_2^T)^T}{L = rbind(L1, L2)}, then (for the default \code{identity}) the new loadings matrix is \eqn{(I, (L_2 L_1^{-1})^T)^T}{rbind(I, L2 \%*\% solve(L1))} and \eqn{\Phi = L_1 L_1^T}{Phi = L1 \%*\% t(L1)}, where \eqn{I}{I} is the \eqn{k}{k} by \eqn{k}{k} identity matrix. It is assumed that \eqn{\Phi = I}{Phi = I} for the initial loadings matrix. One use of this parameterization is for obtaining good starting values (so it looks a little strange to rotate towards this solution afterwards). It has a few other purposes: (1) It can be useful for comparison with published results in this parameterization; (2) The S.E.s are more straightfoward to compute, because it is the solution to an unconstrained optimization (though not necessarily computed as such); (3) One may have an idea about which reference variables load on only one factor, but not impose restrictive constraints on the other loadings, so, in a nonrestrictive way, it has similarities to CFA; (4) For some purposes, only the subspace spanned by the factors is important, not the specific parameterization within this subspace; (5) The back-predicted indicators (explained portion of the indicators) do not depend on the rotation method. Combined with the greater ease to obtain correct standard errors of this method, this allows easier and more accurate prediction-standard errors. } \examples{ data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, rotation="none") fa.eiv <- eiv(fa.unrotated$loadings) fa.eiv2 <- factanal(factors = 2, covmat=NetherlandsTV, rotation="eiv") cbind(loadings(fa.unrotated), loadings(fa.eiv), loadings(fa.eiv2)) fa.eiv3 <- eiv(fa.unrotated$loadings, identity=6:7) cbind(loadings(fa.unrotated), loadings(fa.eiv), loadings(fa.eiv3)) } \seealso{ \code{\link{echelon}}, \code{\link{rotations}}, \code{\link{GPForth}}, \code{\link{GPFoblq}} } \references{ \enc{Gsta}{Gosta} \enc{Hgglund}{Hagglund}. (1982). "Factor Analysis by Instrumental Variables Methods." \emph{Psychometrika}, 47, 209--222. Sock-Cheng Lewin-Koh and Yasuo Amemiya. (2003). "Heteroscedastic factor analysis." \emph{Biometrika}, 90, 85--97. Tom Wansbeek and Erik Meijer (2000) \emph{Measurement Error and Latent Variables in Econometrics}, Amsterdam: North-Holland. } \author{Erik Meijer and Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/man/vgQ.Rd0000644000176200001440000001102514557726307014257 0ustar liggesusers\name{vgQ} \alias{vgQ} \alias{vgQ.oblimin} \alias{vgQ.quartimin} \alias{vgQ.target} \alias{vgQ.pst} \alias{vgQ.oblimax} \alias{vgQ.entropy} \alias{vgQ.quartimax} \alias{vgQ.varimax} \alias{vgQ.simplimax} \alias{vgQ.bentler} \alias{vgQ.tandemI} \alias{vgQ.tandemII} \alias{vgQ.geomin} \alias{vgQ.bigeomin} \alias{vgQ.cf} \alias{vgQ.infomax} \alias{vgQ.mccammon} \alias{vgQ.bifactor} \alias{vgQ.varimin} \title{Rotations} \usage{ vgQ.oblimin(L, gam=0) vgQ.quartimin(L) vgQ.target(L, Target=NULL) vgQ.pst(L, W=NULL, Target=NULL) vgQ.oblimax(L) vgQ.entropy(L) vgQ.quartimax(L) vgQ.varimax(L) vgQ.simplimax(L, k=nrow(L)) vgQ.bentler(L) vgQ.tandemI(L) vgQ.tandemII(L) vgQ.geomin(L, delta=.01) vgQ.bigeomin(L, delta=.01) vgQ.cf(L, kappa=0) vgQ.infomax(L) vgQ.mccammon(L) vgQ.varimin(L) vgQ.bifactor(L) } \arguments{ \item{L}{a factor loading matrix} \item{gam}{0=Quartimin, .5=Biquartimin, 1=Covarimin.} \item{Target}{rotation target for objective calculation.} \item{W}{weighting of each element in target.} \item{k}{number of close to zero loadings.} \item{delta}{constant added to Lambda^2 in objective calculation.} \item{kappa}{see details.} } \value{A list (which includes elements used by \code{GPForth} and \code{GPFoblq}) with: \item{f}{The value of the criterion at L.} \item{Gq}{The gradient at L.} \item{Method}{A string indicating the criterion.} } \description{ vgQ routines to compute value and gradient of the criterion (not exported from NAMESPACE) } \details{ The \code{vgQ.*} versions of the code are called by the optimization routine and would typically not be used directly, so these methods are not exported from the package NAMESPACE. (They simply return the function value and gradient for a given rotation matrix.) You can print these functions, but the package name needs to be specified since they are not exported. For example, use \code{GPArotation:::vgQ.oblimin} to view the function \code{vgQ.oblimin}. The T or Q ending on function names should be omitted for the \code{vgQ.*} versions of the code so, for example, use \code{GPArotation:::vgQ.target} to view the target criterion calculation. \tabular{lll}{ \code{vgQ.oblimin} \tab orthogonal or oblique \tab oblimin family\cr \code{vgQ.quartimin} \tab oblique \tab \cr \code{vgQ.target} \tab orthogonal or oblique \tab target rotation \cr \code{vgQ.pst} \tab orthogonal or oblique \tab partially specified target rotation \cr \code{vgQ.oblimax} \tab oblique \tab \cr \code{vgQ.entropy} \tab orthogonal \tab minimum entropy \cr \code{vgQ.quartimax} \tab orthogonal \tab \cr \code{vgQ.varimax} \tab orthogonal \tab \cr \code{vgQ.simplimax} \tab oblique \tab \cr \code{vgQ.bentler} \tab orthogonal or oblique \tab Bentler's invariant pattern simplicity criterion\cr \code{vgQ.tandemI} \tab orthogonal \tab Tandem principle I criterion \cr \code{vgQ.tandemII} \tab orthogonal \tab Tandem principle II criterion \cr \code{vgQ.geomin} \tab orthogonal or oblique \tab \cr \code{vgQ.bigeomin} \tab orthogonal or oblique \tab \cr \code{vgQ.cf} \tab orthogonal or oblique \tab Crawford-Ferguson family \cr \code{vgQ.cubimax} \tab orthogonal \tab \cr \code{vgQ.infomax} \tab orthogonal or oblique \tab \cr \code{vgQ.mccammon} \tab orthogonal \tab McCammon minimum entropy ratio \cr \code{vgQ.varimin} \tab orthogonal \tab varimin criterion \cr \code{vgQ.bifactor} \tab orthogonal or oblique \tab bifactor/biquartimin rotation\cr } See \link{rotations} for use of arguments. New rotation methods can be programmed with a name "vgQ.newmethod". The inputs are the matrix L, and optionally any additional arguments. The output should be a list with elements \code{f}, \code{Gq}, and \code{Method}. Gradient projection \emph{without} derivatives can be performed using the \code{GPArotateDF} package; type \code{vignette("GPArotateDF", package = "GPArotation")} at the command line. } \examples{ GPArotation:::vgQ.oblimin getAnywhere(vgQ.oblimax) } \seealso{ \code{\link{rotations}} } \references{ Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/man/NormalizingWeight.Rd0000644000176200001440000000100414342727133017145 0ustar liggesusers\name{NormalizingWeight} \alias{NormalizingWeight} \title{Internal Utility for Normalizing Weights} \usage{ NormalizingWeight(A, normalize=FALSE) } \arguments{ \item{A}{A loading matrix.} \item{normalize}{An indication of if/how the matrix should be normalized.} } \description{ See GPFRSoblq and GPFRSorth. } \value{A matrix. This function is not exported in the NAMESPACE, and is only used by the GP rotation functions. See \link{GPFRSorth} for an example of its use.} \keyword{internal} GPArotation/man/print.GPArotation.Rd0000644000176200001440000000323614557556406017052 0ustar liggesusers\name{print.GPArotation} \alias{print.GPArotation} \alias{summary.GPArotation} \alias{print.summary.GPArotation} \title{Print and Summary Methods for GPArotation} \usage{ \method{print}{GPArotation}(x, digits=3, sortLoadings=TRUE, rotateMat=FALSE, Table=FALSE, ...) \method{summary}{GPArotation}(object, digits=3, Structure=TRUE, ...) \method{print}{summary.GPArotation}(x, ...) } \arguments{ \item{x}{a GPArotation object to summarize.} \item{object}{a summary.GPArotation to print.} \item{digits}{precision of printed numbers.} \item{sortLoadings}{display sorted loadings of a GPArotation object.} \item{rotateMat}{display the rotation matrix of a GPArotation object.} \item{Table}{display the convergence table of a GPArotation object.} \item{Structure}{display the structure matrix of a summary.GPArotation object of a oblique rotation.} \item{...}{further arguments passed to other methods.} } \value{The object printed or a summary object.} \description{ Print an object or summary of an object returned by \code{GPFRSorth}, \code{GPFRSoblq}, \code{GPForth}, or \code{GPFoblq}. A GPArotation object by default to will print sorted loadings, Phi, and rotation matrix (if requested). Output includes contributions of factors \code{SS loadings} (Sum of Squared loadings), (see e.g. Harman 1976, sections 2.4 and 12.4). } \details{ For examples of print and summary functions, see \code{\link{GPForth}}. } \references{ Harman, H.H. (1976). \emph{Modern Factor Analysis.} The University of Chicago Press. }\seealso{ \code{\link{GPForth}}, \code{\link[base]{summary}} } \concept{rotation} \keyword{internal} GPArotation/man/rotations.Rd0000644000176200001440000002745714557726545015570 0ustar liggesusers\name{rotations} \alias{rotations} \alias{oblimin} \alias{quartimin} \alias{targetT} \alias{targetQ} \alias{pstT} \alias{pstQ} \alias{oblimax} \alias{entropy} \alias{quartimax} \alias{Varimax} \alias{simplimax} \alias{bentlerT} \alias{bentlerQ} \alias{tandemI} \alias{tandemII} \alias{geominT} \alias{geominQ} \alias{bigeominT} \alias{bigeominQ} \alias{cfT} \alias{cfQ} \alias{equamax} \alias{parsimax} \alias{infomaxT} \alias{infomaxQ} \alias{mccammon} \alias{varimin} \alias{bifactorT} \alias{bifactorQ} \title{Rotations} \usage{ oblimin(A, Tmat=diag(ncol(A)), gam=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) quartimin(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) targetT(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) targetQ(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) pstT(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) pstQ(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) oblimax(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) entropy(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) quartimax(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5,maxit=1000,randomStarts=0) Varimax(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) simplimax(A, Tmat=diag(ncol(A)), k=nrow(A), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bentlerT(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bentlerQ(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) tandemI(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) tandemII(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) geominT(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) geominQ(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bigeominT(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bigeominQ(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) cfT(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) cfQ(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) equamax(A, Tmat=diag(ncol(A)), kappa=ncol(A)/(2*nrow(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) parsimax(A, Tmat=diag(ncol(A)), kappa=(ncol(A)-1)/(ncol(A)+nrow(A)-2), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) infomaxT(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) infomaxQ(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) mccammon(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) varimin(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bifactorT(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000,randomStarts=0) bifactorQ(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000,randomStarts=0) } \arguments{ \item{A}{an initial loadings matrix to be rotated.} \item{Tmat}{initial rotation matrix.} \item{gam}{0=Quartimin, .5=Biquartimin, 1=Covarimin.} \item{Target}{rotation target for objective calculation.} \item{W}{weighting of each element in target.} \item{k}{number of close to zero loadings.} \item{delta}{constant added to Lambda^2 in objective calculation.} \item{kappa}{see details.} \item{normalize}{parameter passed to optimization routine (GPForth or GPFoblq).} \item{eps}{parameter passed to optimization routine (GPForth or GPFoblq).} \item{maxit}{parameter passed to optimization routine (GPForth or GPFoblq).} \item{randomStarts}{parameter passed to optimization routine (GPFRSorth or GPFRSoblq).} \item{L}{provided for backward compatibility in target rotations only. Use A going forward.} } \value{A list (which includes elements used by \code{factanal}) with: \item{loadings}{Lh from \code{GPFRSorth} or \code{GPFRSoblq}.} \item{Th}{Th from \code{GPFRSorth} or \code{GPFRSoblq}.} \item{Table}{Table from \code{GPForth} or \code{GPFoblq}.} \item{method}{A string indicating the rotation objective function.} \item{orthogonal}{A logical indicating if the rotation is orthogonal.} \item{convergence}{Convergence indicator from \code{GPFRSorth} or \code{GPFRSoblq}.} \item{Phi}{t(Th) \%*\% Th. The covariance matrix of the rotated factors. This will be the identity matrix for orthogonal rotations so is omitted (NULL) for the result from GPFRSorth and GPForth.} \item{randStartChar}{Vector indicating results from random starts from \code{GPFRSorth} or \code{GPFRSoblq}} } \description{ Optimize factor loading rotation objective. } \details{ These functions optimize a rotation objective. They can be used directly or the function name can be passed to factor analysis functions like \code{factanal}. Several of the function names end in T or Q, which indicates if they are orthogonal or oblique rotations (using \code{GPFRSorth} or \code{GPFRSoblq} respectively). Rotations which are available are \tabular{lll}{ \code{oblimin} \tab oblique \tab oblimin family \cr \code{quartimin} \tab oblique \tab \cr \code{targetT} \tab orthogonal \tab target rotation \cr \code{targetQ} \tab oblique \tab target rotation \cr \code{pstT} \tab orthogonal \tab partially specified target rotation \cr \code{pstQ} \tab oblique \tab partially specified target rotation \cr \code{oblimax} \tab oblique \tab \cr \code{entropy} \tab orthogonal \tab minimum entropy \cr \code{quartimax} \tab orthogonal \tab \cr \code{varimax} \tab orthogonal \tab \cr \code{simplimax} \tab oblique \tab \cr \code{bentlerT} \tab orthogonal \tab Bentler's invariant pattern simplicity criterion\cr \code{bentlerQ} \tab oblique \tab Bentler's invariant pattern simplicity criterion\cr \code{tandemI} \tab orthogonal \tab Tandem principle I criterion \cr \code{tandemII} \tab orthogonal \tab Tandem principle II criterion \cr \code{geominT} \tab orthogonal \tab \cr \code{geominQ} \tab oblique \tab \cr \code{bigeominT} \tab orthogonal \tab \cr \code{bigeominQ} \tab oblique \tab \cr \code{cfT} \tab orthogonal \tab Crawford-Ferguson family \cr \code{cfQ} \tab oblique \tab Crawford-Ferguson family \cr \code{equamax} \tab orthogonal \tab Crawford-Ferguson family \cr \code{parsimax} \tab orthogonal \tab Crawford-Ferguson family \cr \code{infomaxT} \tab orthogonal \tab \cr \code{infomaxQ} \tab oblique \tab \cr \code{mccammon} \tab orthogonal \tab McCammon minimum entropy ratio \cr \code{varimin} \tab orthogonal \tab \cr \code{bifactorT} \tab orthogonal \tab Jennrich and Bentler bifactor rotation\cr \code{bifactorQ} \tab oblique \tab Jennrich and Bentler biquartimin rotation\cr } Note that \code{Varimax} defined here uses \code{vgQ.varimax} and is not \code{varimax} defined in the \code{stats} package. \code{stats:::varimax} does Kaiser normalization by default whereas \code{Varimax} defined here does not. The argument \code{kappa} parameterizes the family for the Crawford-Ferguson method. If \code{m} is the number of factors and \code{p} is the number of indicators then \code{kappa} values having special names are 0=Quartimax, 1/p=Varimax, m/(2*p)=Equamax, (m-1)/(p+m-2)=Parsimax, 1=Factor parsimony. } \examples{ # see GPFRSorth and GPFRSoblq for more examples # getting loadings matrices data("Harman", package="GPArotation") qHarman <- GPFRSorth(Harman8, Tmat=diag(2), method="quartimax") qHarman <- quartimax(Harman8) loadings(qHarman) - qHarman$loadings #2 ways to get the loadings # factanal loadings used in GPArotation data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, normalize=TRUE, rotation="none") quartimax(loadings(fa.unrotated), normalize=TRUE) geominQ(loadings(fa.unrotated), normalize=TRUE, randomStarts=100) # passing arguments to factanal (See vignette for a caution) # vignette("GPAguide", package = "GPArotation") data(ability.cov) factanal(factors = 2, covmat = ability.cov, rotation="infomaxT") factanal(factors = 2, covmat = ability.cov, rotation="infomaxT", control=list(rotate=list(normalize = TRUE, eps = 1e-6))) # when using factanal for oblique rotation it is best to use the rotation command directly # instead of including it in the factanal command (see Vignette). fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, normalize=TRUE, rotation="none") quartimin(loadings(fa.unrotated), normalize=TRUE) # oblique target rotation of 2 varimax rotated matrices towards each other # See vignette for additional context and computation, trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) trx <- targetQ(trGermany, Target = trBritain) # Difference between rotated loadings matrix and target matrix y <- trx$loadings - trBritain # partially specified target; See vignette for additional method A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) targetT(A, Target=SPA) # using random starts data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, normalize=TRUE, rotation="none") # single rotation with a random start oblimin(loadings(fa.unrotated), Tmat=Random.Start(3)) oblimin(loadings(fa.unrotated), randomStarts=1) # multiple random starts oblimin(loadings(fa.unrotated), randomStarts=100) # assessing local minima for box26 data data(Thurstone, package = "GPArotation") infomaxQ(box26, normalize = TRUE, randomStarts = 150) geominQ(box26, normalize = TRUE, randomStarts = 150) # for detailed investigation of local minima, consult package 'fungible' # library(fungible) # faMain(urLoadings=box26, rotate="geominQ", rotateControl=list(numberStarts=150)) # library(psych) # package 'psych' with random starts: # faRotations(box26, rotate = "geominQ", hyper = 0.15, n.rotations = 150) } \seealso{ \code{\link{GPFRSorth}}, \code{\link{GPFRSoblq}}, \code{\link{vgQ}}, \code{\link{eiv}}, \code{\link{echelon}}, \code{\link{WansbeekMeijer}}, \code{\link[stats]{factanal}}, \code{\link[stats]{varimax}} } \references{ Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. Bifactor rotation, bifactorT and bifactorQ are called bifactor and biquartimin in Jennrich, R.I. and Bentler, P.M. (2011) Exploratory bi-factor analysis. \emph{Psychometrika}, \bold{76}. } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/man/GPA.Rd0000644000176200001440000002506414557556145014142 0ustar liggesusers\name{GPA} \alias{GPFRSorth} \alias{GPFRSoblq} \alias{GPForth} \alias{GPFoblq} \title{Rotation Optimization} \usage{ GPFRSorth(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL, randomStarts=0) GPFRSoblq(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL, randomStarts=0) GPForth(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL) GPFoblq(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL) } \arguments{ \item{A}{initial factor loadings matrix for which the rotation criterian is to be optimized.} \item{Tmat}{initial rotation matrix.} \item{normalize}{see details.} \item{eps}{convergence is assumed when the norm of the gradient is smaller than eps.} \item{maxit}{maximum number of iterations allowed in the main loop.} \item{method}{rotation objective criterian.} \item{methodArgs}{a list ofmethodArgs arguments passed to the rotation objective} \item{randomStarts}{number of random starts (GPFRSorth and GPFRSoblq)} } \description{ Gradient projection rotation optimization routine used by various rotation objective. } \value{A GPArotation object which is a list with elements \item{loadings}{The rotated loadings, one column for each factor. If randomStarts were requested then this is the rotated loadings matrix with the lowest criterion value.} \item{Th}{The rotation matrix, loadings \%*\% t(Th) = A.} \item{Table}{A matrix recording the iterations of the rotation optimization.} \item{method}{A string indicating the rotation objective function.} \item{orthogonal}{A logical indicating if the rotation is orthogonal.} \item{convergence}{A logical indicating if convergence was obtained.} \item{Phi}{t(Th) \%*\% Th. The covariance matrix of the rotated factors. This will be the identity matrix for orthogonal rotations so is omitted (NULL) for the result from GPFRSorth and GPForth.} \item{Gq}{The gradient of the objective function at the rotated loadings.} \item{randStartChar}{A vector with characteristics of random starts (GPFRSorth and GPFRSoblq only; omitted if randomStarts =< 1).} } \details{ Gradient projection (GP) rotation optimization routines developed by Jennrich (2001, 2002) and Bernaards and Jennrich (2005). These functions can be used directly to rotate a loadings matrix, or indirectly through a rotation objective passed to a factor estimation routine such as \code{\link{factanal}}. A rotation of a matrix \code{A} is defined as \code{A \%*\% solve(t(Th))}. In case of orthogonal rotation, the factors the rotation matrix \code{Tmat} is orthonormal, and the rotation simplifies to \code{A \%*\% Th}. The rotation matrix \code{Th} is computed by GP rotation. The \code{GPFRsorth} and \code{GPFRSoblq} functions are the primary functions for orthogonal and oblique rotations, respectively. These two functions serve as wrapper functions for \code{GPForth} and \code{GPFoblq}, with the added functionality of multiple random starts. \code{GPForth} is the main GP algorithm for orthogonal rotation. \code{GPFoblq} is the main GP algorithm for oblique rotation. The \code{GPForth} and \code{GPFoblq} may be also be called directly. Arguments in the wrapper functions \code{GPFRsorth} and \code{GPFRSoblq} are passed to GP algorithms. Functions require an initial loadings matrix \code{A} which fixes the equivalence class over which the optimization is done. It must be the solution to the orthogonal factor analysis problem as obtained from \code{factanal} or other factor estimation routines. The initial rotation matrix is given by the \code{Tmat}. By default the GP algorithm use the identity matrix as the initial rotation matrix. For some rotation criteria local minima may exist. To start from random initial rotation matrices, the \code{randomStarts} argument is available in \code{GPFRSorth} and \code{GPFRSoblq}. The returned object includes the rotated loadings matrix with the lowest criterion value \code{f} among attemnpted starts.Technically, this does not have to be the global minimum. The \code{randomStarts} argument is not available \code{GPForth} and \code{GPFoblq}. However, for \code{GPForth} and \code{GPFoblq} a single random initial rotation matrix may be set by \code{Tmat = \link{Random.Start}(ncol(A))}. The argument \code{method} can be used to specify a string indicating the rotation objective. Oblique rotation defaults to \code{"quartimin"} and orthogonal rotation defaults to \code{"varimax"}. Available rotation objectives are \code{"oblimin"}, \code{"quartimin"}, \code{"target"}, \code{"pst"}, \code{"oblimax"}, \code{"entropy"}, \code{"quartimax"}, \code{"varimax"}, \code{"simplimax"}, \code{"bentler"}, \code{"tandemI"}, \code{"tandemII"}, \code{ "geomin"}, \code{"cf"}, \code{"infomax"}, \code{"mccammon"}, \code{bifactor}, and \code{"varimin"}. The string is prefixed with "vgQ." to give the actual function call. See \code{\link{vgQ}} for details. Some rotation criteria (\code{"oblimin"}, \code{"target"}, \code{"pst"}, \code{"simplimax"}, \code{"geomin"}, \code{"cf"}) require one or more additional arguments. See \code{link{rotations}} for details and default values, if applicable. For examples of the indirect use see \code{\link{rotations}}. The argument \code{normalize} gives an indication of if and how any normalization should be done before rotation, and then undone after rotation. If \code{normalize} is \code{FALSE} (the default) no normalization is done. If \code{normalize} is \code{TRUE} then Kaiser normalization is done. (So squared row entries of normalized \code{A} sum to 1.0. This is sometimes called Horst normalization.) If \code{normalize} is a vector of length equal to the number of indicators (= number of rows of \code{A}) then the colums are divided by \code{normalize} before rotation and multiplied by \code{normalize} after rotation. If \code{normalize} is a function then it should take \code{A} as an argument and return a vector which is used like the vector above. See Nguyen and Waller (2022) for detailed investigation of normalization on factor rotations, including potential effect on qualitative interpretation of loadings. } \seealso{ \code{\link{Random.Start}}, \code{\link[stats]{factanal}}, \code{\link{oblimin}}, \code{\link{quartimin}}, \code{\link{targetT}}, \code{\link{targetQ}}, \code{\link{pstT}}, \code{\link{pstQ}}, \code{\link{oblimax}}, \code{\link{entropy}}, \code{\link{quartimax}}, \code{\link{Varimax}}, \code{\link[stats]{varimax}}, \code{\link{simplimax}}, \code{\link{bentlerT}}, \code{\link{bentlerQ}}, \code{\link{tandemI}}, \code{\link{tandemII}}, \code{\link{geominT}}, \code{\link{geominQ}}, \code{\link{bigeominT}}, \code{\link{bigeominQ}}, \code{\link{cfT}}, \code{\link{cfQ}}, \code{\link{equamax}}, \code{\link{parsimax}}, \code{\link{infomaxT}}, \code{\link{infomaxQ}}, \code{\link{mccammon}}, \code{\link{varimin}}, \code{\link{bifactorT}}, \code{\link{bifactorQ}}, \code{\link[stats]{promax}} } \examples{ # see rotations for more examples data(Harman, package = "GPArotation") GPFRSorth(Harman8, method = "quartimax") quartimax(Harman8) GPFRSoblq(Harman8, method = "quartimin", normalize = TRUE) loadings( quartimin(Harman8, normalize = TRUE) ) # using random starts data("WansbeekMeijer", package = "GPArotation") fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, normalize=TRUE, rotation="none") GPFRSoblq(loadings(fa.unrotated), normalize = TRUE, method = "oblimin", randomStarts = 100) oblimin(loadings(fa.unrotated), randomStarts=100) data(Thurstone, package = "GPArotation") geominQ(box26, normalize = TRUE, randomStarts=100) # displaying results of factor analysis rotation output origdigits <- options("digits") Abor.unrotated <- factanal(factors = 2, covmat = ability.cov, rotation = "none") Abor <- oblimin(loadings(Abor.unrotated), randomStarts = 20) Abor print(Abor) print(Abor, sortLoadings=FALSE) #this matches the output passed to factanal print(Abor, Table=TRUE) print(Abor, rotateMat=TRUE) print(Abor, digits=2) # by default provides the structure matrix for oblique rotation summary(Abor) summary(Abor, Structure=FALSE) options(digits = origdigits$digits) # GPArotation output does sort loadings, but use print to obtain if needed set.seed(334) xusl <- quartimin(Harman8, normalize = TRUE, randomStarts=100) # loadings without ordering (default) loadings(xusl) max(abs(print(xusl)$loadings - xusl$loadings)) == 0 # FALSE # output sorted loadings via print (not default) xsl <- print(xusl) max(abs(print(xsl)$loadings - xsl$loadings)) == 0 # TRUE # Kaiser normalization is used when normalize=TRUE factanal(factors = 2, covmat = ability.cov, rotation = "oblimin", control=list(rotate=list(normalize = TRUE))) # Cureton-Mulaik normalization can be done by passing values to the rotation # may result in convergence problems NormalizingWeightCM <- function (L) { Dk <- diag(sqrt(diag(L \%*\% t(L)))^-1) \%*\% L wghts <- rep(0, nrow(L)) fpls <- Dk[, 1] acosi <- acos(ncol(L)^(-1/2)) for (i in 1:nrow(L)) { num <- (acosi - acos(abs(fpls[i]))) dem <- (acosi - (function(a, m) ifelse(abs(a) < (m^(-1/2)), pi/2, 0))(fpls[i], ncol(L))) wghts[i] <- cos(num/dem * pi/2)^2 + 0.001 } Dv <- wghts * sqrt(diag(L \%*\% t(L)))^-1 Dv } quartimin(Harman8, normalize = NormalizingWeightCM(Harman8), randomStarts=100) quartimin(Harman8, normalize = TRUE, randomStarts=100) } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert} \references{ Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. Jennrich, R.I. (2001). A simple general procedure for orthogonal rotation. \emph{Psychometrika}, \bold{66}, 289--306. Jennrich, R.I. (2002). A simple general method for oblique rotation. \emph{Psychometrika}, \bold{67}, 7--19. Nguyen, H.V. and Waller, N.G. (2022). Local minima and factor rotations in exploratory factor analysis. \emph{ Psychological Methods}. Advance online publication. https://doi.org/10.1037/met0000467 } \concept{rotation} \keyword{multivariate} GPArotation/man/WansbeekMeijer.Rd0000644000176200001440000000117714323662111016403 0ustar liggesusers\name{WansbeekMeijer} \docType{data} \alias{WansbeekMeijer} \alias{NetherlandsTV} \title{Factor Example from Wansbeek and Meijer} \description{ Netherlands TV viewership example p 171, Wansbeek and Meijer (2000) } \usage{ data(WansbeekMeijer) } \details{ The object NetherlandsTV is loaded from the data file WansbeekMeijer. } \format{ The object NetherlandsTV is a correlation matrix. } \source{ Tom Wansbeek and Erik Meijer (2000) \emph{Measurement Error and Latent Variables in Econometrics}, Amsterdam: North-Holland. } \seealso{ \code{\link{GPForth}}, \code{\link{Thurstone}}, \code{\link{Harman}} } \keyword{datasets} GPArotation/man/Random.Start.Rd0000644000176200001440000000451214557730431016032 0ustar liggesusers\name{Random.Start} \alias{Random.Start} \title{Generate a Random Orthogonal Rotation} \usage{ Random.Start(k) } \arguments{ \item{k}{An integer indicating the dimension of the square matrix.} } \description{ Random orthogonal rotation to use as Tmat matrix to start GPFRSorth, GPFRSoblq, GPForth, or GPFoblq. } \value{An orthogonal matrix.} \details{ The random start function produces an orthogonal matrix with columns of length one based on the QR decompostion. This randomization procedures follows the logic of Stewart(1980) and Mezzari(2007), as of GPArotation version 2024.2-1. } \seealso{ \code{\link{GPFRSorth}}, \code{\link{GPFRSoblq}}, \code{\link{GPForth}}, \code{\link{GPFoblq}}, \code{\link{rotations}} } \examples{ # Generate a random ortogonal matrix of dimension 5 x 5 Random.Start(5) # function for generating orthogonal or oblique random matrix Random.Start <- function(k = 2L,orthogonal=TRUE){ mat <- matrix(rnorm(k*k),k) if (orthogonal){ qr.out <- qr(matrix(rnorm(k * k), nrow = k, ncol = k)) Q <- qr.Q(qr.out) R <- qr.R(qr.out) R.diag <- diag(R) R.diag2 <- R.diag/abs(R.diag) ans <- t(t(Q) * R.diag2) ans } else{ ans <- mat \%*\% diag(1/sqrt(diag(crossprod(mat)))) } ans } data("Thurstone", package="GPArotation") simplimax(box26,Tmat = Random.Start(3, orthogonal = TRUE)) simplimax(box26,Tmat = Random.Start(3, orthogonal = FALSE)) # covariance matrix is Phi = t(Th) \%*\% Th rms <- Random.Start(3, FALSE) t(rms) \%*\% rms # covariance matrix because oblique rms rms <- Random.Start(3, TRUE) t(rms) \%*\% rms # identity matrix because orthogonal rms } \references{ Stewart, G. W. (1980). The Efficient Generation of Random Orthogonal Matrices with an Application to Condition Estimators. \emph{SIAM Journal on Numerical Analysis}, \bold{17}(3), 403--409. http://www.jstor.org/stable/2156882 Mezzadri, F. (2007). How to generate random matrices from the classical compact groups. \emph{Notices of the American Mathematical Society}, \bold{54}(5), 592--604. https://arxiv.org/abs/math-ph/0609050 } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert. Additional input from Yves Rosseel. } \concept{rotation} \keyword{multivariate} GPArotation/man/00.GPArotation.Rd0000644000176200001440000001413614557726044016133 0ustar liggesusers\name{00.GPArotation} \alias{GPArotation} \alias{GPArotation-package} \alias{GPArotation.Intro} \docType{package} \title{Gradient Projection Algorithms for Factor Rotation} \description{GPA Rotation for Factor Analysis The GPArotation package contains functions for the rotation of factor loadings matrices. The functions implement Gradient Projection (GP) algorithms for orthogonal and oblique rotation. Additionally, a number of rotation criteria are provided. The GP algorithms minimize the rotation criterion function, and provide the corresponding rotation matrix. For oblique rotation, the covariance / correlation matrix of the factors is also provided. The rotation criteria implemented in this package are described in Bernaards and Jennrich (2005). Theory of the GP algorithm is described in Jennrich (2001, 2002) publications. Additionally 2 rotation methods are provided that do not rely on GP (eiv and echelon) \tabular{ll}{ Package: \tab GPArotation\cr Depends: \tab R (>= 2.0.0)\cr License: \tab GPL Version 2.\cr URL: \tab https://optimizer.r-forge.r-project.org/GPArotation_www/\cr } Index of functions: Wrapper functions that include random starts option \cr \tabular{ll}{ \code{\link{GPFRSorth}} \tab Orthogonal rotation with random starts \cr \code{\link{GPFRSorth}} \tab Oblique rotation with random starts \cr } Gradient Projection Rotation Algorithms (code unchanged since 2008)\cr \tabular{ll}{ \code{\link{GPForth}} \tab Orthogonal rotation function \cr \code{\link{GPForth}} \tab Oblique rotation function \cr } Utility functions\cr \tabular{ll}{ \code{\link{Random.Start}} \tab Generate random a starting matrix \cr \code{\link{NormalizingWeight}} \tab Kaiser normalization (not exported from NAMESPACE) \cr \code{\link{print.GPArotation}}\tab Print results (S3 level function) \cr \code{\link{summary.GPArotation}} \tab Summary of results (S3 level function) \cr} Rotations\cr \tabular{ll}{ \code{\link{oblimin}} \tab Oblimin rotation \cr \code{\link{quartimin}} \tab Quartimin rotation \cr \code{\link{targetT}} \tab Orthogonal Target rotation \cr \code{\link{targetQ}} \tab Oblique Target rotation \cr \code{\link{pstT}} \tab Orthogonal Partially Specified Target rotation \cr \code{\link{pstQ}} \tab Oblique Partially Specified Target rotation \cr \code{\link{oblimax}} \tab Oblimax rotation \cr \code{\link{entropy}} \tab Minimum Entropy rotation \cr \code{\link{quartimax}} \tab Quartimax rotation \cr \code{\link{Varimax}} \tab Varimax rotation \cr \code{\link{simplimax}} \tab Simplimax rotation \cr \code{\link{bentlerT}} \tab Orthogonal Bentler's Invariant Pattern Simplicity rotation \cr \code{\link{bentlerQ}} \tab Oblique Bentler's Invariant Pattern Simplicity rotation \cr \code{\link{tandemI}} \tab The Tandem Criteria Principle I rotation \cr \code{\link{tandemII}} \tab The Tandem Criteria Principle II rotation \cr \code{\link{geominT}} \tab Orthogonal Geomin rotation \cr \code{\link{geominQ}} \tab Oblique Geomin rotation \cr \code{\link{bigeominT}} \tab Orthogonal Bi-Geomin rotation \cr \code{\link{bigeominQ}} \tab Oblique Bi-Geomin rotation \cr \code{\link{cfT}} \tab Orthogonal Crawford-Ferguson Family rotation \cr \code{\link{cfQ}} \tab Oblique Crawford-Ferguson Family rotation \cr \code{\link{equamax}} \tab Equamax rotation \cr \code{\link{parsimax}} \tab Parsimax rotation \cr \code{\link{infomaxT}} \tab Orthogonal Infomax rotation \cr \code{\link{infomaxQ}} \tab Oblique Infomax rotation \cr \code{\link{mccammon}} \tab McCammon Minimum Entropy Ratio rotation \cr \code{\link{varimin}} \tab Varimin rotation \cr \code{\link{bifactorT}} \tab Orthogonal Bifactor rotation \cr \code{\link{bifactorQ}} \tab Oblique Bifactor rotation \cr \code{\link{eiv}} \tab Errors-in-Variables rotation \cr \code{\link{echelon}} \tab Echelon rotation \cr } vgQ routines to compute value and gradient of the criterion (not exported from NAMESPACE)\cr \tabular{ll}{ \code{\link{vgQ.oblimin}} \tab Oblimin vgQ \cr \code{\link{vgQ.quartimin}}\tab Quartimin vgQ \cr \code{\link{vgQ.target}}\tab Target vgQ \cr \code{\link{vgQ.pst}}\tab Partially Specified Target vgQ \cr \code{\link{vgQ.oblimax}} \tab Oblimax vgQ \cr \code{\link{vgQ.entropy}} \tab Minimum Entropy vgQ \cr \code{\link{vgQ.quartimax}}\tab Quartimax vgQ \cr \code{\link{vgQ.varimax}}\tab Varimax vgQ \cr \code{\link{vgQ.simplimax}}\tab Simplimax vgQ \cr \code{\link{vgQ.bentler}}\tab Bentler's Invariant Pattern Simplicity vgQ \cr \code{\link{vgQ.tandemI}}\tab The Tandem Criteria Principle I vgQ \cr \code{\link{vgQ.tandemII}}\tab The Tandem Criteria Principle II vgQ \cr \code{\link{vgQ.geomin}}\tab Geomin vgQ \cr \code{\link{vgQ.bigeomin}}\tab Bi-Geomin vgQ \cr \code{\link{vgQ.cf}} \tab Crawford-Ferguson Family vgQ \cr \code{\link{vgQ.infomax}} \tab Infomax vgQ \cr \code{\link{vgQ.mccammon}}\tab McCammon Minimum Entropy Ratio vgQ \cr \code{\link{vgQ.varimin}}\tab Varimin vgQ \cr \code{\link{vgQ.bifactor}}\tab Bifactor vgQ \cr } Data sets included in the GPArotation package \cr \tabular{ll}{ \code{\link{Harman}}\tab Initial factor loading matrix for Harman's 8 physical variables \cr \code{\link{Thurstone}} \tab box20 and box26 initial factor loadings matrices \cr \code{\link{WansbeekMeijer}} \tab Netherlands TV viewership \cr } } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert. Code is modified from original source \file{splusfunctions.net} available at \url{https://optimizer.r-forge.r-project.org/GPArotation_www/}. } \references{ The software reference is Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. Theory of gradient projection algorithms may be found in: Jennrich, R.I. (2001). A simple general procedure for orthogonal rotation. \emph{Psychometrika}, \bold{66}, 289--306. Jennrich, R.I. (2002). A simple general method for oblique rotation. \emph{Psychometrika}, \bold{67}, 7--19. } \keyword{ package } \seealso{ \code{\link{GPFRSorth}}, \code{\link{GPFRSoblq}}, \code{\link{rotations}}, \code{\link{vgQ}} } GPArotation/man/Harman.Rd0000644000176200001440000000106414323662111014711 0ustar liggesusers\name{Harman} \docType{data} \alias{Harman} \alias{Harman8} \title{Example Data from Harman} \description{ Harman8 is initial factor loading matrix for Harman's 8 physical variables. } \usage{ data(Harman) } \details{ The object Harman8 is loaded from the data file Harman. } \format{ The object Harman8 is a matrix. } \source{ Harman, H. H. (1976) \emph{Modern Factor Analysis}, Third Edition Revised, University of Chicago Press. } \seealso{ \code{\link{GPForth}}, \code{\link{Thurstone}}, \code{\link{WansbeekMeijer}} } \keyword{datasets} GPArotation/man/echelon.Rd0000644000176200001440000000753714323662111015133 0ustar liggesusers\name{echelon} \alias{echelon} \title{Echelon Rotation} \usage{ echelon(L, reference=seq(NCOL(L)), ...) } \arguments{ \item{L}{a factor loading matrix} \item{reference}{indicates rows of loading matrix that should be used to determine the rotation transformation.} \item{...}{additional arguments discarded.} } \value{A list (which includes elements used by \code{factanal}) with: \item{loadings}{The new loadings matrix.} \item{Th}{The rotation.} \item{method}{A string indicating the rotation objective function ("echelon").} \item{orthogonal}{For consistency with other rotation results. Always TRUE.} \item{convergence}{For consistency with other rotation results. Always TRUE.} } \description{ Rotate to an echelon parameterization. } \details{ The loadings matrix is rotated so the \eqn{k}{k} rows of the loading matrix indicated by \code{reference} are the Cholesky factorization given by \code{t(chol(L[reference,] \%*\% t(L[reference,])))}. This defines the rotation transformation, which is then also applied to other rows to give the new loadings matrix. The optimization is not iterative and does not use the GPA algorithm. The function can be used directly or the function name can be passed to factor analysis functions like \code{factanal}. An orthogonal solution is assumed (so \eqn{\Phi}{Phi} is identity). The default uses the first \eqn{k}{k} rows as the reference. If the submatrix of \code{L} indicated by reference is singular then the rotation will fail and the user needs to supply a different choice of rows. One use of this parameterization is for obtaining good starting values (so it may appear strange to rotate towards this solution afterwards). It has a few other purposes: (1) It can be useful for comparison with published results in this parameterization. (2) The S.E.s are more straightforward to compute, because it is the solution to an unconstrained optimization (though not necessarily computed as such). (3) The models with k and (k+1) factors are nested, so it is more straightforward to test the k-factor model versus the (k+1)-factor model. In particular, in addition to the LR test (which does not depend on the rotation), now the Wald test and LM test can be used as well. For these, the test of a k-factor model versus a (k+1)-factor model is a joint test whether all the free parameters (loadings) in the (k+1)st column of \code{L} are zero. (4) For some purposes, only the subspace spanned by the factors is important, not the specific parameterization within this subspace. (5) The back-predicted indicators (explained portion of the indicators) do not depend on the rotation method. Combined with the greater ease to obtain correct standard errors of this method, this allows easier and more accurate prediction-standard errors. (6) This parameterization and its standard errors can be used to detect identification problems (McDonald, 1999, pp. 181-182). } \examples{ data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, rotation="none") fa.ech <- echelon(fa.unrotated$loadings) fa.ech2 <- factanal(factors = 2, covmat=NetherlandsTV, rotation="echelon") cbind(loadings(fa.unrotated), loadings(fa.ech), loadings(fa.ech2)) fa.ech3 <- echelon(fa.unrotated$loadings, reference=6:7) cbind(loadings(fa.unrotated), loadings(fa.ech), loadings(fa.ech3)) } \seealso{ \code{\link{eiv}}, \code{\link{rotations}}, \code{\link{GPForth}}, \code{\link{GPFoblq}} } \references{ Roderick P. McDonald (1999) \emph{Test Theory: A Unified Treatment}, Mahwah, NJ: Erlbaum. Tom Wansbeek and Erik Meijer (2000) \emph{Measurement Error and Latent Variables in Econometrics}, Amsterdam: North-Holland. } \author{Erik Meijer and Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/DESCRIPTION0000644000176200001440000000176114557762672014201 0ustar liggesusersPackage: GPArotation Version: 2024.2-1 Title: Gradient Projection Factor Rotation Authors@R: c( person("Coen", "Bernaards", email = "cab.gparotation@gmail.com", role = c("aut","cre")), person("Paul", "Gilbert", email = "pgilbert.ttv9z@ncf.ca", role = "aut"), person("Robert", "Jennrich", role = "aut") ) Depends: R (>= 2.0.0) Description: Gradient Projection Algorithms for Factor Rotation. For details see ?GPArotation. When using this package, please cite: Bernaards and Jennrich (2005) . "Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis". LazyData: yes Imports: stats License: GPL (>= 2) URL: https://optimizer.r-forge.r-project.org/GPArotation_www/ NeedsCompilation: no Packaged: 2024-02-04 16:11:29 UTC; coen Author: Coen Bernaards [aut, cre], Paul Gilbert [aut], Robert Jennrich [aut] Maintainer: Coen Bernaards Repository: CRAN Date/Publication: 2024-02-04 19:30:02 UTC GPArotation/build/0000755000176200001440000000000014557733461013557 5ustar liggesusersGPArotation/build/vignette.rds0000644000176200001440000000051614557733461016120 0ustar liggesusersP=O0MBE @,HQ:Tu.Őia7\Z;$bA?{2qu|ZGO/cH{f1aSO> 8#E7 䱄\%7ZE\hl(6Pn mӯ .N^qbn ܗLZɪ3p+XG.,TW۞ط"ыvtm9ʌ[GjLWTο׉B1+eVJϾ,Ѐ~$Sg[yɣ ҄SHqnnM'zڑHyi;$$=Eo`ȇGPArotation/tests/0000755000176200001440000000000014557733461013622 5ustar liggesusersGPArotation/tests/Harman.R0000644000176200001440000001261514323662112015141 0ustar liggesusers#Example from: Gradient Projection Algorithms and Software for # Arbitrary Rotation Criteria in Factor Analysis. # by Coen A. Bernaards and Robert I. Jennrich # Website: http://www.stat.ucla.edu/research Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() fuzz <- 1e-5 # using eps=1e-5 these tests do not do better than this all.ok <- TRUE # quartimax (orthogonal) rotation of Harman's 8 physical variables. data("Harman", package="GPArotation") qHarman <- GPForth(Harman8, Tmat=diag(2), method="quartimax") qHarman2 <- quartimax(Harman8) if( fuzz < max(abs(qHarman$loadings - qHarman2$loadings))) { cat("Calculated value is not the same as test value in test Harman 1. Value:\n") print(qHarman$loadings, digits=18) cat("difference:\n") print(qHarman$loadings - qHarman2$loadings, digits=18) all.ok <- FALSE } #qHarman$Th - qHarman2$Th # with eps=1e-8 # tst <- t(matrix(c( # 0.898754567491920398, 0.194823580226859222, # 0.933943406208487592, 0.129748657024604030, # 0.902131483644799892, 0.103864268239045668, # 0.876508251941102934, 0.171284220753554678, # 0.315572019798302239, 0.876476069451083251, # 0.251123191235179066, 0.773488941629975613, # 0.198007116064591759, 0.714678376605717203, # 0.307857241091366252, 0.659334451631046314 # ), 2, 8)) # with eps=1e-5 tst <- t(matrix(c( 0.898755404698461491, 0.194819718009510034, 0.933943963768413821, 0.129744643590955028, 0.902131929972106672, 0.103860391510923730, 0.876508987992224209, 0.171280454135453869, 0.315575786273609882, 0.876474713336210853, 0.251126515144778573, 0.773487862471829213, 0.198010187248201075, 0.714677525703678707, 0.307860074444663512, 0.659333128670876345 ), 2, 8)) if( fuzz < max(abs(qHarman$loadings - tst ))) { cat("Calculated value is not the same as test value in test Harman 2. Value:\n") print(qHarman$loadings, digits=18) cat("difference:\n") print(qHarman$loadings - tst, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 0.790828307905322436, 0.612038060430562525, # -0.612038060430562525, 0.790828307905322214 # ), 2, 2)) # with eps=1e-5 tst <- t(matrix(c( 0.790830938007507367, 0.612034662000581764, -0.612034662000581764, 0.790830938007507145 ), 2, 2)) if( fuzz < max(abs(qHarman$Th - tst ))) { cat("Calculated value is not the same as test value in test Harman 3. Value:\n") print(qHarman$Th, digits=18) cat("difference:\n") print(qHarman$Th - tst, digits=18) all.ok <- FALSE } # quartimin (oblique) rotation of Harman's 8 physical variables. qminHarman <- GPFoblq(Harman8, Tmat=diag(2), method="quartimin") qminHarman2 <- quartimin(Harman8) if( fuzz < max(abs(qminHarman$loadings - qminHarman2$loadings))) { cat("Calculated value is not the same as test value in test Harman 4. Value:\n") print(qminHarman$loadings, digits=18) cat("difference:\n") print(qminHarman$loadings - qminHarman2$loadings, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 0.8918217697289939627, 0.0560146456758183961, # 0.9536799985772628219, -0.0232460005406671701, # 0.9291498623396581280, -0.0465027396531852502, # 0.8766828510822184395, 0.0336582451338717017, # 0.0136988312985193428, 0.9250013826349388069, # -0.0172668087945964319, 0.8212535444941218010, # -0.0524468998178311899, 0.7649536381341245361, # 0.0858880630098148856, 0.6831160953442911854 # ),2, 8)) # with eps=1e-5 tst <- t(matrix(c( 0.8918219293548808047, 0.0560145122875230911, 0.9536799846795966928, -0.0232460559140742311, 0.9291497958388006406, -0.0465027685653178480, 0.8766829604751505967, 0.0336581364763500201, 0.0137008854716444972, 0.9250004106413580729, -0.0172649861805529957, 0.8212526839806429946, -0.0524452035885302342, 0.7649528396536503516, 0.0858895830186393733, 0.6831153711863455769 ),2, 8)) if( fuzz < max(abs(qminHarman$loadings - tst ))) { cat("Calculated value is not the same as test value in test Harman 5. Value:\n") print(qminHarman$loadings, digits=18) cat("difference:\n") print(qminHarman$loadings - tst, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 1.000000000000000000, 0.472747617396915065, # 0.472747617396915065, 1.000000000000000000 # ),2, 2)) # with eps=1e-5 tst <- t(matrix(c( 1.000000000000000222, 0.472745958387102538, 0.472745958387102538, 1.000000000000000000 ),2, 2)) if( fuzz < max(abs(qminHarman$Phi - tst ))) { cat("Calculated value is not the same as test value in test Harman 6. Value:\n") print(qminHarman$Phi, digits=18) cat("difference:\n") print(qminHarman$Phi - tst, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 0.878125245495924522, 0.836723841642554422, # -0.478430823863515542, 0.547625065922776710 # ),2, 2)) # with eps=1e-5 tst <- t(matrix(c( 0.878125280760480686, 0.836722770276292271, -0.478430759137962514, 0.547626702874473570 ),2, 2)) if( fuzz < max(abs(qminHarman$Th - tst ))) { cat("Calculated value is not the same as test value in test Harman 7. Value:\n") print(qminHarman$Th, digits=18) cat("difference:\n") print(qminHarman$Th - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/WansbeekMeijer.R0000644000176200001440000000606314323662112016626 0ustar liggesusers Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() fuzz <- 1e-6 all.ok <- TRUE data(WansbeekMeijer, package="GPArotation") fa.none <- factanal(factors=2, covmat=NetherlandsTV, rotation="none") tst <- t(matrix(c( 0.6972803, -0.3736554, 0.7774628, -0.3184149, 0.6832300, -0.3620428, 0.6612198, 0.2361132, 0.6972393, 0.3026050, 0.7100285, 0.4059509, 0.6353584, 0.3526947 ), 2, 7)) if( fuzz < max(abs(fa.none$loadings - tst))) { cat("Calculated value is not the same as test value in test WansbeekMeijer 1. Value:\n") print(fa.none$loadings, digits=18) cat("difference:\n") print(fa.none$loadings - tst, digits=18) all.ok <- FALSE } fa.varimax <- GPFoblq(fa.none$loadings, method="varimax", normalize=TRUE) # with eps=1e-8 # tst <- t(matrix(c( # 0.229695829694226694, -0.757005882905721683, # 0.325474298411086493, -0.774533969509160203, # 0.227951538606475851, -0.738861531224136225, # 0.634850649690308022, -0.299876110481063607, # 0.707312661165822032, -0.278246783076943283, # 0.789359884149245072, -0.214120439603779994, # 0.698885205896135120, -0.199081171877497243 # ), 2, 7)) # with eps=1e-5 tst <- t(matrix(c( 0.229698038368303409, -0.757005212686898243, 0.325476558225504142, -0.774533019824047542, 0.227953694341768043, -0.738860866094951829, 0.634851524619887475, -0.299874258087383661, 0.707313472988376213, -0.278244719250824557, 0.789360508873491518, -0.214118136377292989, 0.698885786741510029, -0.199079132641678647 ), 2, 7)) if( fuzz < max(abs(fa.varimax$loadings - tst))) { cat("Calculated value is not the same as test value in test WansbeekMeijer 2. Value:\n") print(fa.varimax$loadings, digits=18) cat("difference:\n") print(fa.varimax$loadings - tst, digits=18) all.ok <- FALSE } fa.oblimin <- GPFoblq(fa.none$loadings, method="oblimin", normalize=TRUE) # with eps=1e-8 # tst <- t(matrix(c( # -0.0244898894997362740, -0.8055076884898763057, # 0.0821776433220552660, -0.7883517482514345032, # -0.0194442483441249758, -0.7847120136813017233, # 0.6350106056917923514, -0.1038114236654337219, # 0.7293893902400611085, -0.0495156037400738894, # 0.8517915457391848078, 0.0588983480418694277, # 0.7504355940804637859, 0.0408946221245683056 # ), 2, 7)) # with eps=1e-5 tst <- t(matrix(c( -0.0244886312423446446, -0.8055069385602275922, 0.0821788889356081659, -0.7883509906546982693, -0.0194430219824419312, -0.7847112821295906260, 0.6350108529538124325, -0.1038111848933331444, 0.7293895650539216069, -0.0495153948664520185, 0.8517915670863017708, 0.0588984825074335624, 0.7504356301074717184, 0.0408947509009953206 ), 2, 7)) if( fuzz < max(abs(fa.oblimin$loadings - tst))) { cat("Calculated value is not the same as test value in test WansbeekMeijer 3. Value:\n") print(fa.oblimin$loadings, digits=18) cat("difference:\n") print(fa.oblimin$loadings - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/varimaxVarimax.R0000644000176200001440000000250614336234075016737 0ustar liggesusers# Also see the first test in rotations.R # compares varimax to Varimax to 0.001 discrepancy Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() ### note that this is a slightly lower bar than other tests ### to correct for the built-in varimax function working differently ### than GPA, and to ensure Varimax convergence ### these are differences in the 4th decimal or better fuzz <- 1e-4 ### all.ok <- TRUE sortFac <- function(x){ # Based on Fungible faSort vx <- order(colSums(x$loadings^2), decreasing = TRUE) Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vx] x$Th <- x$Th %*% Dsgn x$loadings <- x$loadings %*% Dsgn if ("Phi" %in% names(x)) { x$Phi <- diag(1/diag(Dsgn)) %*% x$Phi %*% Dsgn } x } data(Thurstone, package="GPArotation") yv1 <- varimax(box20, normalize = FALSE, eps = 1e-7) #built-in R names(yv1) <- c("loadings","Th") yv1 <- sortFac(yv1) yv2 <- sortFac(Varimax(box20, normalize = FALSE, maxit = 10000, eps = 1e-7)) #GPArotation version # yv.diff <- unclass(yv1$loadings) - unclass(yv2$loadings) # max(abs(yv.diff)) if( fuzz < max(abs(yv1$loadings - yv2$loadings))) { cat("Calculated varimax is not the same as Varimax:\n") # print(yv2$loadings, digits=18) cat("difference:\n") print(yv1$loadings - yv2$loadings, digits=18) all.ok <- FALSE } GPArotation/tests/Revelle.R0000644000176200001440000000072214323662112015325 0ustar liggesusers# This tests fix for an error caused by an exact initial setting. # (from William Revelle) require("GPArotation") f3 <- structure(c(0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,0), .Dim = c(6L, 3L), .Dimnames = list(NULL, c("PC1", "PC2", "PC3"))) f3 # PC1 PC2 PC3 #[1,] 0 0 1 #[2,] 0 1 0 #[3,] 1 0 0 #[4,] 0 0 1 #[5,] 0 1 0 #[6,] 1 0 0 # These previously gave object 'VgQt' not found GPForth(f3) Varimax(f3) GPArotation/tests/MASSoblimin.R0000644000176200001440000000447614323662112016056 0ustar liggesusers Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() #require("stats") fuzz <- 1e-6 all.ok <- TRUE # test MASS 4th ed. p 322-324 data(ability.cov) ability.cov ability.FA <- factanal(factors = 1, covmat=ability.cov) (ability.FA <- update(ability.FA, factors = 2)) # ability.FA2 <- factanal(factors = 2, covmat = ability.cov) # max(abs(ability.FA2$loadings - ability.FA$loadings)) # summary(ability.FA) MASS ed.4 p 323 seems to be print not summary in R 2.0.1 ability.FA # this is default varimax rotation. There are 3rd+ digit differences with MASS tst <- t(matrix(c( 0.499437829039896530, 0.54344904693111962, 0.156070079431279873, 0.62153798991197484, 0.205786989958578748, 0.85992588538426895, 0.108530754440558652, 0.46776101732283504, 0.956242470279811574, 0.18209631992182243, 0.784768183877880943, 0.22482213687364205 ), 2, 6)) if( fuzz < max(abs(loadings(ability.FA) - tst))) { cat("Calculated value is not the same as test value in test 1. Value:\n") #print(loadings(ability.FA), digits=18) this truncates print(unclass(ability.FA$loadings), digits=18) cat("difference:\n") print(unclass(ability.FA$loadings) - tst, digits=18) all.ok <- FALSE } # differences with MASS here are a bit more than might be expected, # but there is already a difference before rotation. (oblirot <- oblimin(loadings(ability.FA))) obli2 <- factanal(factors = 2, covmat = ability.cov, rotation="oblimin") max(abs(loadings(oblirot) - loadings(obli2))) # factanal(factors = 2, covmat = ability.cov, scores = Bartlett, rotation="oblimin") tst <- t(matrix(c( 0.3863637969729337152, 0.4745113977203344047, -0.0110032278171669998, 0.6458708261423832253, -0.0262888675561207576, 0.8961123879025085781, -0.0180180060207963122, 0.4882918937716873575, 0.9900948712271664398, -0.0370729040114848238, 0.7905663749272058283, 0.0526099352008769991 ), 2, 6)) if( fuzz < max(abs(loadings(oblirot) - tst ))) { cat("Calculated value is not the same as test value in test 2. Value:\n") print(loadings(oblirot), digits=18) cat("difference:\n") print(loadings(oblirot) - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/print-GPArotation.R0000644000176200001440000000412614335443420017254 0ustar liggesusers# testing that the print.GPArotation output is identical # for 2 runs of quartimin rotation, that have 2 # different looking loadings matrices wrt sign and order # the print.GPArotation should look identical Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") athl <- matrix(c( .73, -.07, .50, .82, -.01, .27, .77, -.46, -.22, .78, .17, .03, .77, .41, .13, .81, -.01, .27, .71, -.45, -.30, .82, .12, -.11, .66, -.15, -.45, .39, .76, -.40), byrow=T, ncol =3) ## z1 gives the results that have the right ordering and sign of the factors ## z2 is a random other order and sign set.seed(238) z1 <- quartimin(athl, Tmat = Random.Start(3)) head(z1$loadings) set.seed(46) z2 <- quartimin(athl, Tmat = Random.Start(3)) head(z2$loadings) #> z1 #Oblique rotation method Quartimin converged. #Loadings: # [,1] [,2] [,3] # [1,] 0.9451 -0.0535 -0.18033 # [2,] 0.7725 0.1431 0.01187 # [3,] 0.1323 0.8617 -0.12847 # [4,] 0.5377 0.2050 0.28753 # [5,] 0.6888 -0.0555 0.44072 # [6,] 0.7665 0.1386 0.00987 # [7,] 0.0150 0.8967 -0.08931 # [8,] 0.4047 0.3792 0.32647 # [9,] -0.1056 0.7915 0.24071 #[10,] -0.0155 -0.0165 0.94994 # # [,1] [,2] [,3] #SS loadings 3.034 2.405 1.401 #Proportion Var 0.303 0.240 0.140 #Cumulative Var 0.303 0.544 0.684 # #Phi: # [,1] [,2] [,3] #[1,] 1.000 0.554 0.259 #[2,] 0.554 1.000 0.186 #[3,] 0.259 0.186 1.000 #> z2 #Oblique rotation method Quartimin converged. #Loadings: # [,1] [,2] [,3] # [1,] 0.9451 -0.0535 -0.18033 # [2,] 0.7725 0.1431 0.01187 # [3,] 0.1323 0.8617 -0.12847 # [4,] 0.5377 0.2050 0.28753 # [5,] 0.6888 -0.0555 0.44072 # [6,] 0.7665 0.1386 0.00987 # [7,] 0.0150 0.8967 -0.08930 # [8,] 0.4047 0.3792 0.32647 # [9,] -0.1056 0.7915 0.24071 #[10,] -0.0155 -0.0165 0.94994 # # [,1] [,2] [,3] #SS loadings 3.034 2.405 1.401 #Proportion Var 0.303 0.240 0.140 #Cumulative Var 0.303 0.544 0.684 # #Phi: # [,1] [,2] [,3] #[1,] 1.000 0.554 0.259 #[2,] 0.554 1.000 0.186 #[3,] 0.259 0.186 1.000 GPArotation/tests/Thurstone.R0000644000176200001440000003353114323662112015726 0ustar liggesusers#Example from: Gradient Projection Algorithms and Software for # Arbitrary Rotation Criteria in Factor Analysis. # by Coen A. Bernaards and Robert I. Jennrich # Website: http://www.stat.ucla.edu/research Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() data("Thurstone", package="GPArotation") if (!exists("box20")) stop("Test data not found. Testing stopped.") fuzz <- 1e-5 all.ok <- TRUE # Thurstone's box problem. (1947, p. 136) # The matrix box20 is the initial loading matrix from Thurstone's box problem. # This takes a lot of iterations to converge at a higher tolerance qbox20 <- quartimax(box20, eps=1e-5) qbox20G <- GPForth(box20, Tmat=diag(1,3), method="quartimax", eps=1e-5) if( fuzz < max(abs(qbox20$loadings - qbox20G$loadings))) { cat("Calculated value is not the same as test value in test Thurstone 1. Value:\n") print(qbox20$loadings - qbox20G$loadings, digits=18) cat("difference:\n") print(qbox20$loadings - tst, digits=18) all.ok <- FALSE } #qbox20$Th - qbox20G$Th # These values compare with those in: # http://www.stat.ucla.edu/research/web.pdf tst <- t(matrix(c( 0.0104916072210123716, -0.993396087928394733, -0.089861775335686706, 0.1584646383898045685, -0.167305085570175344, -0.967087879524061056, 0.9822741057703969769, -0.094961339079248266, -0.081938545344928893, 0.1249962020162782989, -0.597065497283680413, -0.789290657131387352, 0.8695614167874907707, -0.471622450093366785, -0.090438968384549553, 0.8757114893176747294, -0.141012080768234127, -0.452333925937943637, 0.0679423211019681700, -0.811411071716238719, -0.588554936857709099, 0.4066768108416509708, -0.907862149146695163, -0.115673202040957226, 0.5770808894249742638, -0.142370726163931066, -0.806527261406603468, 0.1012712863762783577, -0.723336747696182614, -0.694640249329106285, 0.5000928657774492692, -0.949746569049947253, -0.046846346456817907, 0.7412589798326677526, -0.140350561965914555, -0.663578062154924320, 0.0055655501003109590, -0.983847100401775698, -0.120037109608235590, 0.2142330103903098415, -0.119429100752156334, -0.947421187831809397, 0.9550804066106526324, -0.108275659756619305, -0.039227521113362487, 0.7823218737697450464, -0.405437596810190704, -0.439275358874331168, 0.3626971102221024923, -0.753122462957226402, -0.546281394544768872, 0.0162483298780003657, -0.966230359337758582, -0.052114148464710915, 0.1076692386876715729, -0.206734953950642314, -0.934620775424686911, 0.9744239420161749932, -0.092650552854598708, -0.090828719474599584 ), 3, 20)) if( fuzz < max(abs(qbox20$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 2. Value:\n") print(qbox20$loadings, digits=18) cat("difference:\n") print(qbox20$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 0.57232345894276127, -0.60751194947821441, -0.55079496147384377, 0.60249460283341838, 0.76716797198365361, -0.22012168525406509, 0.55627880770383020, -0.20587018726291534, 0.80509089803322043 ), 3, 3)) if( fuzz < max(abs(qbox20$Th - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 3. Value:\n") print(qbox20$Th, digits=18) cat("difference:\n") print(qbox20$Th - tst, digits=18) all.ok <- FALSE } # sorted absolute loading plots. sal <- abs(c(loadings(qbox20)))[order(abs(c(loadings(qbox20))))] plot(seq(length(sal)), sal) #compare quartimax rotation of the initial loading matrix box20. if( fuzz < max(abs(loadings(qbox20) - box20 %*% qbox20$Th ))) { cat("Calculated value is not the same as test value in test Thurstone 4. Value:\n") print(loadings(qbox20), digits=18) cat("difference:\n") print(loadings(qbox20) - box20 %*% qbox20$Th, digits=18) all.ok <- FALSE } qminbox20G <- GPFoblq(box20, Tmat=diag(1,3), method="quartimin", eps=1e-5) qminbox20 <- quartimin(box20, eps=1e-5) if( fuzz < max(abs(loadings(qminbox20) - qminbox20G$loadings))) { cat("Calculated value is not the same as test value in test Thurstone 5. Value:\n") print(qminbox20G$loadings , digits=18) cat("difference:\n") print(loadings(qminbox20) - qminbox20G$loadings, digits=18) all.ok <- FALSE } #qminbox20$Th - quartimin(box20)$Th # These values compare with those in: # http://www.stat.ucla.edu/research/web.pdf tst <- t(matrix(c( -0.099561899210599963, -1.0236437309424475384, 0.017110338313848200, -0.007103778102200991, 0.0427848301281630802, -1.009962780073245581, 1.012864497258948226, 0.0331727792925069487, 0.050367710973030555, -0.054843850612513692, -0.4493155290974688021, -0.772334543778026350, 0.856287122381722998, -0.3740197232441037078, 0.069350368268248391, 0.835580575619599641, 0.0487450425576793633, -0.360381644212301344, -0.102893671454670210, -0.7226715938020771279, -0.537456650126404090, 0.322103633211960838, -0.8816846447967544576, 0.031159743715387874, 0.462799683447739529, 0.0852338438217692257, -0.783762970578423479, -0.076585435689138226, -0.6043060025891554554, -0.658295846696152820, 0.427772530893690217, -0.9288687512327726825, 0.122866182561916254, 0.659408232467282085, 0.0772080094990600374, -0.607348040513722709, -0.108761719100651882, -1.0079608432113262850, -0.017378089000366713, 0.059518597564186392, 0.0955950614351480238, -0.986779686330629513, 0.989890866913205381, 0.0071520817823045348, 0.094691644950703049, 0.713733277219835149, -0.2427293600063723522, -0.328268187306521242, 0.220344503737931546, -0.6353746612195683152, -0.459661643730432223, -0.084703580704062989, -1.0022284232457450148, 0.055740317456252478, -0.059151779416785115, -0.0113377397453605679, -0.976867596293413132, 1.003360458549731771, 0.0365098037316876067, 0.039427150580815938 ), 3, 20)) if( fuzz < max(abs(qminbox20G$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 6. Value:\n") print(qminbox20G$loadings, digits=18) cat("difference:\n") print(qminbox20G$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 1.00000000000000000, -0.25676300454795098, -0.32155119431295237, -0.25676300454795098, 1.00000000000000000, 0.33656790396842257, -0.32155119431295237, 0.33656790396842257, 1.00000000000000000 ), 3, 3)) if( fuzz < max(abs(qminbox20G$Phi - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 7. Value:\n") print(qminbox20G$Phi, digits=18) cat("difference:\n") print(qminbox20G$Phi - tst, digits=18) all.ok <- FALSE } #To fuzz precision the rotated loading matrix and the factor cor- #relation matrix phi are identical to those produced using the oblique GP #algorithm with exact derivatives. if( fuzz < max(abs(qminbox20G$Phi - t(qminbox20G$Th )%*% qminbox20G$Th ))) { cat("Calculated value is not the same as test value in test Thurstone 8. Value:\n") print(qminbox20G$Phi, digits=18) cat("difference:\n") print(qminbox20G$Phi - t(qminbox20G$Th )%*% qminbox20G$Th, digits=18) all.ok <- FALSE } #compare quartimin rotation of the initial loading matrix box20. if( fuzz < max(abs(qminbox20G$loadings - box20 %*% solve(t(qminbox20G$Th))))) { cat("Calculated value is not the same as test value in test Thurstone 9. Value:\n") print(qminbox20G$loadings, digits=18) cat("difference:\n") print(qminbox20G$loadings - box20 %*% solve(t(qminbox20G$Th)), digits=18) all.ok <- FALSE } data("box26", package="GPArotation") if (!exists("box26")) stop("Test data box26 not found. Testing stopped.") qbox26 <- GPForth(box26, Tmat=diag(1,3), method="quartimax", eps=1e-5) tst <- t(matrix(c( 0.6245197355925140581, -0.2708954695931116152, 0.7151983951389878635, 0.7386116884036847408, 0.6266342260884526505, -0.0617439911892987553, 0.7803093788467402314, -0.3830982859243221017, -0.4578886072022986253, 0.8540550453155928423, 0.2886436985992582027, 0.4062915145925659610, 0.8810593765418006651, -0.4428658074662961130, 0.1233946983666596581, 0.9084731768740617053, 0.1540526132602804965, -0.3723026715563940159, 0.8150592858039771293, 0.0441965358534676597, 0.5600768044145943980, 0.8466584455973064083, 0.4551177395514792168, 0.1889929089788950356, 0.8156808837280125069, -0.4090629943132625956, 0.3690652552112651530, 0.9629492340906220527, -0.4781483041690369196, -0.0866081507974762743, 0.8731366884896356595, 0.3451069860590937899, -0.2914969834947889749, 0.8921854600753849063, -0.0276323108621970258, -0.4257376659710629951, -0.0938760381595044741, -0.7873218033841372643, 0.6012450975895150540, 0.0938760381595044741, 0.7873218033841372643, -0.6012450975895150540, -0.0986092863860908303, 0.1513605567468480073, 0.9692559984337008050, 0.0986092863860908303, -0.1513605567468480073, -0.9692559984337008050, -0.0189573629854957251, 0.9527983290277913797, 0.2944078167958268377, 0.0189573629854957251, -0.9527983290277913797, -0.2944078167958268377, 0.8394181189595459891, 0.3631767908642606346, 0.3398717995655929913, 0.8703065201362156778, -0.4691145408161159214, 0.0770980453920554615, 0.9141063746617547059, 0.1583184861345137973, -0.3535658252020681958, 0.8348118627305495254, 0.3535663452183119837, 0.3271666140872500073, 0.8541352373790773722, -0.4476738735312740247, 0.0569042988261160704, 0.9034738474019414767, 0.1663655738425987851, -0.3227406124130587362, 0.9861758757457432800, 0.0103496363116840455, 0.0635926656567585569, 0.9643516568468981642, 0.0660181478622221818, -0.0304218028637989850 ), 3, 26)) if( fuzz < max(abs(qbox26$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 10. Value:\n") print(qbox26$loadings, digits=18) cat("difference:\n") print(qbox26$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 0.9996572020207266096, 0.0216275672176080257, 0.0147555679097727491, -0.0158190757965277796, 0.9480178905874908635, -0.3178235925273457108, -0.0208622934749700742, 0.3174812237948764770, 0.9480350400953921897 ), 3, 3)) if( fuzz < max(abs(qbox26$Th - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 11. Value:\n") print(qbox26$Th, digits=18) cat("difference:\n") print(qbox26$Th - tst, digits=18) all.ok <- FALSE } qminbox26 <- GPFoblq(box26, Tmat=diag(1,3), method="quartimin", eps=1e-5) tst <- t(matrix(c( 0.6088436426802223966, -0.2567107018725688361, 0.7213648290819488773, 0.7318447535507376367, 0.6298398026581654152, -0.0549983771960348838, 0.7973321695017724364, -0.3855960314746548212, -0.4504478973568259437, 0.8392144987741166906, 0.2994932968625432235, 0.4143558581243267924, 0.8833452352200144020, -0.4361046712803113290, 0.1319331147095905710, 0.9161366872228343672, 0.1535557844336666034, -0.3638337328539109072, 0.7993355454002614158, 0.0571270784641514026, 0.5678963531379384033, 0.8354288250614068101, 0.4626764152757318893, 0.1968789749765105790, 0.8109923806202916641, -0.3989909333845649830, 0.3770226870580207779, 0.9712737747877250305, -0.4740722765307348041, -0.0773243882106463137, 0.8761501947960563808, 0.3456235893514668089, -0.2834183138879167174, 0.9036601763684347643, -0.0290211959776035672, -0.4173652812159966974, -0.0995525797764766768, -0.7788574612781464790, 0.6007791331268093060, 0.0995525797764766768, 0.7788574612781464790, -0.6007791331268093060, -0.1264036712449473909, 0.1653130238928011975, 0.9684661160120416890, 0.1264036712449473909, -0.1653130238928011975, -0.9684661160120416890, -0.0392946742598458687, 0.9571059478962877787, 0.2939285303852590125, 0.0392946742598458687, -0.9571059478962877787, -0.2939285303852590125, 0.8253744379910458173, 0.3729516010405902748, 0.3477554718030251846, 0.8741734789142978634, -0.4631063486451737488, 0.0855349365396926159, 0.9212130243051569467, 0.1581334796580046720, -0.3450412531516501846, 0.8212340853954427367, 0.3631252613622908965, 0.3350076577679809153, 0.8582635618771776720, -0.4420579024138228674, 0.0651757040961165046, 0.9096561314838297330, 0.1665824736284239604, -0.3143133889989875307, 0.9840845767693481294, 0.0168070160966761091, 0.0729425956763933708, 0.9640420478016114014, 0.0709475796833391181, -0.0213192081807395371 ), 3, 26)) if( fuzz < max(abs(qminbox26$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 12. Value:\n") print(qminbox26$loadings, digits=18) cat("difference:\n") print(qminbox26$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 1.000000000000000 , 0.00767934084449363279, 0.0170654511973979163, 0.00767934084449363279, 1.000000000000000 , -0.0144994900961642244, 0.01706545119739791630, -0.01449949009616422445, 1.000000000000000 ), 3, 3)) if( fuzz < max(abs(qminbox26$Phi - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 13. Value:\n") print(qminbox26$Phi, digits=18) cat("difference:\n") print(qminbox26$Phi - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 0.9993401424148040668, 0.0347479564402226465, 0.0408645923859655008, -0.0179660947915933414, 0.9476477730670300748, -0.3324117322929439067, -0.0315673755054017430, 0.3174212937474846785, 0.9422486536594960604 ), 3, 3)) if( fuzz < max(abs(qminbox26$Th - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 14. Value:\n") print(qminbox26$Th, digits=18) cat("difference:\n") print(qminbox26$Th - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/Jennrich2002.R0000644000176200001440000001062514337257176016015 0ustar liggesusers# test by William Revelle # from Jennrich, Psychometrika, 2002, solution for the Thurstone 20 box problem. # Specifying 27 elements to be 0 as discussed in that article (Table 1 at # page 12) and using vgQ.target as revised or vgQ.pst with a W matrix # and Target as specified does not yield the reported solution. # The solution is almost identical for the high loadings but differs slightly # for the small loadings. The two models have a factor congruence of .99 for # all three factors, but do not agree completely. # Jennrich (2002) apparently was using the oblique rotation option. # When running TargetQ the results are fine, or when running # the vgQ.pst function with GPFoblq. # This a good test case for both TargetQ # (It could also be adapted for pst but there is already a test for it.) require("GPArotation") data(Thurstone) #the 20 box problem #solution reported in Jennrich 2002 browne <- t(matrix(c( 0.013, 0.994, 0.007, 0.991, 0.012, 0.001, 0.018, 0.003, 0.986, 0.772, 0.477, 0.002, 0.003, 0.393, 0.874, 0.409, 0.003, 0.816, 0.548, 0.730, -0.020, 0.023, 0.870, 0.405, 0.799, -0.024, 0.453, 0.664, 0.621, -0.005, -0.058, 0.915, 0.512, 0.639, -0.018, 0.644, 0.046, 0.980, -0.003, 0.971, -0.038, 0.060, -0.026, 0.025, 0.965, 0.380, 0.281, 0.726, 0.490, 0.652, 0.286, -0.025, 0.971, 0.019, 0.957, 0.061, -0.045, 0.028, 0.000, 0.976), 3,20,dimnames = list(c("B1", "B2", "B3"), NULL))) #a simplified target matrix, with NAs for ? and 0 for 0s. # (compare to pst appproach) Target <- t(matrix(c( 0, NA, 0, NA, 0, 0, 0, 0, NA, NA, NA, 0, 0, NA, NA, NA, 0, NA, NA, NA, 0, 0, NA, NA, NA, 0, NA, NA, NA, 0, 0, NA, NA, NA, 0, NA, 0, NA, 0, NA, 0, 0, 0, 0, NA, NA, NA, NA, NA, NA, NA, 0, NA, 0, NA, 0, 0, 0, 0, NA), 3, 20, dimnames = list(c("T1", "T2", "T3"), NULL))) v <- targetQ(box20,Target=Target)$loadings # THIS ONE WORKS #v <- GPFoblq(box20, method ="target", methodArgs = list(Target=Target))$loadings all.ok <- TRUE #slightly larger fuzz for comparison with published value. # note max(abs(v) - abs(browne))rather than max(abs(v - browne)) # as sign change is possible if( 10e-4 < max(abs(v) - abs(browne))) { cat("Calculated value is not the same as test value in Jennrich2002. Value:\n") print(v, digits=18) cat("difference:\n") print(v - browne, digits=18) all.ok <- FALSE } good <- t(matrix(c( 0.01324194563970146343, -0.99360765277094842407, 0.007265459960371034587, 0.99121314541487770544, -0.01178320700232154961, 0.000654586020267855506, 0.01798447315534307256, -0.00266076852016330911, 0.985581004768931734361, 0.77198435084052174915, -0.47723548341238952730, 0.001547735983967568618, 0.00334198654247502835, -0.39290416948063611180, 0.874043793719835537814, 0.40934347835281348349, -0.00274610551094590233, 0.815649888720176186041, 0.54757055519984310088, -0.72951044925148011977, -0.020211353947714422175, 0.02292379053779741716, -0.87011712730189194609, 0.404542252780873523577, 0.79911058029224457666, 0.02416810475294199623, 0.452727043944761764482, 0.66393502364020362538, -0.62149665012300570055, -0.005186928343372421146, -0.05839790682548451350, -0.91517931889838155524, 0.511521949806932663130, 0.63924406199386740735, 0.01841750353525576159, 0.643544196342115570886, 0.04597086497418309547, -0.97980801598321454193, -0.002918643110053173451, 0.97103389549392915558, 0.03847065084578840666, 0.060066450372699808913, -0.02622776344285615568, -0.02482060086975104718, 0.965272709232911085842, 0.37998105522582992233, -0.28073835673932595602, 0.726047993725112084107, 0.48985182554738604388, -0.65226812910595410866, 0.285738966726349907788, -0.02451057644240206557, -0.97122042802717223342, 0.019132901654980147277, 0.95708220223038309449, -0.06086293722346142188, -0.045050942196376064786, 0.02797903728304645954, 0.00036458752733534161, 0.976083771686937051726), 3,20,dimnames = list(c("B1", "B2", "B3"), NULL))) #tighter fuzz for numerical comparison with previous test value if( 10e-12 < max(abs(v - good))) { cat("Calculated value is not the same as previous test value. Value:\n") print(v, digits=18) cat("difference:\n") print(v - good, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/rotations.R0000644000176200001440000005467014340513035015762 0ustar liggesusers# Tests here only compare against values computed previously with this code, # to ensure there was no accidental change. It would be better to have # comparisons with known correct values. # Test for oblimax is commented out as it appears to be unstable. Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") fuzz <- 1e-6 all.ok <- TRUE data(ability.cov) L <- loadings(factanal(factors = 2, covmat=ability.cov)) if( 0.001 < max(abs(varimax(L, normalize=FALSE)$loadings - Varimax(L, normalize=FALSE)$loadings))) { cat("Calculated difference exceeds tolerance\n") cat("difference:\n") print(varimax(L, normalize=FALSE)$loadings - Varimax(L, normalize=FALSE)$loadings, digits=18) all.ok <- FALSE } if( 0.01 < max(abs(varimax(L, normalize=TRUE)$loadings - Varimax(L, normalize=TRUE, eps=1e-5)$loadings))) { cat("Calculated difference exceeds tolerance\n") cat("difference:\n") print(varimax(L, normalize=TRUE)$loadings - Varimax(L, normalize=TRUE, eps=1e-5)$loadings, digits=18) all.ok <- FALSE } v <- oblimin(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- quartimin(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 2. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- targetT(L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), eps=1e-5)$loadings tst <- t(matrix(c( 0.551529228817982942, 0.4905002767031292898, 0.217748645523411000, 0.6027046291262584399, 0.291173432863349457, 0.8348885228488550636, 0.154994397662456290, 0.4544843569140373241, 0.969702339393929247, 0.0850652965070581996, 0.803390575440818822, 0.1448091121037717866 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 3. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- targetQ(L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), eps=1e-5)$loadings tst <- t(matrix(c( 0.735795682866631218, 0.565351705145453853, 0.433590223819374398, 0.664644550038417159, 0.589924557708411568, 0.920006940799857786, 0.317543426981046928, 0.500590650032113116, 1.021758247914384077, 0.155121528590726393, 0.872521244896209747, 0.208735706420634437 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 4. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # Does not converge even with maxit=10000, but the loadings matrix is not # changing. Possibly the gradient is extremely large even very close to opt. v <- pstT(L, W = matrix(c(rep(.4,6),rep(.6,6)), 6,2), Target= matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), maxit=1000, eps=1e-5)$loadings tst <- t(matrix(c( 0.37067889993474656407, 0.638257130653133720, 0.01855112570739854416, 0.640564749523800270, 0.01576132191496706567, 0.884065831441111172, 0.00524531003824213384, 0.480158078874985073, 0.89458633399812259590, 0.383762977265515448, 0.71793428958051475064, 0.388556883222951677 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 5. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # Does not converge even with maxit=10000, but the loadings matrix is not # changing. Possibly the gradient is extremely large even very close to opt. v <- pstQ(L, W = matrix(c(rep(.4,6),rep(.6,6)), 6,2), Target= matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), maxit=1000, eps=1e-5)$loadings tst <- t(matrix(c( 0.573125161748393785, 0.700868331877288475, 0.214899397066479453, 0.681727425525818886, 0.286558275327103040, 0.940272379393286339, 0.152257795885557295, 0.510481967637567036, 1.029289798076480578, 0.462598702071116141, 0.850691132520651205, 0.456859727346562328 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 6. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # oblimax # this is test value on one computer # tst <- t(matrix(c( # -8111059.94622692652, 8111060.62253121007, # 1495036.43465861562, -1495035.79614594672, # 2331634.63904705830, -2331633.75893370388, # 1356735.91680212389, -1356735.43916810025, # -23187491.19758165255, 23187491.68068471923, # -18357040.58573083207, 18357041.05348757654 # ), 2, 6)) # # this is test value on another computer # tst <- t(matrix(c( # 2694770.06630349346, -2694769.38999920478, # -496701.45733913727, 496702.09585180727, # -774647.63529061736, 774648.51540397422, # -450753.43529273639, 450753.91292676108, # 7703672.48495316971, -7703672.00185009185, # 6098832.71036116872, -6098832.24260441773 # ), 2, 6)) # # this does not converge on all platforms and has large differences possible a mistake ??? # v <- oblimax(L, eps=1e-5)$loadings # if( fuzz < max(abs(v - tst))) { # cat("Calculated value is not the same as test value in test rotations 7. Value:\n") # print(v, digits=18) # cat("difference:\n") # print(v - tst, digits=18) # all.ok <- FALSE # } v <- entropy(L, maxit=3000, eps=1e-5)$loadings tst <- t(matrix(c( 0.528292107548243184, 0.515443945340967824, 0.189686511729033253, 0.612116304198454975, 0.252311894464850861, 0.847442931117894815, 0.133843268148035738, 0.461156452364903380, 0.964740133927989407, 0.129750551769587635, 0.795847094000000532, 0.181751199795689433 ), 2, 6)) if( 0.01 < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 8. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- quartimax(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.534714740804540178, 0.508778102568043678, 0.197348140750149392, 0.609689309353509956, 0.262919828098457153, 0.844212045390758559, 0.139616102327241837, 0.459441658926639795, 0.966291466215733252, 0.117641548844535412, 0.798063848020893585, 0.171756193883937508 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 9. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- Varimax(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.515866523962843160, 0.527879475961036904, 0.175054634278874244, 0.616460231981747930, 0.232057748479543163, 0.853211588623112749, 0.122822468397975171, 0.464213243286899446, 0.961376376417989453, 0.152689863976982837, 0.791292800869773050, 0.200653429940987366 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 10. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- simplimax(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.3384175759313114429, 0.508414890494446547464, -0.0654601124161610648, 0.670992229004664153535, -0.1016231721735353366, 0.930535379393095940515, -0.0589933707274080121, 0.506904360351960181497, 0.9733094402675376289, 0.000234046050254643859, 0.7702037184085044341, 0.085651123319384916965 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 11. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bentlerT(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.523583611303327312, 0.520226117818945788, 0.184113022124463677, 0.613815719643687197, 0.244596116053327067, 0.849702038129718673, 0.129644684715025493, 0.462354355134084738, 0.963520501269179652, 0.138517057902201340, 0.794161628656258278, 0.188979901644201559 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 12. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bentlerQ(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3801726240258240241, 0.4741208368044214638, -0.0223632969057368826, 0.6514196922540864687, -0.0421105927111659756, 0.9039359851665277334, -0.0266594447192576613, 0.4925968005718689424, 0.9961524457620027917, -0.0485973498906049697, 0.7939648477384558811, 0.0440983921679098251 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 13. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- tandemI(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.615424480780047745, 0.4074649925368262759, 0.300894306348887419, 0.5658002819054848143, 0.406455233467338028, 0.7852483408305571677, 0.217785179074990981, 0.4279590047675180808, 0.971977129465111611, -0.0530960591067626969, 0.815800376450207976, 0.0295946184147908228 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 14. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- tandemII(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.512160139332842212, 0.531476249107136312, 0.170736763115044710, 0.617670057812827134, 0.226081850628144149, 0.854814488884392154, 0.119571200821562001, 0.465061309851099225, 0.960284416460420398, 0.159413208985883820, 0.789869387186175276, 0.206185467095899383 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 15. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- geominT(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.572197044101002361, 0.4662247895688098054, 0.243573415560656120, 0.5927388411683653935, 0.326956608263186954, 0.8215352639437966120, 0.174476792179181994, 0.4473668997335142894, 0.972471249855535680, 0.0431091626026945812, 0.808894688433769660, 0.1099794466209375043 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 16. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- geominQ(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.39672053553904490508, 0.4713295988080449250, 0.00424452688463150020, 0.6389466007374070555, -0.00510976786312981532, 0.8864521406378518265, -0.00646959173137159373, 0.4830101828530461994, 0.98709860078485589518, -0.0318959930081098297, 0.79011178369962709045, 0.0558689642678330683 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 17. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- cfT(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.534721263659975854, 0.508771247100584523, 0.197355957387199576, 0.609686779159006154, 0.262930651479430233, 0.844208674501022327, 0.139621992686633722, 0.459439868910532512, 0.966292974385164483, 0.117629160286744874, 0.798066049992627313, 0.171745962120156664 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 18. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- cfQ(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 19. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- infomaxT(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.495330443338021176, 0.547195361446864537, 0.151384273205308784, 0.622695868320644275, 0.199304253086364791, 0.861451466010626055, 0.105004533733904976, 0.468565194910632365, 0.954843809781045660, 0.189293503899924942, 0.783052579543945471, 0.230726576980168713 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 20. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- infomaxQ(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.39327554287862442894, 0.4693137508305071925, -0.00319802321222481794, 0.6422985517185823001, -0.01549245038490981718, 0.8912279460026399924, -0.01214605901641467763, 0.4856544522916727002, 0.99260028929193111491, -0.0433225495465055510, 0.79356458059567791530, 0.0471559021503157039 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 21. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- mccammon(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.4293472299617892007, 0.600363196582340275, 0.0790140496845253004, 0.635943490060206229, 0.0992523811009183854, 0.878618107277518656, 0.0506062164774049028, 0.477512622702450096, 0.9268544198491108776, 0.297488850382792269, 0.7514463663627769519, 0.318958389348199534 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ###### ADDED IN NOVEMBER 2022 FOR EQUAMAX, PARSIMAX, VARIMIN, OBLIMAX data(Thurstone) v <- equamax(box26, eps=1e-5)$loadings tst <- t(matrix(c( 0.511813618717971597, 0.1252460667724786814, 0.835031881099661200, 0.211275278125612587, 0.9469860693462274215, 0.024701038786419674, 0.923671387190205140, 0.1861505968810791833, -0.278366886980007111, 0.414270797796799317, 0.7243752493532077397, 0.530526346393166759, 0.927099794400001564, 0.1710560637343615797, 0.314400690653154735, 0.685509679739711331, 0.6873945075387188908, -0.212674093365320949, 0.500975325417812756, 0.4985944480056956341, 0.693100497576226382, 0.350251174602310256, 0.8631423492204841619, 0.303299191676876356, 0.809196181501955492, 0.1468111894018074293, 0.540855816747015439, 1.051940508364259674, 0.2023337382785123650, 0.126016765617061266, 0.528246625368315792, 0.8145581663496035407, -0.154555803579673606, 0.791784749686200273, 0.5353191515116044741, -0.254010464723911089, 0.283760830721282831, -0.7132278971933163625, 0.633221728633476699, -0.283760830721282831, 0.7132278971933163625, -0.633221728633476699, -0.351981708826951678, 0.0145585781278812498, 0.920862598031950474, 0.351981708826951678, -0.0145585781278812498, -0.920862598031950474, -0.641238077659381234, 0.7340358583767647715, 0.211813801195267382, 0.641238077659381234, -0.7340358583767647715, -0.211813801195267382, 0.370916272566192251, 0.7781992933002486179, 0.457012011497068604, 0.943267697340363864, 0.1458935486092693412, 0.269085717994103968, 0.683769139477491628, 0.6932804480935084168, -0.193612975261152009, 0.375506314902942506, 0.7683789003013250518, 0.444462454027040654, 0.921697465732450816, 0.1542330203892136042, 0.244709944799956780, 0.664806997738585315, 0.6918110118942031317, -0.165931249557543792, 0.748952844572093657, 0.5985308972371030656, 0.239842451746804020, 0.716556890444816297, 0.6343221919993241587, 0.139425892477791219 ), 3, 26)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ### SAME FOR CRAWFORD FERGUSON WITH KAPPA = m / (2 * p) = 3 / (2 * 26) v <- cfT(box26, kappa = (3 / (2 * 26)), eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- parsimax(box26, eps=1e-5)$loadings tst <- t(matrix(c( 0.7201835790622810318, -0.2820790149262949464, 0.6137467244615277817, -0.0679423851913938670, 0.6010788795762025405, 0.7590243822315081434, 0.6707172136894012926, 0.7174085874409354968, -0.0277909684381195121, 0.3564975652920873705, 0.2169149780725644350, 0.8964682806594965747, 0.8905375652375422391, 0.2961288407436278303, 0.3269136806262873951, 0.3419238671745732927, 0.8395902605853544642, 0.4072361273101923196, 0.5551801796495600128, 0.0181442676741417341, 0.8193941381745735164, 0.1791815177720131602, 0.4232257028261393605, 0.8651329309165379788, 0.8735154515073240145, 0.0707778916688399651, 0.4481498031114935499, 0.9254209439635597834, 0.5018907196720771013, 0.2347334274887991901, 0.1871266535649374341, 0.7975561080494565358, 0.5434380093797205324, 0.4642991413806251688, 0.8329607560592182658, 0.2619421428072832847, 0.6793187635833454197, -0.7070938474525871875, -0.1694942722875707464, -0.6793187635833454197, 0.7070938474525871875, 0.1694942722875707464, 0.0126413340577520572, -0.7959999181796318934, 0.5816488003350992475, -0.0126413340577520572, 0.7959999181796318934, -0.5816488003350992475, -0.7005089039174136056, -0.0349340740508546910, 0.7091733821870598309, 0.7005089039174136056, 0.0349340740508546910, -0.7091733821870598309, 0.2764675266718980007, 0.2782887989292237019, 0.8933946782281918519, 0.8957285854821546156, 0.3212930506383097073, 0.2790825626255922787, 0.3455615085575033385, 0.8287155760723180498, 0.4236528505493507568, 0.2788032457489866833, 0.2837361452757249936, 0.8779069142135194070, 0.8654448252950356357, 0.3331277242202841382, 0.2706070467039994321, 0.3390107603224090660, 0.7999236919074760310, 0.4396564471388281214, 0.5855440811683275681, 0.5029973433114328651, 0.6171108503586539840, 0.5106729412991620753, 0.5782224406112924653, 0.5832066153589001711 ), 3, 26)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ### SAME FOR CRAWFORD FERGUSON WITH KAPPA = (m - 1) / (p + m - 2) = (3 -1) / (26 + 3 - 2) v <- cfT(box26, kappa=( (3-1)/(26+3-2) ), eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } data(Harman, package= "GPArotation") v <- varimin(Harman8, eps=1e-5)$loadings tst <- t(matrix(c( 0.800626657046876855, -0.452452158825595752, 0.783606930490612252, -0.524447498313301397, 0.742635936060292656, -0.522609669324872517, 0.768357486963803682, -0.455227165519225097, 0.818696625686402668, 0.444445536696790211, 0.702064973637186673, 0.410429985249392060, 0.623283524595303340, 0.401857745935120247, 0.668480210595655877, 0.287458184858228272 ), 2, 8)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- oblimax(Harman8, eps=1e-5)$loadings tst <- t(matrix(c( 0.93395421734409445058, -0.0302013026726007383, 0.99243032312927881300, -0.1121899246869615951, 0.96509469978483286567, -0.1322258547171115683, 0.91647702431117861188, -0.0502569243958834178, 0.08441855308346873921, 0.8875309317276611765, 0.04427084251510177149, 0.7907585046311147448, 0.00332736511424391868, 0.7399752420126202157, 0.14133359391312094733, 0.6483050831171799366 ), 2, 8)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/KaiserNormalization.R0000644000176200001440000000726314524252342017726 0ustar liggesusers# tests using normalization # All tests below use Kaiser normalization # A few other tests also use normalization when comparing varimax and Varimax # Following examples are from SPSS # See https://psych.unl.edu/psycrs/statpage/pc_rot.pdf Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") fuzz <- 1e-3 #less strict; differences in 4rd decimal compared to SPSS all.ok <- TRUE # unrotated matrix L <- matrix(c(.758, .413, 1.164E-03, .693, .489, -.199, .362, .656, -.204, .826, 6.589E-02, .235, .540, -.510, .441, .654, -.335, .507, -.349, .539, .669, -.580, .450, .551), byrow=T, ncol=3) # quartimax, Kaiser normalization # uses the print command to get the right order of factors v <- print(quartimax(L, normalize = TRUE, eps = 1e-6))$loadings tst <- matrix(c(.814, .285, -4.99E-02, .856, 8.321E-02, -.135, .746, -.203, 7.244E-02, .576, .634, -8.73E-02, -6.10E-02, .850, -.142, .129, .882, -3.86E-02, 2.063E-02, -4.15E-02, .927, -.181, -.220, .873), byrow=T, ncol=3) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # oblimin, Kaiser normalization # Pattern Matrix vw <- print(oblimin(L, normalize = TRUE, eps = 1e-7)) v <- vw$loadings tst <- matrix(c(.241, .787, -1.36E-02, 1.783E-02, .848, -.119, -.240, .779, 6.824E-02, .608, .507, -2.52E-02, .858, -.163, -7.26E-02, .896, 3.050E-02, 3.954E-02, 9.405E-02, 7.397E-02, .949, -8.61E-02, -.113, .875), byrow=T, ncol=3) tst <- tst %*% matrix(c(0,1,0,1,0,0,0,0,1), 3) # Needed to line up factors correctly fuzz <- 3e-3 #less strict; differences in 4th decimal compared to SPSS; 0.003 or smaller diff if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # oblimin, Kaiser normalization # Structure Matrix v <- vw$loadings %*% vw$Phi tst <- matrix(c(.379, .829, -.146, .191, .862, -.203, -.123, .731, .051, .701, .613, -.218, .847, -.010, -.261, .891, .180, -.176, -.118, .000, .919, -.313, -.211, .906), byrow=T, ncol=3) tst <- tst %*% matrix(c(0,1,0,1,0,0,0,0,1), 3) # Needed to line up factors correctly fuzz <- 4e-3 #less strict; differences in 4th decimal compared to SPSS; 0.004 or smaller diff if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ################################################################# # # Confirmation that a row of zeroes will not break the normalization function # Normalizing with a column of zeroes was not affected # based on example from Kim-Laura Speck (25 October 2023) # Only affects Normalize=TRUE settings fuzz <- 1e-6 D <- matrix(c(0,0,0, 1,2,3, 2,3,4, 5,2,5, 1,2,1, 3,4,5),ncol=3,byrow=T) set.seed(1000) #set seed becasuse some variance is observed in converged values v <- geominQ(D, normalize = TRUE, maxit = 10000)$loadings tst <- matrix(c( 0.00000000, 0.00000000, 0.00000000, -0.36979732, -0.13603325, 3.99622380, 0.03102554, 0.76678245, 4.68896063, 3.28926158, 0.01317821, 5.35447764, -0.02755956, 2.40311582, 0.06247234, 0.43184841, 1.66959816, 5.38169746), ncol = 3, byrow = TRUE) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } GPArotation/vignettes/0000755000176200001440000000000014557733461014470 5ustar liggesusersGPArotation/vignettes/GPArotateDF.Stex0000644000176200001440000002402114404451600017353 0ustar liggesusers%\VignetteIndexEntry{Derivative-Free Gradient Projection: the GPArotateDF package} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{cubimax} %\VignetteKeyword{forced simple structure} \documentclass[english, 10pt]{article} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=FALSE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Derivative-Free Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotateDF} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \subsection*{Principle of derivative-Free Factor Rotation} The gradient projection algorithm package \emph{GPArotation} consists of wrapper functions and functions that compute the gradients, $G_q$, needed for minimization. For all functions included in the {\em GPArotation} package, the gradients are included in the \texttt{vgQ} routines. For example, the \texttt{vgQ.quartimax} function provides the gradients $G_q$ for quartimax rotation. Examples of gradient derivations, computations are provided in \cite{gpa.1} and \cite{gpa.2}, as well as \cite{gpa.rotate}. However, the derivation of the gradient can be quite involved for complex rotation criteria. In such cases, a derivative free version of gradient projection algorithm can be used for minimization of the criterion function, {\em without} the need for a gradient. Details of the methods are described in \cite{gpa.df}. The method is implemented in the package \emph{GPArotateDF} that may be downloaded and installed. To perform derivative-free rotation, the main algorithms \texttt{GPForth.df} and \texttt{GPFoblq.df} are available for orthogonal and oblique rotation, respectively. Both are minimization algorithms. The algorithms differ from the regular algorithms by the inclusion of the numerical derivates $G_f$ for the rotation criteria in \texttt{GPForth.df} and \texttt{GPFoblq.df}. The algorithms require: an initial loadings matrix \texttt{A} and a rotation method. Optional are initial rotation matrix \texttt{Tmat} (default is the identity matrix). Other arguments needed for individual rotations are applied in the same way as in the {\em GPArotation} package. The rotation method is provided between quotation marks, and refers to the name of the ff-function. For example, the \texttt{method = "varimax"} through \texttt{GPForth.df} calls the \texttt{ff.varimax} function. The ff-functions are the derivative-free analogues of the GPArotation vgQ functions. The output of \texttt{ff.varimax} is the rotation criteria value, \texttt{f}, and the \texttt{Method} name, e.g. \texttt{DF-Varimax}. New rotation functions need to be programmed as \texttt{ff.newmethod}. The only required input is an initial loadings matrix \texttt{A}, and any potential additional arguments. The output consist of the value \texttt{f} of the criterion, and the \texttt{Method} name (the \texttt{GPForth.df} and \texttt{GPFoblq.df} algorithms expect this included in the result). \subsection*{Derivative-free quartimax rotation} As an example, consider quartimax rotation. Gradient projection quartimax orthogonal rotation seeks to minimize the sum of all loadings raised to power 4. Thus, using the notation of \cite{gpa.rotate} (page 682), the criterion $f$ for minimization is calculated as \[ f = Q(\Lambda) = -\frac{1}{4}\sum_{i} \sum_{r} \lambda_{ir}^{4}. \] Derivative-free quartimax rotation using \texttt{ff.quartimax} is then very simple \begin{Scode} library(GPArotateDF) ff.quartimax<- function(L){ f = -sum(L^4) / 4 list(f = f, Method = "DF-Quartimax") } data(Harman, package="GPArotation") GPForth.df(Harman8, method="quartimax") \end{Scode} Of course, for quartimax, the gradient is easy to derive and regular rotation is a better choice. \subsection*{Rotation when the derivative is complicated: cubimax} Sometimes the gradient is hard to derive. For example, a criterion that seeks to minimize loadings to the power 3, the absolute value is needed for a meaningful result. \[ f = Q(\Lambda) = -\sum_{i} \sum_{r} | \lambda_{ir}^{3} |. \] While the gradient may be complicated, the derivative-free function for minimization is straightforward. \begin{Scode} ff.cubimax<- function(L){ f = -sum(abs(L^3)) list(f = f, Method = "DF-Cubimax") } GPForth.df(Harman8, method="cubimax") \end{Scode} Results differ from quartimax and varimax rotation \cite{mulaik} describes Absolmin, the sum of absolute factor loadings minimized. Minimizing the criterion is straightforward using the {\em GPArotateDF} package. \subsection*{Rotation when an algorithm is involved: Forced Simple Structure} In certain cases the derivate is so poorly defined that deriving the \texttt{vgQ} function is a non-starter. For example, an algorithm that updates a weight matrix that, when multiplied with the loadings matrix provides a rotation criterion to be minimized. The algorithm Forced Simple Structure chooses a weight matrix focused on the lowest loadings. The rotation criterion value $f$ is minimized representing a rotated factor pattern which many low loadings, restricted to each factor having at least some salient loadings. In each iteration, the weight matrix \texttt{Imat} gets weight 1 at the lowest factor loadings, and 0 elsewhere. Assume we have \texttt{p} items, and \texttt{m} factors (for a \texttt{p x m} loadings matrix). In each iteration, first the lowest loadings get weight 1. Next, for each pair \texttt{(i,j)} of factors, lowest loadings get weight 1 until there are at least \texttt{(m + kij)} items with weight 1 on a single factor \texttt{i} or \texttt{j} (but not the other factor), or not enough loadings are left to get weight 1. Possible values for \texttt{kij = (0, ..., [p - m] )} and defaults to 2. Forced Simple Structure is most effective when \texttt{kij} has a lower value. For each increase of 1, an additional \texttt{(m)} loadings get weight~1. The criterion \texttt{f} minimizes the squared loadings for low loadings (``non-salient''). Salient loadings are therefor increased as the sum of squared non-salient loadings is minimized. \begin{Scode} ff.fss <- function(L, kij=2){ m <- ncol(L) p <- nrow(L) zm <- m + kij Imat <- matrix(0, p, m) for (j in 1:m){ Imat[abs(L[,j]) <= sort(abs(L[,j]))[zm],j] <- 1 } for (i in 1:(m-1)){ for (j in (i+1):m){ nz <- sum( (Imat[,i] + Imat[,j]) ==1) while (nz < zm && sum(Imat[ ,c(i,j)]) < m * 2){ tbc <- c(abs(L[,i]), abs(L[,j])) tbcs <- sort(tbc [c(Imat[,i], Imat[,j])==0])[1] Imat[abs(L) == tbcs] <- 1 nz <- sum( (Imat[,i] + Imat[,j]) ==1) } } } Method <- paste("DF-Forced Simple Structure (kij = ",kij,")", sep="") f <- sum(Imat*L^2) list(f = f, Imat = Imat, Method = Method) } data(WansbeekMeijer, package = "GPArotation") z <- factanal(covmat = NetherlandsTV, factors = 3, rotation = "none") fssT.df(loadings(z), kij = 3) # which loadings get weight 1 in the first iteration? ff.fss(loadings(z), kij = 3)$Imat \end{Scode} The added \texttt{sum(Imat) < m * 2} requirement was added to avoid infinite looping. It is useful to consider random starts as the rotation tends to have many local minima. The method works both orthogonal and oblique. \subsection*{Examples of other ff-functions} Writing ff-functions is straightforward because only the criterion value is needed. Here are a few additional examples of ff-functions. For all vgQ-functions exist and are preferable to be used. The oblique rotation criterion for oblimax \begin{Scode} ff.oblimax <- function(L){ f <- -(log(sum(L^4))-2*log(sum(L^2))) list(f = f, Method = "DF-Oblimax") } \end{Scode} Entropy criterion for orthogonal rotation \begin{Scode} ff.entropy <- function(L){ f <- -sum(L^2 * log(L^2 + (L^2==0)))/2 list(f = f, Method = "DF-Entropy") } \end{Scode} Simplimax that works well in oblique rotation \begin{Scode} ff.simplimax <- function(L,k=nrow(L)){ # k: Number of close to zero loadings Imat <- sign(L^2 <= sort(L^2)[k]) f <- sum(Imat*L^2) list(f = f, Method = "DF-Simplimax") } \end{Scode} Target rotation. Requires both a weight matrix and a target matrix. Target rotation can be both orthogonal and oblique. \begin{Scode} ff.pst <- function(L,W,Target){ # Needs weight matrix W with 1's at specified values, 0 otherwise # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). # When W has only 1's this is procrustes rotation # Needs a Target matrix Target with hypothesized factor loadings. # e.g. Target = matrix(0,8,2) Btilde <- W * Target f <- sum((W*L-Btilde)^2) list(f = f, Method = "DF-PST") } \end{Scode} \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock doi: 10.1177/0013164404272507 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2001}]{gpa.1} Jennrich, R. I. (2002). \newblock A simple general procedure for orthogonal rotation. \newblock{\em Psychometrika}, 66(3), 289--306. \newblock doi: 10.1007/BF02294840 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2002}]{gpa.2} Jennrich, R. I. (2002). \newblock A simple general method for oblique rotation. \newblock{\em Psychometrika}, 67(3), 7--19. \newblock doi: 10.1007/BF02294706 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2004}]{gpa.df} Jennrich, R. I. (2004). \newblock Derivative free gradient projection algorithms for rotation. \newblock{\em Psychometrika}, 69(3), 475--480. \newblock doi: 10.1007/BF02295647 \bibitem[\protect\citeauthoryear{Jennrich}{Mulaik}{2010}]{mulaik} Mulaik, S.A. (2010). \newblock {\em Foundations of factor analysis} \newblock (2nd ed.). Chapman and Hall/CRC Press, Taylor \& Francis Group. \newblock doi: 10.1201/b15851 \end{thebibliography} \end{document}GPArotation/vignettes/GPAguide.Stex0000644000176200001440000002700214557733443016763 0ustar liggesusers%\VignetteIndexEntry{Gradient Projection Factor Rotation} %\VignettePackage{GPArotation} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{varimax} %\VignetteKeyword{oblimin} \documentclass[english, 10pt]{article} \usepackage{hyperref} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=TRUE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotation} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \section*{GPArotation Functions} In R, the functions in this package are made available with \begin{Scode} library("GPArotation") \end{Scode} The most complete reference for the software is: Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. Educational and Psychological Measurement. A mirror of the original repository that is referenced in the paper, with additional material is available here: \href{https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html} {https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html}. Rotations can be performed by providing an orthogonal matrix to the gradient projection function. Orthogonal matrix for rotation can be obtained by extracting an unrotated factor loadings matrix. A rotation is done by calling the rotation name directly, or by calling one of the wrapper functions \texttt{GPFRSorth} or \texttt{GPFRSoblq}, for orthogonal and oblique rotation, respectively. Under the hood, rotations are computed using the Gradient Projection Algorithm code, which can be called directly. The key functionality of the algorithm is included in the \texttt{GPForth} and \texttt{GPFoblq} functions for orthogonal and oblique rotation, respectively. Calling these functions directly works as it always has (the codes have not changed). The rotated loadings matrix is the pattern matrix. The structure matrix may be obtained using the \texttt{summary} command. \subsection*{GPArotation Functions with \texttt{factanal}} The {\em GPArotation} can be used in conjunction with the built-in R \texttt{factanal} function. It is recommended to rotate outside of \texttt{factanal}. \begin{Scode} data(ability.cov) z <- factanal(factors = 2, covmat = ability.cov, rotation = "none") # quartimax rotation GPFRSorth(loadings(z), method = "quartimax") quartimax(z$loadings) # oblimin rotation GPFRSoblq(z$loadings, method = "oblimin") oblimin(loadings(z)) \end{Scode} {\bf Important note}: \texttt{factanal} allows for calling a rotation directly from the \texttt{factanal} call. However, due to a \texttt{factanal} calculation error in the computation of the correlation matrix, the produced correlation matrix {\em may} be wrong for oblique rotation (orthogonal rotation are not affected). However, the correlation matrix \texttt{Phi} produced by {\em GPArotation} is the correct correlation matrix when performing rotation outside of \texttt{factanal}. \subsection*{Recovery of The Unrotated Loadings Matrix} Recovery of the unrotated loadings matrix is consistent with the definitions used in \cite{gpa.rotate} (page 678). For example, the unrotated matrix $A$ may be recovered as follows. \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none") y.quart <- quartimax(y$loadings) max( loadings(y.quart) %*% t(y.quart$Th) - loadings(y) ) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max( loadings(y.obli) %*% t(y.obli$Th) - loadings(y) ) # last equation on Page 678 max( loadings(y.obli) - loadings(y) %*% solve(t(y.obli$Th)) ) \end{Scode} By the same definitions logic, the factor correlation matrix is calculated as \cite{gpa.rotate} (page 695), \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none", randomStarts=15) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max(abs(y.obli$Phi - t(y.obli$Th) %*% y.obli$Th)) \end{Scode} \subsection*{Random Starts} If multiple random starts are desired then the \texttt{randomStarts} option may be utilized. For example, 100 random starts of the oblique infomax rotation, \begin{Scode} data(Thurstone, package = "GPArotation") infomaxQ(box26, randomStarts = 100) # 100 random starts infomaxQ(box26, Tmat=Random.Start(3)) # a single random start infomaxQ(box26, randomStarts = 1) # also a single random start \end{Scode} The loadings that are output have the lowest complexity value \texttt{f}. While the lowest local minimum may be the global minimum solution, technically it can not be guaranteed that the lowest local minimum is in fact the global minimum. To further investigate the local minima it is recommended to use the {\em fungible} package using the \texttt{faMain} function. When in doubt, trying random initial rotation matrix is advised. For a detailed discussion, consult \cite{nguwall}. Additional algorithmic considerations are in \cite{gpa.rotate} (page 680). \subsection*{An Example of Target Rotation} \cite{fisfon} describe measuring self-reported extra-role behavior in samples of British and East German employees. They publish rotation matrices for two samples, and investigate the structural equivalence of the loadings matrices. Additional context is available in the manuscript. Structural equivalence includes target rotation, as well as calculation of a number of agreement coefficients. The table lists the varimax rotated loadings matrices. Performing target rotation of one loadings matrix to the other can help in interpreting assessing equivalence. \begin{tabular}{l c c c c} \hline & \multicolumn{2}{c}{Britain} & \multicolumn{2}{c}{East Germany} \\ & Factor 1& Factor 2 & Factor 1& Factor 2\\ \hline\hline I am always punctual.&.783&-.163& .778 &-.066\\ I do not take extra breaks.&.811&.202&.875&.081\\ I follow work rules and instructions &.724&.209&.751&.079\\ ~~~ with extreme care.& & & & \\ I never take long lunches or breaks.&.850&.064&.739&.092\\ I search for causes for something .&-.031&.592&.195&.574\\ ~~~ that did not function properly.& & & & \\ I often motivate others to express &-.028&.723&-.030&.807\\\ ~~~ their ideas and opinions.& & & & \\ During the last year I changed &.388&.434&-.135&.717\\ ~~~ something. in my work.& & & & \\ I encourage others to speak up at meetings.&.141&.808&.125& .738\\ I continuously try to submit suggestions&.215&.709& .060&.691\\ ~~~ to improve my work.& & & & \\ \hline \end{tabular} \\ The varimax rotations for each of the samples may be expected to be similar because the two loadings matrices are from different samples measuring the same constructs. Below are target rotation of the East German loadings matrix towards the Britain one, followed by calculation of agreement coefficients. \cite{fisfon} note that coefficients generally should be ``beyond the commonly accepted value of 0.90.'' \begin{Scode} origdigits <- options("digits") options(digits = 2) trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) # orthogonal rotation of trGermany towards trBritain trx <- targetT(trGermany, Target = trBritain) # Factor loadings after target rotation trx # Differences between loadings matrices after rotation y <- trx$loadings - trBritain print(y, digits = 1) # Square Root of the mean squared difference per item sqrt(apply((y^2), 1, mean)) # Square Root of the mean squared difference per factor sqrt(apply((y^2), 2, mean)) # Identity coefficient per factor after rotation 2 * colSums(trx$loadings*trBritain)/( colSums(trx$loadings^2)+colSums(trBritain^2)) # Additivity coefficient per factor after rotation diag(2 * cov(trx$loadings, trBritain) ) / diag(var(trx$loadings)+var(trBritain)) # Proportionality coefficient per factor after rotation colSums(trBritain * trx$loadings)/sqrt(colSums(trBritain^2)*colSums(trx$loadings^2)) # Correlation for each factor per factor after rotation diag(cor(trBritain, trx$loadings)) options(digits = origdigits$digits) \end{Scode} \subsection*{An Example of Partially Specified Target Rotation } \cite{browne} reported an initial loadings matrix and a partially specified target to rotated towards. In {\em GPArotation} the partially specified target matrix is of the same dimension as the initial matrix \texttt{A}, and with \texttt{NA} in the matrix entries that are not pre-specified. Both procedures target rotation and partially specified target rotation can be used to reproduce \cite{browne} results. In this orthogonal rotation example, \texttt{targetT} includes a \texttt{Target} matrix with \texttt{NA} in entries not used in target rotation. With \texttt{pst} no missing values are present in the \texttt{Target} matrix, and the weight matrix \texttt{W} includes weight 0 for entries not used, and 1 for entries included in the rotation. \begin{Scode} A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) # using targetT SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) xt <- targetT(A, Target=SPA) # using pstT SPApst <- matrix(c(rep(0, 6), .7,.0,.7, rep(0,3), rep(0, 7), 0, 0, 0, 0, rep(0, 4)), ncol=3) SPAW <- matrix(c(rep(0, 6), rep(1, 6), rep(0, 7), 1, 1, 0, 1, rep(0, 4)), ncol=3) xpst <- pstT(A, Target = SPApst, W = SPAW) max(abs(loadings(xt)- loadings(xpst))) \end{Scode} Note that convergence tables are identical for both methods. Additional examples are available in the help pages of \texttt{GPFoblq} and \texttt{rotations}. \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock\href{https://doi.org/10.1177/0013164404272507}{https://doi.org/10.1177/0013164404272507} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Browne}{1972}]{browne} Browne, M.W. (1972). \newblock Orthogonal rotation to a partially specified target. \newblock\textit{British Journal of Mathematical and Statistical Psychology}, 25(1), 115--120. \newblock\href{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x}{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Fischer \& Fontaine}{2010}]{fisfon} Fischer, R., \& Fontaine, J. (2010). \newblock Methods for investigating structural equivalence. \newblock In D. Matsumoto, \& F. van de Vijver (Eds.), {\em Cross-Cultural Research Methods in Psychology} (179--215). \newblock Cambridge University press. \newblock \href{https://doi.org/10.1017/CBO9780511779381.010}{https://doi.org/10.1017/CBO9780511779381.010} \bibitem[\protect\citeauthoryear{Nguyen \& Waller}{Nguyen \& Waller}{2022}]{nguwall} Nguyen, H. V., \& Waller, N. G. (2022). \newblock Local minima and factor rotations in exploratory factor analysis. \newblock\textit{Psychological Methods}. Advance online publication. \newblock\href{https://doi.org/10.1037/met0000467}{https://doi.org/10.1037/met0000467} \end{thebibliography} \end{document} GPArotation/NEWS0000644000176200001440000001006414557725056013161 0ustar liggesusersKnown problems o Very occassionally (about 1 in 1000 in monte carlo experiments) the algorithm gets stuck and does not improve (so does not converge). The workaround is to restart with a very slightly perturbed starting point. Changes in GPArotation version 2024.2-1 o Improved Random.Start for orthonormal matrix generation o Added Bi-Geomin rotation criterion o Edits to the manual pages Changes in GPArotation version 2023.11-1 o Updated NormalizingWeight to handle cases with a row of zeroes Changes in GPArotation version 2023.8-1 o Added L to target rotation and pst to better handle backward compatibility. Changes in GPArotation version 2023.3-1 o January 2023: GPArotation turns 18 years old. o Included GPFRSorth and GPFRSoblq functions, and set rotation routines to call GPFRSorth instead of GPForth, GPFRSoblq instead of GPFoblq. o GPFRSorth and GPFRSoblq include randomStarts = 0 as default. o Included equamax, parsimax and varimin (added vgQ.varimin). o Cleaned up print.GPArotation to account for random starts. o Cleaned up vgQ.bifactor. o Expanded vignette GPAguide. o Added vignette on derivative-free GPA: the GPArotateDF package. Changes in GPArotation version 2022.10-1 o Changed maintainer from Paul Gilbert to Coen Bernaards Changes in GPArotation version 2022.4-1 o Switched URL from defunct http://:www.stat.ucla.edu/research/gpa to https://optimizer.r-forge.r-project.org/GPArotation_www/ o Added importFrom("stats", "rnorm") to NAMESPACE file. o Fixed Rbuildignore so that files from building vignettes are not omitted in the package build. Changes in GPArotation version 2015.7-1 o Added default package imports as now required by CRAN. Changes in GPArotation version 2014.11-1 o Minor format and cleanup required by CRAN checks, no real changes. Changes in GPArotation version 2012.3-1 o no real changes, but bumping version for new CRAN suitability check. Changes in GPArotation version 2011.11-1 o updated maintainer email address. Changes in GPArotation version 2011.10-1 o Modification to vgQ.target to allow NA in target, which is replaced by 0.0 (from William Revelle). o Added bifactorT and bifactorQ (biquartimin) from William Revelle. Changes in GPArotation version 2010.07-1 o Fix an error caused by an exact initial setting (from William Revelle). Changes in GPArotation version 2009.02-2 o Standardized NEWS format for new function news(). Changes in GPArotation version 2009.02-1 o minor documentation corrections as found by a new R-devel. Changes in GPArotation version 2008.05-1 o added echelon rotation. o added gradient Gq to result list from GPForth and GPFoblq. o change license from "GPL-2" to "GPL-2 or later". Changes in GPArotation version 2007.06-1 o fixed a couple of lingering $Lh (in print and summary methods) that should have been changed to $loadings. Changes in GPArotation version 2007.04-1 o removed an extra comma in c() that caused a test failure with R-2.5.0 o added eiv rotation. o renamed $Lh in the result from GPForth and GPFoblq to $loadings. As a result, rotation methods calling these function no longer need to rename this element in order to work with factanal and other programs. (It is a good idea to use the extractor function loadings() rather than refer directly to object structure.) o changed rotation method functions to return all elements of GPFoblq and GFForth. o fixed the documentation file primary alias for all rotations (which was being called oblimin). Changes in GPArotation version 2006.2-2 o extra argument (...) to invisible in print.GPArotation was removed. Changes in GPArotation version 2006.2-1 o broken references in documentation were fixed and updated. Changes in GPArotation version 2005.10-1 o warning message about non-convergence expanded to indicate function. Changes in GPArotation version 2005.4-1 o First released version. GPArotation/R/0000755000176200001440000000000014557733461012661 5ustar liggesusersGPArotation/R/printsummary.R0000644000176200001440000000563614557553777015602 0ustar liggesusers# summaries for GPArotation # S3 class functions # print.GPArotation # summary.GPArotation # print.summary.GPArotation print.GPArotation <- function (x, digits=3L, sortLoadings=TRUE, rotateMat=FALSE, Table=FALSE, ...){ cln <- colnames(x$loadings) # Based on Fungible faSort and orderFactors ifelse(x$orthogonal, vx <- colSums(x$loadings^2), vx <- diag(x$Phi %*% t(x$loadings) %*% x$loadings)) if (sortLoadings){ vxo <- order(vx, decreasing = TRUE) vx <- vx[vxo] Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vxo] x$Th <- x$Th %*% Dsgn x$loadings <- x$loadings %*% Dsgn if ("Phi" %in% names(x)) { x$Phi <- t(Dsgn) %*% x$Phi %*% Dsgn } colnames(x$loadings) <- cln } cat(if(x$orthogonal)"Orthogonal" else "Oblique") cat(" rotation method", x$method) cat((if(!x$convergence)" NOT" ), "converged") cat(if ("randStartChar" %in% names(x)) " at lowest minimum.\n" else ".\n") if ("randStartChar" %in% names(x)){ cat("Of ",x$randStartChar[1]," random starts ",round(100 * x$randStartChar[2] / x$randStartChar[1]),"% converged, ", sep="") cat(round(100 * x$randStartChar[3] / x$randStartChar[1]),"% at the same lowest minimum.\n", sep="") if (x$randStartChar[4] > 1){ cat("Random starts converged to ",x$randStartChar[4], " different local minima\n", sep="") } } cat(if ("randStartChar" %in% names(x)) "Loadings at lowest minimum:\n" else "Loadings:\n") print(x$loadings, digits=digits) varex <- rbind(`SS loadings` = vx) colnames(varex) <- cln if (is.null(attr(x, "covariance"))) { varex <- rbind(varex, `Proportion Var` = vx/nrow(x$loadings)) varex <- rbind(varex, `Cumulative Var` = cumsum(vx/nrow(x$loadings))) } cat("\n") print(round(varex, digits)) if(!x$orthogonal){ dimnames(x$Phi) <- list(cln, cln) cat("\nPhi:\n") print(x$Phi, digits=digits) } if(rotateMat){ cat("\nRotating matrix:\n") print(t(solve(x$Th)), digits=digits) } if(Table){ cat("\nIteration table:\n") print(x$Table, digits=digits) } invisible(x) } summary.GPArotation <- function (object, digits=3L, Structure=TRUE, ...){ r <- list(loadings=object$loadings, Phi=object$Phi, method=object$method, orthogonal=object$orthogonal, convergence=object$convergence, iters= rev(object$Table[, 1])[1], Structure=Structure, digits=digits) class(r) <- "summary.GPArotation" r } print.summary.GPArotation <- function (x, ...){ cat(ifelse(x$orthogonal, "Orthogonal", "Oblique")) cat(" rotation method", x$method) if(!x$convergence) cat(" NOT" ) cat(" converged in ", x$iters, " iterations.\n", sep="") prstr <- !x$orthogonal && x$Structure cat(ifelse(prstr, "Pattern (loadings):\n", "Loadings:\n")) print(x$loadings, digits=x$digits) if (prstr){ cat("\nStructure:\n") print(x$loadings %*% x$Phi, digits=x$digits) } } GPArotation/R/NormalizingWeight.R0000644000176200001440000000271214524247121016432 0ustar liggesusers# Kaiser normalization #NormalizingWeight <- function(A, normalize=FALSE){ # if ("function" == mode(normalize)) normalize <- normalize(A) # if (is.logical(normalize)){ # if (normalize) normalize <- sqrt(rowSums(A^2)) # else return(array(1, dim(A))) # } # if (is.vector(normalize)) # {if(nrow(A) != length(normalize)) # stop("normalize length wrong in NormalizingWeight") # return(array(normalize, dim(A))) # } # stop("normalize argument not recognized in NormalizingWeight") #} # # # Version below submitted by Kim-Laura Speck, Uni Kassel, 25 October 2023 # # avoid NaNs in matrix A by adding machine precision values to zeros NormalizingWeight <- function(A, normalize=FALSE){ # custom function to normalize; input from user if ("function" == mode(normalize)) normalize <- normalize(A) # Kaiser normalization if (is.logical(normalize)){ if (normalize) { normalize <- sqrt(rowSums(A^2)) # this is only a vector # avoid division by zero exceptions by checking that double != exactly zero idxZero <- which(normalize == 0) # add machine precision to values that are exactly zero normalize[idxZero] <- normalize[idxZero] + .Machine$double.eps } else return(array(1, dim(A))) } if (is.vector(normalize)) {if(nrow(A) != length(normalize)) stop("normalize length wrong in NormalizingWeight") return(array(normalize, dim(A))) } stop("normalize argument not recognized in NormalizingWeight") } GPArotation/R/echelon.R0000644000176200001440000000201214323662111014374 0ustar liggesusersechelon <- function(L, reference=seq(NCOL(L)), ...) { # Split L in reference part and the rest A1 <- L[reference,, drop=FALSE] #A2 is L[-reference,] # Compute the part of A Phi A' corresponding to the reference variables # Compute cholesky rot = rotated reference part # No check or error message for singularity. Exact singularity is rare in # practice but ill-conditioning is a real danger. # now assuming orthogonal (Phi=I) #newPhi <- if (is.null(Phi)) A1 %*% t(A1) else A1 %*% Phi %*% t(A1) #B1 <- t(chol(newPhi)) B1 <- t(chol(A1 %*% t(A1))) # Transformation matrix: B1 = A1 * Tmat # Rotated solution for non-reference part: B2 = A2 * Tmat Tmat <- solve(A1, B1) # Assemble rotated solution B <- matrix(0, NROW(L), NCOL(L)) B[reference,] <- B1 B[-reference,] <- L[-reference,, drop=FALSE] %*% Tmat dimnames(B) <- list(dimnames(L)[[1]], paste("factor", seq(NCOL(L)))) list(loadings=B, Th=Tmat, method="echelon", orthogonal=TRUE, convergence=TRUE) } GPArotation/R/GPF.R0000644000176200001440000000711514344535050013410 0ustar liggesusers# legacy functions that contain the actual GP algorithms # these function shall not be changed # functions have not changes since 2008 GPForth <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL){ if((!is.logical(normalize)) || normalize) { W <- NormalizingWeight(A, normalize=normalize) normalize <- TRUE A <- A/W } if(1 >= ncol(A)) stop("rotation does not make sense for single factor models.") al <- 1 L <- A %*% Tmat #Method <- get(paste("vgQ",method,sep=".")) #VgQ <- Method(L, ...) Method <- paste("vgQ",method,sep=".") VgQ <- do.call(Method, append(list(L), methodArgs)) G <- crossprod(A,VgQ$Gq) f <- VgQ$f Table <- NULL #set initial value for the unusual case of an exact initial solution VgQt <- do.call(Method, append(list(L), methodArgs)) for (iter in 0:maxit){ M <- crossprod(Tmat,G) S <- (M + t(M))/2 Gp <- G - Tmat %*% S s <- sqrt(sum(diag(crossprod(Gp)))) Table <- rbind(Table, c(iter, f, log10(s), al)) if (s < eps) break al <- 2*al for (i in 0:10){ X <- Tmat - al * Gp UDV <- svd(X) Tmatt <- UDV$u %*% t(UDV$v) L <- A %*% Tmatt #VgQt <- Method(L, ...) VgQt <- do.call(Method, append(list(L), methodArgs)) if (VgQt$f < (f - 0.5*s^2*al)) break al <- al/2 } Tmat <- Tmatt f <- VgQt$f G <- crossprod(A,VgQt$Gq) } convergence <- (s < eps) if ((iter == maxit) & !convergence) warning("convergence not obtained in GPForth. ", maxit, " iterations used.") if(normalize) L <- L * W dimnames(L) <- dimnames(A) r <- list(loadings=L, Th=Tmat, Table=Table, method=VgQ$Method, orthogonal=TRUE, convergence=convergence, Gq=VgQt$Gq) class(r) <- "GPArotation" r } GPFoblq <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL){ if(1 >= ncol(A)) stop("rotation does not make sense for single factor models.") if((!is.logical(normalize)) || normalize) { W <- NormalizingWeight(A, normalize=normalize) normalize <- TRUE A <- A/W } al <- 1 L <- A %*% t(solve(Tmat)) #Method <- get(paste("vgQ",method,sep=".")) #VgQ <- Method(L, ...) Method <- paste("vgQ",method,sep=".") VgQ <- do.call(Method, append(list(L), methodArgs)) G <- -t(t(L) %*% VgQ$Gq %*% solve(Tmat)) f <- VgQ$f Table <- NULL #Table <- c(-1,f,log10(sqrt(sum(diag(crossprod(G))))),al) #set initial value for the unusual case of an exact initial solution VgQt <- do.call(Method, append(list(L), methodArgs)) for (iter in 0:maxit){ Gp <- G - Tmat %*% diag(c(rep(1,nrow(G)) %*% (Tmat*G))) s <- sqrt(sum(diag(crossprod(Gp)))) Table <- rbind(Table,c(iter,f,log10(s),al)) if (s < eps) break al <- 2*al for (i in 0:10){ X <- Tmat - al*Gp v <- 1/sqrt(c(rep(1,nrow(X)) %*% X^2)) Tmatt <- X %*% diag(v) L <- A %*% t(solve(Tmatt)) #VgQt <- Method(L, ...) VgQt <- do.call(Method, append(list(L), methodArgs)) improvement <- f - VgQt$f if (improvement > 0.5*s^2*al) break al <- al/2 } Tmat <- Tmatt f <- VgQt$f G <- -t(t(L) %*% VgQt$Gq %*% solve(Tmatt)) } convergence <- (s < eps) if ((iter == maxit) & !convergence) warning("convergence not obtained in GPFoblq. ", maxit, " iterations used.") if(normalize) L <- L * W dimnames(L) <- dimnames(A) # N.B. renaming Lh to loadings in specificific rotations # uses fact that Lh is first. r <- list(loadings=L, Phi=t(Tmat) %*% Tmat, Th=Tmat, Table=Table, method=VgQ$Method, orthogonal=FALSE, convergence=convergence, Gq=VgQt$Gq) class(r) <- "GPArotation" r } GPArotation/R/GPFRS.R0000644000176200001440000000601414344533753013662 0ustar liggesusers# Main functions that serve as a wrapper function to GPForth and GPFoblq # these functions have the added functionality of random starts # Functions added in version 2023-1.1 GPFRSorth <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL, randomStarts = 0){ if (0 < randomStarts){ Tmat <- Random.Start(ncol(A)) } r <- GPForth(A, Tmat = Tmat, normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) if (randomStarts > 1){ Qvalues <- rev(r$Table[,2])[1] Qmin <- Qvalues Qconverged <- r$convergence for (inum in 2:randomStarts){ gpout <- GPForth(A, Tmat = Random.Start(ncol(A)), normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) Qvalues <- c(Qvalues, rev(gpout$Table[,2])[1]) Qconverged <- c(Qconverged, gpout$convergence) if (rev(gpout$Table[,2])[1] < Qmin){ r <- gpout Qmin <- rev(gpout$Table[,2])[1] } } Qmin <- eps * round(Qmin * 1/eps,0) Qvalues <- eps * round(Qvalues * 1/eps,0) Qvaluessame <- Qvalues == Qmin randStartChar <- c(randomStarts, sum(Qconverged), sum(Qvaluessame), length(unique(Qvalues))) names(randStartChar) <- c("randomStarts","Converged","atMinimum", "localMins") r <- list(loadings = r$loadings, Th = r$Th, Table = r$Table, method = r$method, orthogonal = TRUE, convergence = r$convergence, Gq=r$Gq, randStartChar = randStartChar) class(r) <- "GPArotation" } colnames(r$Table) <- c("iter", "f", "log10(s)", "alpha") r } GPFRSoblq <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL, randomStarts=0){ if (0 < randomStarts){ Tmat <- Random.Start(ncol(A)) } r <- GPFoblq(A, Tmat = Tmat, normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) if (randomStarts > 1){ Qvalues <- rev(r$Table[,2])[1] Qmin <- Qvalues Qconverged <- r$convergence for (inum in 2:randomStarts){ gpout <- GPFoblq(A, Tmat = Random.Start(ncol(A)), normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) Qvalues <- c(Qvalues, rev(gpout$Table[,2])[1]) Qconverged <- c(Qconverged, gpout$convergence) if (rev(gpout$Table[,2])[1] < Qmin){ r <- gpout Qmin <- rev(gpout$Table[,2])[1] } } Qmin <- eps * round(Qmin * 1/eps,0) Qvalues <- eps * round(Qvalues * 1/eps,0) Qvaluessame <- Qvalues == Qmin randStartChar <- c(randomStarts, sum(Qconverged), sum(Qvaluessame), length(unique(Qvalues))) names(randStartChar) <- c("randomStarts","Converged","atMinimum", "localMins") r <- list(loadings = r$loadings, Phi = r$Phi, Th = r$Th, Table = r$Table, method = r$method, orthogonal = FALSE, convergence = r$convergence, Gq=r$Gq, randStartChar = randStartChar) class(r) <- "GPArotation" } dimnames(r$Phi) <- list(colnames(r$loadings),colnames(r$loadings)) colnames(r$Table) <- c("iter", "f", "log10(s)", "alpha") r } GPArotation/R/RandomStart.R0000644000176200001440000000166114557563657015256 0ustar liggesusers# Random start routine that outputs 1 orthogonal random matrix #Random.Start <- function(k){ # qr.Q(qr(matrix(rnorm(k*k),k))) # } # Changed after discussion with Yves Rosseel # Stewart, G. W. (1980). The Efficient Generation of Random Orthogonal Matrices # with an Application to Condition Estimators. SIAM Journal on Numerical Analysis, # 17(3), 403-409. http://www.jstor.org/stable/2156882 # # Mezzadri, F. (2007). How to generate random matrices from the classical # compact groups. Notices of the American Mathematical Society, 54(5), 592-604. # see https://arxiv.org/abs/math-ph/0609050 # # This updated version changed as of GPArotation 2024-2.1 # Thanks to Yves Rosseel for pointing this out # Random.Start <- function(k = 2L) { qr.out <- qr(matrix(rnorm(k * k), nrow = k, ncol = k)) Q <- qr.Q(qr.out) R <- qr.R(qr.out) R.diag <- diag(R) R.diag2 <- R.diag/abs(R.diag) out <- t( t(Q) * R.diag2 ) out } GPArotation/R/eiv.R0000644000176200001440000000110114323662111013540 0ustar liggesuserseiv <- function(L, identity=seq(NCOL(L)), ...){ A1 <- L[ identity, , drop=FALSE] g <- solve(A1) if(1e-14 < max(abs(diag(1, length(identity)) - A1 %*% g))) warning("Inverse is not well conditioned. Consider setting identity to select different rows.") B <- array(NA, dim(L)) B[identity, ] <- diag(1, length(identity)) B[-identity,] <- L[-identity,, drop=FALSE] %*% g dimnames(B) <- list(dimnames(L)[[1]], paste("factor", seq(NCOL(L)))) list(loadings=B, Th=t(A1), method="eiv", orthogonal=FALSE, convergence=TRUE, Phi= tcrossprod(A1)) } GPArotation/R/rotations.R0000644000176200001440000005003114557552026015021 0ustar liggesusers########################################### ########################################### ### ### OBLIMIN ### ########################################### ########################################### oblimin <- function(A, Tmat=diag(ncol(A)), gam=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="oblimin", methodArgs=list(gam=gam), randomStarts = randomStarts ) } vgQ.oblimin <- function(L, gam=0){ X <- L^2 %*% (!diag(TRUE,ncol(L))) if (0 != gam) { p <- nrow(L) X <- (diag(1,p) - matrix(gam/p,p,p)) %*% X } list(Gq=L*X, f=sum(L^2 * X)/4, Method= if (gam == 0) "Oblimin Quartimin" else if (gam == .5) "Oblimin Biquartimin" else paste("Oblimin g=", gam,sep="") ) } # original # vgQ.oblimin <- function(L, gam=0){ # Method <- paste("Oblimin g=",gam,sep="") # if (gam == 0) Method <- "Oblimin Quartimin" # if (gam == .5) Method <- "Oblimin Biquartimin" # if (gam == 1) Method <- "Oblimin Covarimin" # k <- ncol(L) # p <- nrow(L) # N <- matrix(1,k,k)-diag(k) # f <- sum(L^2 * (diag(p)-gam*matrix(1/p,p,p)) %*% L^2 %*% N)/4 # Gq <- L * ((diag(p)-gam*matrix(1/p,p,p)) %*% L^2 %*% N) # return(list(Gq=Gq,f=f,Method=Method)) # } ########################################### ########################################### ### ### QUARTIMIN ### ########################################### ########################################### quartimin <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, method="quartimin", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.quartimin <- function(L){ X <- L^2 %*% (!diag(TRUE,ncol(L))) list(Gq= L*X, f= sum(L^2 * X)/4, Method= "Quartimin" ) } #original #vgQ.quartimin <- function(L){ # Method="Quartimin" # L2 <- L^2 # k <- ncol(L) # M <- matrix(1,k,k)-diag(k) # f <- sum(L2 * (L2 %*% M))/4 # Gq <- L * (L2 %*% M) # return(list(Gq=Gq,f=f,Method=Method)) #} ########################################### ########################################### ### ### TARGET ROTATION ### required argument is a ### target matrix 'Target' ### ########################################### ########################################### targetT <- function(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(Target)) stop("argument Target must be specified.") GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="target", methodArgs=list(Target=Target), randomStarts = randomStarts) } targetQ <- function(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(Target)) stop("argument Target must be specified.") GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="target", methodArgs=list(Target=Target), randomStarts = randomStarts) } vgQ.target <- function(L, Target=NULL){ if(is.null(Target)) stop("argument Target must be specified.") # e.g. Target <- matrix(c(rep(NA,4),rep(0,8),rep(NA,4)),8) # approximates Michael Brown approach Gq <- 2 * (L - Target) Gq[is.na(Gq)] <- 0 #missing elements in target do not affect the first derivative list(Gq=Gq, f=sum((L-Target)^2, na.rm=TRUE), Method="Target rotation") #The target rotation ? option in Michael Browne's algorithm should be NA } ########################################### ########################################### ### ### PARTIALLY SPECIFIED TARGET ROTATION ### required arguments are a ### target matrix 'Target' and ### weight matrix 'W' ### ########################################### ########################################### pstT <- function(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(W)) stop("argument W must be specified.") if(is.null(Target)) stop("argument Target must be specified.") GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="pst", methodArgs=list(W=W, Target=Target), randomStarts = randomStarts) } pstQ <- function(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(W)) stop("argument W must be specified.") if(is.null(Target)) stop("argument Target must be specified.") GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="pst", methodArgs=list(W=W, Target=Target), randomStarts = randomStarts) } vgQ.pst <- function(L, W=NULL, Target=NULL){ if(is.null(W)) stop("argument W must be specified.") if(is.null(Target)) stop("argument Target must be specified.") # Needs weight matrix W with 1's at specified values, 0 otherwise # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). # When W has only 1's this is procrustes rotation # Needs a Target matrix Target with hypothesized factor loadings. # e.g. Target = matrix(0,8,2) Btilde <- W * Target list(Gq= 2*(W*L-Btilde), f = sum((W*L-Btilde)^2), Method="Partially specified target") } ########################################### ########################################### ### ### OBLIMAX ### ########################################### ########################################### oblimax <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="oblimax", methodArgs = NULL, randomStarts = randomStarts) } vgQ.oblimax <- function(L){ list(Gq= -(4*L^3/(sum(L^4))-4*L/(sum(L^2))), f= -(log(sum(L^4))-2*log(sum(L^2))), Method="Oblimax") } ########################################### ########################################### ### ### MINIMUM ENTROPY ### ########################################### ########################################### entropy <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="entropy", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.entropy <- function(L){ list(Gq= -(L*log(L^2 + (L^2==0)) + L), f= -sum(L^2*log(L^2 + (L^2==0)))/2, Method="Minimum entropy") } ########################################### ########################################### ### ### QUARTIMAX ### ########################################### ########################################### quartimax <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="quartimax", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.quartimax <- function(L){ list(Gq= -L^3, f= -sum(diag(crossprod(L^2)))/4, Method="Quartimax") } ########################################### ########################################### ### ### VARIMAX ### ########################################### ########################################### Varimax <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="varimax", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.varimax <- function(L){ QL <- sweep(L^2,2,colMeans(L^2),"-") list(Gq= -L * QL, f= -sqrt(sum(diag(crossprod(QL))))^2/4, Method="varimax") } ########################################### ########################################### ### ### SIMPLIMAX ### argument: # close to zero loadings 'k' ### ########################################### ########################################### simplimax <- function(A, Tmat=diag(ncol(A)), k=nrow(A), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="simplimax", methodArgs=list(k=k), randomStarts = randomStarts) } vgQ.simplimax <- function(L, k=nrow(L)){ # k: Number of close to zero loadings Imat <- sign(L^2 <= sort(L^2)[k]) list(Gq= 2*Imat*L, f= sum(Imat*L^2), Method="Simplimax") } ########################################### ########################################### ### ### BENTLER'S INVARIANT PATTERN SIMPLICITY ### ########################################### ########################################### bentlerT <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bentler", methodArgs = NULL, randomStarts = randomStarts) } bentlerQ <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bentler", methodArgs = NULL, randomStarts = randomStarts) } vgQ.bentler <- function(L){ L2 <- L^2 M <- crossprod(L2) D <- diag(diag(M)) list(Gq= -L * (L2 %*% (solve(M)-solve(D))), f= -(log(det(M))-log(det(D)))/4, Method="Bentler's criterion") } ########################################### ########################################### ### ### TANDEM CRITERIA ### ########################################### ########################################### tandemI <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="tandemI", methodArgs = NULL, randomStarts = randomStarts) } #vgQ.tandemI <- function(L){ # Tandem Criterion, Comrey, 1967. # Method <- "Tandem I" # LL <- (L %*% t(L)) # LL2 <- LL^2 # f <- -sum(diag(crossprod(L^2, LL2 %*% L^2))) # Gq1 <- 4 * L *(LL2 %*% L^2) # Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L # Gq <- -Gq1 - Gq2 # return(list(Gq=Gq,f=f,Method=Method)) #} vgQ.tandemI <- function(L){ # Tandem Criterion, Comrey, 1967. LL <- (L %*% t(L)) LL2 <- LL^2 Gq1 <- 4 * L *(LL2 %*% L^2) Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L Gq <- -Gq1 - Gq2 list(Gq=Gq, f= -sum(diag(crossprod(L^2, LL2 %*% L^2))), Method="Tandem I") } tandemII <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="tandemII", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.tandemII <- function(L){ # Tandem Criterion, Comrey, 1967. LL <- (L %*% t(L)) LL2 <- LL^2 f <- sum(diag(crossprod(L^2, (1-LL2) %*% L^2))) Gq1 <- 4 * L *((1-LL2) %*% L^2) Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L Gq <- Gq1 - Gq2 list(Gq=Gq, f=f, Method="Tandem II") } ########################################### ########################################### ### ### GEOMIN ### ########################################### ########################################### geominT <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="geomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } geominQ <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="geomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } vgQ.geomin <- function(L, delta=.01){ k <- ncol(L) p <- nrow(L) L2 <- L^2 + delta pro <- exp(rowSums(log(L2))/k) list(Gq=(2/k)*(L/L2)*matrix(rep(pro,k),p), f= sum(pro), Method="Geomin") } ########################################### ########################################### ### ### BI-GEOMIN ### ########################################### ########################################### bigeominT <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bigeomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } bigeominQ <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bigeomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } vgQ.bigeomin <- function(L, delta = 0.01){ Lg <- L[ , -1, drop = FALSE] out <- vgQ.geomin(Lg, delta = delta) list(Gq=cbind(0, out$Gq), f= out$f, Method="Bi-Geomin") } ########################################### ########################################### ### ### CRAWFORD FERGUSON FAMILY ### needs kappa parameter ### ### EQUAMAX PARSIMAX ### ########################################### ########################################### cfT <- function(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } cfQ <- function(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } equamax <- function(A, Tmat=diag(ncol(A)), kappa=ncol(A)/(2*nrow(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } parsimax <- function(A, Tmat=diag(ncol(A)), kappa=(ncol(A) - 1)/(ncol(A) + nrow(A) - 2), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } vgQ.cf <- function(L, kappa=0){ k <- ncol(L) p <- nrow(L) # kappa <- 0 # quartimax # kappa <- 1/p # varimax # kappa <- k/(2*p) # equamax # kappa <- (k-1)/(p+k-2) # parsimax # kappa <- 1 # factor parsimony N <- matrix(1,k,k)-diag(k) M <- matrix(1,p,p)-diag(p) L2 <- L^2 f1 <- (1-kappa)*sum(diag(crossprod(L2,L2 %*% N)))/4 f2 <- kappa*sum(diag(crossprod(L2,M %*% L2)))/4 list(Gq= (1-kappa) * L * (L2 %*% N) + kappa * L * (M %*% L2), f= f1 + f2, Method= if (kappa == 0) "Crawford-Ferguson Quartimax/Quartimin" else if (kappa == 1/p) "Crawford-Ferguson Varimax" else if (kappa == k/(2*p)) "Equamax" else if (kappa == (k-1)/(p+k-2)) "Parsimax" else if (kappa == 1) "Factor Parsimony" else paste("Crawford-Ferguson:k=",kappa,sep="")) } ########################################### ########################################### ### ### INFOMAX ### ########################################### ########################################### infomaxT <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, method="infomax", randomStarts = randomStarts) } infomaxQ <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="infomax", methodArgs = NULL, randomStarts = randomStarts) } vgQ.infomax <- function(L){ k <- ncol(L) p <- nrow(L) S <- L^2 s <- sum(S) s1 <- rowSums(S) s2 <- colSums(S) E <- S/s e1 <- s1/s e2 <- s2/s Q0 <- sum(-E * log(E)) Q1 <- sum(-e1 * log(e1)) Q2 <- sum(-e2 * log(e2)) f <- log(k) + Q0 - Q1 - Q2 H <- -(log(E) + 1) alpha <- sum(S * H)/s^2 G0 <- H/s - alpha * matrix(1, p, k) h1 <- -(log(e1) + 1) alpha1 <- s1 %*% h1/s^2 G1 <- matrix(rep(h1,k), p)/s - as.vector(alpha1) * matrix(1, p, k) h2 <- -(log(e2) + 1) alpha2 <- h2 %*% s2/s^2 G2 <- matrix(rep(h2,p), ncol=k, byrow=T)/s - as.vector(alpha2) * matrix(1, p, k) Gq <- 2 * L * (G0 - G1 - G2) list(f = f, Gq=Gq, Method="Infomax") } ########################################### ########################################### ### ### MCCAMMON ENTROPY ### ########################################### ########################################### mccammon <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="mccammon", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.mccammon <- function(L){ k <- ncol(L) p <- nrow(L) S <- L^2 M <- matrix(1,p,p) s2 <- colSums(S) P <- S / matrix(rep(s2,p),ncol=k,byrow=T) Q1 <- -sum(P * log(P)) H <- -(log(P) + 1) R <- M %*% S G1 <- H/R - M %*% (S*H/R^2) s <- sum(S) p2 <- s2/s Q2 <- -sum(p2 * log(p2)) h <- -(log(p2) + 1) alpha <- h %*% p2 G2 <- rep(1,p) %*% t(h)/s - as.vector(alpha)*matrix(1,p,k) Gq <- 2*L*(G1/Q1 - G2/Q2) f <- log(Q1) - log(Q2) list(f = f, Gq = Gq, Method = "McCammon entropy") } ########################################### ########################################### ### ### BIFACTOR BIQUARTIMIN ### ########################################### ########################################### bifactorT <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ #adapted from Jennrich and Bentler 2011. GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bifactor", randomStarts = randomStarts) } bifactorQ <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ #oblique. Adapted from Jennrich and Bentler 2011. GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bifactor", randomStarts = randomStarts) } vgQ.bifactor <- function(L){ k <- ncol(L) Lt <- L[,2:k] Lt2 <- Lt^2 N <- matrix(1, nrow=k-1, ncol=k-1) - diag(k-1) f <- sum(Lt2 * (Lt2 %*% N)) Gt <- 4 * Lt * (Lt2 %*% N) G <- cbind(0, Gt) list(f = f, Gq = G, Method = "Bifactor Biquartimin") } ########################################### ########################################### ### ### VARIMIN ### ########################################### ########################################### varimin <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="varimin", methodArgs=NULL, randomStarts = randomStarts) } vgQ.varimin <- function (L){ QL <- sweep(L^2, 2, colMeans(L^2), "-") list(Gq = L * QL, f = sqrt(sum(diag(crossprod(QL))))^2/4, Method = "varimin") } ########################################### ########################################### ### ### PROMAX ### (not in use) ### ########################################### ########################################### # promax is already defined in the stats (previously mva) package # #GPromax <- function(A,pow=3){ # method <- "Promax" # # Initial rotation: Standardized Varimax # require(statsa) # xx <- promax(A,pow) # Lh <- xx$loadings # Th <- xx$rotmat # orthogonal <- F # Table <- NULL #return(list(loadings=Lh,Th=Th,Table=NULL,method,orthogonal=orthogonal)) #} GPArotation/MD50000644000176200001440000000453614557762672013006 0ustar liggesusers223d6c3c065f91e6b6e7562d0b303eb1 *DESCRIPTION d33cd9ef710324b7fbe8e02e5ee3f079 *NAMESPACE d3e72515a5df7183c2a0ba3604f5ab84 *NEWS 8a4132c99961e9e87f8313ab3c5dbb3d *R/GPF.R 9387383de4cdef2b30c841a672d11c48 *R/GPFRS.R 9f4b96a2dfee75edb4a4901f065918f0 *R/NormalizingWeight.R fa4dd320c95103bf48e3726aa74e6d1e *R/RandomStart.R b2283c391ae6a2be35b3fdd08ac120d0 *R/echelon.R 0d865c154a7c2323eced4a2dc5300f92 *R/eiv.R df85dc6a7f25b195782441290afcfb21 *R/printsummary.R f1f851b8ac3ac391b70ddc3412305327 *R/rotations.R c24ad5ddf7986bb0e715af725aaee8d6 *build/vignette.rds 7f348dc5f8a2ab65e2dea15b43ed84df *data/Harman.rda 1f218e36ca05bd6e16c50d212aebd6df *data/Thurstone.rda 08afb21d4b7673c7deb2269a3df860e9 *data/WansbeekMeijer.rda e8a386c68402ae9d8f05c7a9bea60c51 *inst/CITATION e7704312a929f314ffb264a2a6f17d8a *inst/doc/GPAguide.R 58edd38fd7505516ac3d5ba47732db07 *inst/doc/GPAguide.Stex f63f7d40be04699d6443c30a869b15a2 *inst/doc/GPAguide.pdf f77834c2bfa540de1868e7e103e3db92 *inst/doc/GPArotateDF.R e150ab1c5873face932f18d69bb87090 *inst/doc/GPArotateDF.Stex 6ca014df7e7fe8c595c01e8944ab3695 *inst/doc/GPArotateDF.pdf 0b3e566230b09ca3ec480348a343c539 *man/00.GPArotation.Rd b06ba86ad843365d0264aaacadd83d1f *man/GPA.Rd b07d349e1b8f30647094651a5d2a0afd *man/Harman.Rd 5c36d60ed084d2698b5a0543ecbbd4df *man/NormalizingWeight.Rd 94cd13a215e82eb4be10066299c13a4e *man/Random.Start.Rd 1b2feb8eed2d7acc01d9bf6c4b926c6b *man/Thurstone.Rd c172806d91b6ead4f5586323cc3b0d68 *man/WansbeekMeijer.Rd 6d6131cbf00420833323ee4acea45672 *man/echelon.Rd 38433de7a3c98490a03151b76c310134 *man/eiv.Rd 76236e632de875edc2edecefe65d0f57 *man/print.GPArotation.Rd 5e5faf1e45d169960df2e627d4c920ea *man/rotations.Rd 9974f81896e827e29bfe5a529e723814 *man/vgQ.Rd 676d839d54d47a340f6ba36a5934221b *tests/Harman.R 9f69c980178c705014f0b982c1200f0f *tests/Jennrich2002.R b23cf38f1863aec555c6f4c910c41d66 *tests/KaiserNormalization.R 781b18f963b96896b8db5224e710ebd1 *tests/MASSoblimin.R 46d697ec9edc75bcc1822dbdae7c3f0f *tests/Revelle.R 4a45741f72f718179f8070cf11681727 *tests/Thurstone.R 721c8721d216481c1af1e14680c61627 *tests/WansbeekMeijer.R 6516a79368c2bd35c1633cee216f2972 *tests/print-GPArotation.R fbe10030749fea8ab8ddb2cdc564bae0 *tests/rotations.R 0ff9640302263cb72e34844d7a090dcd *tests/varimaxVarimax.R 58edd38fd7505516ac3d5ba47732db07 *vignettes/GPAguide.Stex e150ab1c5873face932f18d69bb87090 *vignettes/GPArotateDF.Stex GPArotation/inst/0000755000176200001440000000000014557733461013435 5ustar liggesusersGPArotation/inst/doc/0000755000176200001440000000000014557733461014202 5ustar liggesusersGPArotation/inst/doc/GPArotateDF.Stex0000644000176200001440000002402114404451600017065 0ustar liggesusers%\VignetteIndexEntry{Derivative-Free Gradient Projection: the GPArotateDF package} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{cubimax} %\VignetteKeyword{forced simple structure} \documentclass[english, 10pt]{article} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=FALSE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Derivative-Free Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotateDF} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \subsection*{Principle of derivative-Free Factor Rotation} The gradient projection algorithm package \emph{GPArotation} consists of wrapper functions and functions that compute the gradients, $G_q$, needed for minimization. For all functions included in the {\em GPArotation} package, the gradients are included in the \texttt{vgQ} routines. For example, the \texttt{vgQ.quartimax} function provides the gradients $G_q$ for quartimax rotation. Examples of gradient derivations, computations are provided in \cite{gpa.1} and \cite{gpa.2}, as well as \cite{gpa.rotate}. However, the derivation of the gradient can be quite involved for complex rotation criteria. In such cases, a derivative free version of gradient projection algorithm can be used for minimization of the criterion function, {\em without} the need for a gradient. Details of the methods are described in \cite{gpa.df}. The method is implemented in the package \emph{GPArotateDF} that may be downloaded and installed. To perform derivative-free rotation, the main algorithms \texttt{GPForth.df} and \texttt{GPFoblq.df} are available for orthogonal and oblique rotation, respectively. Both are minimization algorithms. The algorithms differ from the regular algorithms by the inclusion of the numerical derivates $G_f$ for the rotation criteria in \texttt{GPForth.df} and \texttt{GPFoblq.df}. The algorithms require: an initial loadings matrix \texttt{A} and a rotation method. Optional are initial rotation matrix \texttt{Tmat} (default is the identity matrix). Other arguments needed for individual rotations are applied in the same way as in the {\em GPArotation} package. The rotation method is provided between quotation marks, and refers to the name of the ff-function. For example, the \texttt{method = "varimax"} through \texttt{GPForth.df} calls the \texttt{ff.varimax} function. The ff-functions are the derivative-free analogues of the GPArotation vgQ functions. The output of \texttt{ff.varimax} is the rotation criteria value, \texttt{f}, and the \texttt{Method} name, e.g. \texttt{DF-Varimax}. New rotation functions need to be programmed as \texttt{ff.newmethod}. The only required input is an initial loadings matrix \texttt{A}, and any potential additional arguments. The output consist of the value \texttt{f} of the criterion, and the \texttt{Method} name (the \texttt{GPForth.df} and \texttt{GPFoblq.df} algorithms expect this included in the result). \subsection*{Derivative-free quartimax rotation} As an example, consider quartimax rotation. Gradient projection quartimax orthogonal rotation seeks to minimize the sum of all loadings raised to power 4. Thus, using the notation of \cite{gpa.rotate} (page 682), the criterion $f$ for minimization is calculated as \[ f = Q(\Lambda) = -\frac{1}{4}\sum_{i} \sum_{r} \lambda_{ir}^{4}. \] Derivative-free quartimax rotation using \texttt{ff.quartimax} is then very simple \begin{Scode} library(GPArotateDF) ff.quartimax<- function(L){ f = -sum(L^4) / 4 list(f = f, Method = "DF-Quartimax") } data(Harman, package="GPArotation") GPForth.df(Harman8, method="quartimax") \end{Scode} Of course, for quartimax, the gradient is easy to derive and regular rotation is a better choice. \subsection*{Rotation when the derivative is complicated: cubimax} Sometimes the gradient is hard to derive. For example, a criterion that seeks to minimize loadings to the power 3, the absolute value is needed for a meaningful result. \[ f = Q(\Lambda) = -\sum_{i} \sum_{r} | \lambda_{ir}^{3} |. \] While the gradient may be complicated, the derivative-free function for minimization is straightforward. \begin{Scode} ff.cubimax<- function(L){ f = -sum(abs(L^3)) list(f = f, Method = "DF-Cubimax") } GPForth.df(Harman8, method="cubimax") \end{Scode} Results differ from quartimax and varimax rotation \cite{mulaik} describes Absolmin, the sum of absolute factor loadings minimized. Minimizing the criterion is straightforward using the {\em GPArotateDF} package. \subsection*{Rotation when an algorithm is involved: Forced Simple Structure} In certain cases the derivate is so poorly defined that deriving the \texttt{vgQ} function is a non-starter. For example, an algorithm that updates a weight matrix that, when multiplied with the loadings matrix provides a rotation criterion to be minimized. The algorithm Forced Simple Structure chooses a weight matrix focused on the lowest loadings. The rotation criterion value $f$ is minimized representing a rotated factor pattern which many low loadings, restricted to each factor having at least some salient loadings. In each iteration, the weight matrix \texttt{Imat} gets weight 1 at the lowest factor loadings, and 0 elsewhere. Assume we have \texttt{p} items, and \texttt{m} factors (for a \texttt{p x m} loadings matrix). In each iteration, first the lowest loadings get weight 1. Next, for each pair \texttt{(i,j)} of factors, lowest loadings get weight 1 until there are at least \texttt{(m + kij)} items with weight 1 on a single factor \texttt{i} or \texttt{j} (but not the other factor), or not enough loadings are left to get weight 1. Possible values for \texttt{kij = (0, ..., [p - m] )} and defaults to 2. Forced Simple Structure is most effective when \texttt{kij} has a lower value. For each increase of 1, an additional \texttt{(m)} loadings get weight~1. The criterion \texttt{f} minimizes the squared loadings for low loadings (``non-salient''). Salient loadings are therefor increased as the sum of squared non-salient loadings is minimized. \begin{Scode} ff.fss <- function(L, kij=2){ m <- ncol(L) p <- nrow(L) zm <- m + kij Imat <- matrix(0, p, m) for (j in 1:m){ Imat[abs(L[,j]) <= sort(abs(L[,j]))[zm],j] <- 1 } for (i in 1:(m-1)){ for (j in (i+1):m){ nz <- sum( (Imat[,i] + Imat[,j]) ==1) while (nz < zm && sum(Imat[ ,c(i,j)]) < m * 2){ tbc <- c(abs(L[,i]), abs(L[,j])) tbcs <- sort(tbc [c(Imat[,i], Imat[,j])==0])[1] Imat[abs(L) == tbcs] <- 1 nz <- sum( (Imat[,i] + Imat[,j]) ==1) } } } Method <- paste("DF-Forced Simple Structure (kij = ",kij,")", sep="") f <- sum(Imat*L^2) list(f = f, Imat = Imat, Method = Method) } data(WansbeekMeijer, package = "GPArotation") z <- factanal(covmat = NetherlandsTV, factors = 3, rotation = "none") fssT.df(loadings(z), kij = 3) # which loadings get weight 1 in the first iteration? ff.fss(loadings(z), kij = 3)$Imat \end{Scode} The added \texttt{sum(Imat) < m * 2} requirement was added to avoid infinite looping. It is useful to consider random starts as the rotation tends to have many local minima. The method works both orthogonal and oblique. \subsection*{Examples of other ff-functions} Writing ff-functions is straightforward because only the criterion value is needed. Here are a few additional examples of ff-functions. For all vgQ-functions exist and are preferable to be used. The oblique rotation criterion for oblimax \begin{Scode} ff.oblimax <- function(L){ f <- -(log(sum(L^4))-2*log(sum(L^2))) list(f = f, Method = "DF-Oblimax") } \end{Scode} Entropy criterion for orthogonal rotation \begin{Scode} ff.entropy <- function(L){ f <- -sum(L^2 * log(L^2 + (L^2==0)))/2 list(f = f, Method = "DF-Entropy") } \end{Scode} Simplimax that works well in oblique rotation \begin{Scode} ff.simplimax <- function(L,k=nrow(L)){ # k: Number of close to zero loadings Imat <- sign(L^2 <= sort(L^2)[k]) f <- sum(Imat*L^2) list(f = f, Method = "DF-Simplimax") } \end{Scode} Target rotation. Requires both a weight matrix and a target matrix. Target rotation can be both orthogonal and oblique. \begin{Scode} ff.pst <- function(L,W,Target){ # Needs weight matrix W with 1's at specified values, 0 otherwise # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). # When W has only 1's this is procrustes rotation # Needs a Target matrix Target with hypothesized factor loadings. # e.g. Target = matrix(0,8,2) Btilde <- W * Target f <- sum((W*L-Btilde)^2) list(f = f, Method = "DF-PST") } \end{Scode} \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock doi: 10.1177/0013164404272507 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2001}]{gpa.1} Jennrich, R. I. (2002). \newblock A simple general procedure for orthogonal rotation. \newblock{\em Psychometrika}, 66(3), 289--306. \newblock doi: 10.1007/BF02294840 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2002}]{gpa.2} Jennrich, R. I. (2002). \newblock A simple general method for oblique rotation. \newblock{\em Psychometrika}, 67(3), 7--19. \newblock doi: 10.1007/BF02294706 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2004}]{gpa.df} Jennrich, R. I. (2004). \newblock Derivative free gradient projection algorithms for rotation. \newblock{\em Psychometrika}, 69(3), 475--480. \newblock doi: 10.1007/BF02295647 \bibitem[\protect\citeauthoryear{Jennrich}{Mulaik}{2010}]{mulaik} Mulaik, S.A. (2010). \newblock {\em Foundations of factor analysis} \newblock (2nd ed.). Chapman and Hall/CRC Press, Taylor \& Francis Group. \newblock doi: 10.1201/b15851 \end{thebibliography} \end{document}GPArotation/inst/doc/GPAguide.pdf0000644000176200001440000032216214557733456016334 0ustar liggesusers%PDF-1.5 % 8 0 obj << /Length 2664 /Filter /FlateDecode >> stream xڭYs]1ai;c{֙Tէ$ RLw.R>=kzu{ݖ!GAl/s;o&v*Iv&sK-erܻ 5Pηt,8R.x{Zj4 @ko%UNJ<ΪOy)nʡ`.vZƩjz0:TOrkcdž )m@+E'QNcWr+qw㊆+c؋qЖ,KfXmv֖&ѱߵgHtr: ћ#S2zYr}' (N6(~R%#:MgWd̩j֡-\XyZPi7XK،FB׏,t<#?FJ@çYt4o4PΏ#"}:ǗKO]} ڐmϚ~hxcs˧ ',5ȍ+l8OU'LTʙ>>G_?ϸ(?[TQEr⧎^3$9wgoKи:VlЗ{.*D`2^iub:O&Ε 0uH*jc4`UФBBNI;Cp971] #DZdaE\'L:6- mմqVS\ֈHoM\uk' R,(*m20^ $qyY N[8yOsb eY 9Ӳ!kkY *fV~;֌F8flc`W56?'qUFlb[VӨ5pSYrSq)Y~^OHR7ulk }t E2z e,KQix}(4*(z'O Sc/h\JAcH.i$W [ ɡȕD_R endstream endobj 33 0 obj << /Length 2431 /Filter /FlateDecode >> stream xZKFWybWX/ f8aGhHzu7)jcK~TWUW}U^L2=*2&2]g]y?B muk_?,I-ݯ`<'iYSߛjm߶0bůׯ{Y΁xT&Jb~j f 3mc_OSO1?m#{jh&iLOb‚2t4 ~B<) 5HzM<`_H,M:3jSSC^UhLdjayRo}a{F͡v/ݎ Tw]`uQFIRZP0nľ!*oK ؂yͫ ?UGCoan'cPtj#UmԬ-N]#ph+b񰆹.+3󴞇 vQL>  yNɯH5eN'uEZ\hĊHyDa!vl (cGNMľo׷|}Z>??L.5lAd=6C`/`Ȟ2 Ay9^q52?-:_h +1v1P_{aGf;M},e9m^4v ):EQ~ 1᚞jOkG vpjn0_%N+ K-Q\5ѝmsqG[j'7YD]+ԇ RkuSc8qD܏ <CAG(naJ=FĂ{Z*BH#N ~**0g[۞16< 9,T"nq2aۂh)ag9YTPk"-bAd%RMsv/)K ;Q!8p3q98;čo}uO$U' x-:{)@~;O|h at O^fmS+:#ˣ&(~15DT$SgQZ<#Ĥ )<kR;tv,xSQN-(МȮhO!$… :yq]f },QdQA dv,%E9uֽ(R/{& mT>\{-=5w #=12i(EsN7Xr֠y`0JRka:DƁ-wʶ*ЦJ1{JɆ/0l]@ԇVK i);*ψ<'o褾paY硰"^τ˺^N״z L yS;a̬]'X{37].1QlsOio,c +mVܤQn擄 .Hؽ5&'D|XGD 5ҖOړ Nؤ:."-;40aL={G3Fe# '1c5!3~VFx8;Յxlƴi?6(k, _x^rRQ0G$r99o黤ك+0FjW }>'"ѕ+ !U'0+>]?/N>0(s*i  6c uIPw>K5[<0[e(g3sy˯;j԰SQj|$۳3Wl撵, )+p)Z> stream xYiF>.Tva7o``ɧ$dZXes&:f._zU|R^4K NI}70{ ڋ8(W޻]ܪ+"թw7X!@ڻz?o#/᳇O8.hE Mr=ҹ4{o ͼRA$v~9Aw͖oVa8ۥF{1\}{w9[D!'!8?sti B1Gqe-+a3`cz6L @R`%AGsqIO%I8|}ځsD abw npZr]bGrSNLURm/*8bA| 稰XIZ-dt{nI6Ν62h5oask-c帽#՘ ?狣xR7 /~ _>SoHtv^޹Ѡ"Iv\<1q >GWI@|F]ȼ |/mZc%݋ȠQFb2Fdz{$6HDyOkwQ|~GSO6R_CV>H99{iNP YQcjT4"F=әsÔ׸}P_,JP]scL#˯55 RC}^mi]͆H4l`zH{'R̦m>~Ϸg5w6|{a[*<|AonRGqvt]]mV2,o-͒#Nφ􍸜ǰ][Z^P=QeiP[ k*[瞵'zw|ܤ.;(/bM,-$!7zcגn5(Ndy$>2Ytc_S޹Fl i ?~r7ϼNU=FL W-;^Ѻ4}Eľݷu6ou?32;ݟyʩz/ ̩񗕔 5J}&_x"${.˔z ~ J),*i/_ ?:8*rIJ8?[8 ן6ޟ4"sWd3> endstream endobj 57 0 obj << /Length 2701 /Filter /FlateDecode >> stream xZo@ pzA{-ɲ䜍גKrofggk_Tm~rysRUjs*h-$XOMgnw4̤k!{uDɨ&}̜ OQUe'[`Is.{+n6[S[d5 G.瑻?nN'62]w$u_g/$#qפuš*3jJJ¾ܒBA_P uEԪ Kk-)`z+ϰ;ꆇ21:pi:YyC4X7Pz`M]+XO_|#ڳ{Z:mMzbZ3͐B DաHTm P=}Z@c !NFan >pjj':ĔيV,Z ȧ^H3BNG_6WE| UaD%Bm_cS2D#L| `㔭\dGډsZB6oN-kZizaV(4K'8k̈Љ/, lE6 rI:)OHSELrP4g׆c`󴖗kG53R관F<@a9pP0^Uދ:62L\΢F6"eX79YmH]5οɝ2JF-f^԰r^+b^_b:x{Ŗ)Wlvk2k*rim]eW52,-IOB+g%$νuxh+}a؆?,d}3r RSkG?&0'vIR+l>m녑2Ix2`rr.?g1p)n7B"TiΨB4:1lʳ7¯ib؞"Toex:@GYh2Rgh)ih- 1',v 6XFvgqT@G^/=>f+j|7}ɲƞ8izoĦ悦I B7-^QuuX Ѱ>Ab] DҲRJa0k `O'v8S>tz^ւYcϋW/ ;' endstream endobj 70 0 obj << /Length1 1873 /Length2 12583 /Length3 0 /Length 13727 /Filter /FlateDecode >> stream xڍP[Ӯ ݂%Ip5{8d;UUsuuZO͐+ lmh2B f:&X22es'+?fX2U @w{ @ `dbdb`010pO@ C:’ ڹ;9o?OFFNNvF6'3FV%[#scdEOJg`Hg`GIp5w2(.@c+%(;mW5qr5p VF@6@%Ii`h ?Y;_ l lmL&V@4 wzs+S#y?eQca[kk#o}"@cwf-ml]m<scE;ѫؘ;%E y7k3:X@{Ȍwzew;_οx{Lދzz:N@o?Mcs#'!f~n-c0ϓ{{X ʪI+HP]|BBnOZ6&-+}{N#o`?Jژ8V~LpP;{mrmVVk_k-H/?M>2c`CՀI:Q h,oddwmW=cV6@y[G/-#!r濷15=`Llw+~'V'$ja={q[5M'^?3^_bKKz{ @/gQXһ2{N]J&o2.|`3N?]EZIoe2w ?].|G2w{^@viֈ;Т6ZוvwwlW-sɡ **N0iyu[ӭ2qK=THkB۳׋^n$FD`|q+ {Ul^ʧiT"Jf 氈!hP/ܐfofPs&aO<57<־)39vabkbݢN{ $Ka.xG.d .}V&"XF˛+݉Fڕj`j4! "+7I$*3y|%WW5&:9S餦GzTo$GNT)Z6yZyx]rA'ob(~kt+}y Ȕv>;F ooj,L s^Dpga&qML,M: EsZe0f{ VWa7%E~ dZH/Q;j.km'6ԂUmkRm݅LPrM(Ieڏ<)Ėt O4aGc ~o~Dě=?ڣ#QLC8>;+HTCp.DKv5i5$P\.RT%&.6 6'WQ_OWŹjF[DMJ4]>0h>S3ACp3,J@Dg@C˵4"K?xqgDo'5{DQ'tcE|MK4f%%s+p@3}'fB:7T"b{DZQP%С:Y :Jtg;v K\P;O Z|HtE(*[v7ma'7H&+zC_H<9h9\z-] K߁tAFR8 7H,_#9/ә1eG&@_c -84ԷLp@_~Qvő@x#*2rD4oʇ>kLy=; 4~Kږi~څނVExq!$E?3nZCzČ$5=?/ꭙKm1gf1mSdoTsl~&RH&jaykGY@S^jfFc&sbhqF7RqCfl*TB >ɐֳTvyōfߔp٧'8/VhLr5CHZ e<C$4 p|*i9_Ubi|yH"46-b3D@k$|p_Fȼ\W<9,#PPu= fZs,ѥNyk]>WI\;kzdDWE`Ii;ցFxDu6ڈ˒bpy+]UZ6fd-׏v{ř]Pװn!`DB-\oҷXG޹&~vkǘ载Qچ:ĕ^nAi>tnLyS)bo +qa0<܇) ~ĝ|O=l)̂gt˶YyRbyg&9^~"vwjj.·I2cvj:RwTH3\`[X32ߕ2Gwӓ= _jhN5aXf& A?r# \-+Y?Mszj"&/+JUԕ?sH: ~4]?O\ dPY$R뚯(*iMxQaCcqN~.4>p?8>̈S akAD9Icm\$#7Ʌ% MOn6]<#yfAZF@BE sA"%f7DF]1."j}IfD\Cj[iFx؅+8jl B^7{Fa3:{6W#Q}Q<=ꀦ{>I!w@.y(nBjlL$u>/o2ʐ5;$b5W5I> exz7U=1\srFG[>1G}*/D?I |z9 Q%"siB<臙Swh-G~p$5E}!̖ m$uozW «aRw4RTGWcq'oOtjI08QJˍkV`T5W?LH#;a UIO_oy׎Kh]0ĜL|\`>H8Ŋ)=#I% R9 {Ws 9 S(US, W4Ҹk} Ҧ#6b盓s 8$H=4kuǔ"xV/Ge۟#P+5F"0#TǵOFa+~˂0$$Md8/O"_ \U^Եp|vJ,=ʼc5(qVf Jz"iߵSLk19Zo^荂(y-Yni1{]_/{`Ǖ}>J!mn*fqӓD9'"%'W0f eooթO(SԅDgk_\h}Hb\E_0fW$u3lÛÿF Ux{ Αˣ [hGLBt¿,1)Z*S9g*ek/pF ̮hDXIn'6\.t,>5ʚg,Fk8d~(#[l52TOYkEdNΥt& KL3/_0*VNkj5k:+˴lNa5KϹ9Q.T]JJx` )6ϔ$'B=a]ozr}Z܂. B !3mN]ϕσb\ê.؊ ʗOESJaӞJpsH'D-X2& 6|NCElk=?*A<;Vhw!tl'k6bX0 gADKqZB n] F#nYL)ڕ"1]۝nvAm잼`,Ё|_N6ԫӅ#K@L&ŻuC¸zv.Let%]zvM,L}#(M*/G,NOXFG:T}Vl Z9&C1rbA/q@Ȭi#gwƉ( ؊DU=$[!g}X9K QC(H/~ "&ÆgI OȉZI+; QX=*i 52&wq߉g d⚨z>5{VB~LQo4G~TjlZ8 GJj|X|]R;?b}H+)ހ/ Rbal%~SPS>ܐZo.SW3`(mLO3e#5b!ItFK'Q#T!u8"iD D4P^:#_Vt UHZ{-,ޑ0D/q ` ̃"K6TEWν =A2;s0&&"8;Cʢ #sd\?5w1]A[NLGOyFu_E6c/~٪ V,/Ah aA049Zlg>v37r#}S"Ᏹr4Ǝ*c;lMgn,3$%c}fQ)XYa7>^|R1|ҍ% w PtwE7-p(_kC=-؂ !rd`0"H܇[2]ȇ/¦܅]B$M W$f%%DOC,dvku$hy5I׋OKe6 Jlw~+r)GǛI6O-E[,{#1:Nm_yu-}yT!]rǍ߄NJQ!6a6 H?LISH59I[*b8 k#e걤}/9Uh |O1x/s݇Uוتˋ,쭡(<ė2`EE1}*ӈO)T=Sp*es ڤ!SpELG2QcQF}#+TKϝ; %S|_n!T:: ]"_,Cf,#jd=gX:vlpV+O/!#]-zًFGYL W}=Eݳ,@ ߞT7U2.M?]szE kV$\06Z=(ӷKܱ*"pÌ7'Y2y2j3FXvUn ^'!(JŷrVf!dؾvٖ|^IkY?BxX~WOGB[2"՘  = b)l^m՝@!;cx:YKTl[a~ :TQ6)x5?魯y*bXB%Do7ЬS',iIWpfO,ݵ?(^ tQpu5ˌ(| R):~bfZPZ;~Ǥ\χ)__!y]lp2 xa/MrZ31ZLPhF;%⾲(°8< {`Ȅg\'KR'#BD]). BgSgpq[Ae{H g]Z4,jobO{{x7C,ss 6|:Y_G[6 X*(.ҟ |wO'#Mn N P@W`N'1n޿EyE[힗oNk.u Gpb.iS3w9 Kci*=I=CK]2R9n& hT QZuބUBiG0*߃?ZIPF/dX5&)+@l2NT:4m|[>氏A^f #]]m`>D&Rnv<20I(շ *xtmJtéԕ=JP{7RsZ92Y5R_Y˅MTYj Oˌ.l%V+ms:aYGAc-48᜽վI$i\Ԍ mTmn4{ZhgӇfuqTqDm k0~ߜc_Z}C7>\uLo{a]14K}n6`*}xi3:>uTwܷXco]Xq ` <Bo39Ɨ̸ojov]_Â,l(g1pfo5X>EGC^ tVt=<v_*\Xc\2&M K7&Q&Sbѐ4SaHa3<]rx-IP18d rX|qҁ $զ(/Ĵ ^;8Ĕ+=0 F2^ *Cb]9$*xp4S!-TZ-RZ}H??Ľ: #+$'0ǘL בFm~)YA&{XK٤&ń+[˸s Q>HjR F-Ch6d 53eG&nq6|f:Eܤw۪jN}Ab/sHa?5X=ud Z2BrSQƜ^u>c -T(ƟT8HOpIZN.O3LDAٟbyS=FU|R0(}  h"g䶣3wUqlcBNӼL%$2|T\h̤&ؕ]b"O*ّ44!\#*wCdآa9trA6!nC n떫63Ɵl WIrtX#V;H*0hV!&~byW+Vl&W-rtSвʎ06˒7N7Odmt;6ጼMTAXQpЍ(K*JGI=5\=vT@\T/j7 HNJ7h]6HrP/<g'%,,5Fo_,*rtDL^U|}v'h͟+sv"̮H0b`L9 4Pƕdc_:F(Xh<޷&g/6J8z;9L~Y)پهvGf ޸4U1١%̢6~T w2[ñS79eۘPDxB{iB Q l`>8Ct(+"x<( - |=}zRY{nrrCMgN~4xa+f7P[VdsQ_Y4{Xd`)izLѷh٠`yP˃m}]7c4EiҼ*bMZdfw 8"FQAIYٍ*_4Lu>H2 _} lQzq{{C4Vm(ДLZ 2O(/i }>bbf+W)=P4E/z@mhIM ~.8,{̢ϭ1 /y­ pe|UW3;j8% =e̫9ks&lָ`.K="u[;$ܕF7v-T?a /&9ijsW2UtAyn7\5ȯ!s;Wq3f G7Uߟ%zAt2MoP.@&i:4 -$%[f~gºꃥs8aR?BiWJ 2.%}ʭ.NT`da"dRF&9;+l ; y +'XnA}dL2_dR`YzhVU#;l]N45X+ױ$%ՎwÔ8Ie #K Llk Cl  38+#eV(L% R3xzZJV1cYYG\CDoޒiTV6xwf?Q3nuv)TTGf)\Ђld/%#'hMDqzw+2xZT|gua,6_`y!uUc;yѱe~WqD-$KdOr] Kglw G<ux,6z:>J^i٧bCѰ-`nWH#%g]+x53 ##3YaSޜTI-M:Y.\7?ZHuй[c[ۊ?iCV[LkE?"C!u:@"fp܎`f3Z#7ʴt,{ł!~ҧ Bn}*-_7JN%I.זxL K:nB9_9+Gi pX~E׊՘M֚%I'v;@xByz~¼nZԖL0>ZuZLcN7yi-]{Pg: q\!Ё }% Ao$/啹SXո;GZΪ>Z)Lߎ@:$ao33(dn1 ܛBK%SgD@le HN#a^N4W+']EGWz hT3BV_x yTu!ʎ~+&B+K"@U7(la:<ſ!gù@,e Ɗ 4뀲<žW*"dﲀ1hF;["ͻpNI`U[2C_-p; L!=V endstream endobj 72 0 obj << /Length1 1393 /Length2 6081 /Length3 0 /Length 7030 /Filter /FlateDecode >> stream xڍwT6ҡ"2* !0CwwJ0 003tH7ҝ]" **H79=Yk{_}g=z cA<@1*y@>RVV} Bp@ȣ` V`8@ @b@ @n0&@ IYHODEr:CQ0c،@A1.a xyyhNBPe5RV= ۠ŸQPV@h+`T5H(7X7@<3  `' n9AJ<#nvB#`70 l* P A4Gޟa׬G8;C4`(({Gýlap۟mظ"y 0W VEo.Ğg}O$S@lm@}aP7`PP_4S"60` IG<@,@ϿNX N1c=Y%m?-('xsP@ =3c0O E~Y/ lXBfP~|(+"%W'_v߀v9yA`& A&VU p;,A<@zZ y @94# eGco]9̯Aإgp (ؓ;|$a^8u`{"P?-$"GX+ ؜˿6 BH?N# u5 ܫ#i|#Ę^q˥2= % N-9{+ JvqW&O<7J+VyEE"mc׫JC;uZHVGWMi ϺVU}ڇc"R2Kt&WS,R0 Ps \a P*0*U<-qʠV%e[r\ke~=K*6넉9K-+Xƌ/x6N93=xj:H3Abo#7Pܶ5)+\4YA},Nٳ!F|"iRųmƔ7`iU7yz{ \utȱ͸TGcg}2 nxjF{;/T{,k;#|+ڝ-|0 i_i'nn NV^~>D;?[i@n@EPxP~/GW?ReMӭ\4m+TK!0Qͤmr{_[؟Hn]o͍☨evT2Gŗ}~;,ɛ̦WW;r }CDMF4egYn (9 Ymv[ݣ^VNH(ʎ5I*Ȯ&a'Ⱥ5K caSb㵻I6Ĕ 萴Sg#ylGz+͞N9$/{|v>$ep2[-v2Y@e9q|RⓍ`<͢3B-GccYD['dx ɆB,7oP͞f('5jD"β#:<m߫ԟ2+v:#[YdQ9x& s3U4k//"((hԍD=svZk6qt5 GB^)K*(FuD-Dn(^2|ӶIbAc<39E2wOPGSٓ4 wyϦrf_ ~W1E:h-fcF^Dotl |ʃ'hW)*q"22 \ȸ/b-r4l 9# W!9*4a?'-F w}cGұekN_cKUQs2󖅲w=i"~Ģ %qT0ūA TD&^8z}uB] GVV+bdwp_]r 1}k[[J\}͂spƄpJaUje9瓀WEC;ul|R7M鼤 K$0ht >oxS!5aNg%43OӸela`A.!o`Q,KPRqX3_OPY]e:Y=]ȧn|~1&[śCʒ=693[a$"2h;W<[GO;T/+Q϶DmPPBr=%^[fHe-֖jAfd>٧7]}>Ӆ"P6w_G'lGn־n5"8DN9 1S4\{OINBA}8gP96\47SqKP1:΋2pm8}%k_B "P{x:h?ɶ.};(Ppk*9{YF4^LF#j!JYx(jH"⥾NW|IR_=\Ĕ0UfnĽ3z,\SnKXD3HU@gOW}<(kG m"CmnMYV &NP|S=jL!HaF3 jɃ=yWnQ-81iCLktܨH4BS8/Zi*D\n.OF~+Ժi0m5sq~)HP@U "+MlV$"ab͢kU{E"s@?Fwo|stUn_nԤN%@\/<[ to$ o:C}ڍOetv{[ Ù;#B^;L@f)q`b-)i"P*=N~<v.{U(/ZV3'|liH2m}{{:I[ 5=ͧ UWOLM g vʉo#!}|mQT=(_IF*uf(Bs+%U6"JvJ2؞O؈ 㙱/}@ܦB8 i{JVt}9e;HoS}RI\*]ȹ_ny$}}^TE1slf{AيS>0X^K@ݬinwMZ]Q%s 2zy>jdW*f Ծ $8R 3/ 'I˺gN&OMs\I+!>\x̍[#"wa'YHsiùKW ZxVf8- &:Yھud1a"cpщ .V)1$)ڹuEmQ37>[J^%R\pVk>7#[ $a܀els!m0e/ /cpM{*$}$r6*|`JqTZ$솁>>+nl}}Y5OWh>Ov xc1cdd{\4M*: 7kOS6gA6FHUOqKdYnʣԾ5&߽ գ0Ά )FvQ/]F$yk;@J?/2ix&X,pȺV/ =z`")PZar2BI \kZқ뇧=Lsf:$uX yS5Yw D?xA2%%n)}dQ2ד\څMbN}raq:߈0nb}bv\:1!zF2:5o4EKTƓ3s2*rv?| (-P|x6MF+4a{jyFJ:N T?Dº 5Ϗ`􍴭Bwx=X<> stream xڌt.FLl۶56Fml5ƶI~Z第5uyTEQ(`W|`aagbaaCTv[@ tvv@h IrnVv++7  &E&=Rhh rL&V@;PD3[5(h\]=<[4zkL @6wQ~srAQ|/] SݯG|.fш*1͚#qe$øDD.4Gw-q{\ͥ _clG0Ew`u`ѳY+2×UF4C*Q5cKߒԄI5%XFdUFQ5Dbkm6_-N׾fuN}~9 0 08U$/҃NJ/ru/ t8DP a=$L'J쉀u;E|97߫XKY[ .7/Vƙ49ݟrUgCJ/7+p$JH9) 8d{*#ly5+4Z9TIwO냆Bx 4/+<> ns|xE9MZ4 \/);]cxWC%ub@DA/~uΧ@4`Hhk+FÍWbjޛ` ;S;>E}M~4H*҅ AWȦu Ѻw5ӡ1(1rvv,S@~ uB=|RͶwI\`u;2A.p \_=sT#w(?g.L!(eВF[iÙGQc }Kz.CWN:)"Z]\nLεׇ&0H/?@Fiٴ;~b9[[bڷY.VB䲩7Ή <8fx?~n_Z~u.ىwFģ=i⛹xSFIJ-6OC'RB]VM!1Aۧ3RnGɟ}S[[lJaYQT1s)\zVs!/]")P<{NkƓlibcCEz{ &VAv,v@B<# m͏踃7ߪUC="G_ BpCm}~UXrlOykcU-c%+S4u Y+Pe9_i8Q̤iS6OrfUP_pSLjI^A=WjBfP-eewֲtEia\ܨˣ *+n?wIhb,<6;" oB7A,e3bdAniDG47/+U%ᖽ-x|r<މxr(ވ{xRm.AB^UW.K<;zi 4Gg[T`+;8ӵ75WVQ,$GLzyjl%J@G4!U޷=@QN6SwT7ƅ7xyYOsFLC"0*6[o}Y6 kaʼoy{dʅ_1GƵz0 ֘ JUk?WM!+]Vǹ94Tܛ)䇫Q*sUѿO?s.xҹR+2L{qΌ VL5r}?bo_(lr$ݲR`:ۉ$c \n@ҥ qt2IG#'5c m8 gG4u tQI5`ҿ͠RphBDmϨ9|bKi*v!2.]95M} n!W$.!e%c{O ^GK*+DZţQy*R{/fH^ĶijۂNwNŢ*HX z€_6aK9~ƟO$Ϋj7SlW 1-鼻Y.sNA3=_ CP~qUfT@O" mchbX3cL\=yV,/~k^%JhB|h۩NP0CސX_pOd|Ƿ %Wc#rYV"I"=?т[2:ue6@[8kvs SBtAnԤ7}׻<^b08"{\Hh-L=q pXuQS\Kۻn)R={$+/OyhV YtJ+CPliLր0P 'f eLO5\Tq/_˄#]27O d W '*L30qmc!3A17qXB&"m;s5aGhyr*LJ/˶}T)UxZ]) FY3057s(=fPT;͚R QR/zl3oa}+4F@#")͎WIr>Fi[W}!a2qg]`ȓ;xb1^JM x'mvXr83?wdDV,~|hztqN8hDŽA=#:dLǝs7/^[owToP%i$PZ Օ9zu3flaS kG_B~0VDO"Ƌe0'PN:n=kS:hGDw|q1İ IH' X-&-V?xfxCKL)v=,C!<CdiRl8Je|(%15~{зM\d Xv=@B`ϯ~%6ŞVX&*y#JđQaj(䅼8%|sإT}uP]"VEJ[zh|R|ԍnl[Է4^L'I'6!7am6xb܌tI'TB7i2-dG>\_o] }վM+ɜ~sP[(mxkNnEkY ! 2}V8e = .j:W*qLg? k)0@].iTT 5|nݏZK\*Q*PqQDȆV@xWBpLSBnKK6Ts! Dj^g?~H6o(R?^~&A@"wH#˸rLRg{7ŒߐژP6%2 (g9k{T7ô n_|7zt@;șGcP*ƴ.ԯQR햽$V;Y=PzCU^c(mԚ jhh[aZU5Tb${a@$=)~[Q1=4޻gmZaUg8W(ó;&R#7Z;~0"TZUcKQ$-`^oXİ\!/,L>"6ƲհۮTR~؝FdGg'KKQlYMsUVNm.Ȍ"q;dO{ko߰`>:5@9QK5D>AyZŤgLZw8fxϏ06Sίؕ]Scr= $ua?53R3TCV4A/Sn)q?tׂǔfeZh|D{?bGF'b):WxS:5'4fhF{̔ g_X*PS-[}­`[Qƭ">f.5!`>$i+j>iu@ yT(1sҸNЧzu@j{ȟ/Z\L[" _E *q+W)]WZ蛴0s'H&dMgXH%cKҠLKaw;<eċ8a#aE*Ĕ$ls ٥vdGaT̐vs>UTlQ%w}7E<64u6 bOpuV_hB-$Ç p 8%T}K-~6'a"Bxܕ'\8ɡYet:薲-dggrH8tk`+Նo7RwkxYgܻ %+c~wbl&0p{C1U kŽ)04zg%sE͘j,Խ뱁'h!dJJvMŻ="Yw9M֥hWn6)X :\r>,^GTפHYG.l,Nesޑ]7Z(WĶOK Is)} AF*?pʪQ"0X$.y\7*toqQsu&ǏKHw}$ `K,Z9zrE'1%Lˠ'*X5T5±l>mZBT\t/ٴ8Y{tJh] jђB$ G %pzv:Tyf (}Yü,9p, :罽;Ή`XHCGn smE^:<}PuCe0@N uk3k!I̅fe)tq5$CN|"EiuSrWj$}JE9U`ә1ݾw@bSC8pm0! +81}XJ[,т5@dMY֛m1MV\9Ρk H#FoRfS5 vsا!4:Sm#jj.n .J.}Z[6w -xkFK d[c4Ӵeg{N=}/ESdlXol` LN] jb4AôOpShu۽sr0a; Ȱ`NU/&W/[1l|k: Jox6zDT%/nma OhR,_3e_{D%~*ޱ}@1qk CWԆ->f,HRZnV#Vָj]yK2K1i+Ժq ,4fZQΎ>|~+U͙{Pa=$<9WC j avbA.voeUVF4LƇb T%m6H=Ǚ}Zg½'Dc I6y`dj۔!#lSG=Ӿ3F p08dšaIh!1%"JHT.&/l2 *yL'eo5mOr+K V'Gb 0 裲+K_,7kmt/X|[߮!znt7o#_&7< B6␌Tn zO']\R8Eu0+4,(i /6V -4N}\3J1;Ɗ|S8i ӣ07N0BI%sP]lEWTcZz&덛Ϧ)atS)f{0vW1J[&oXbK|p ŁѦӚh5:5mW r=yӸ k?#$|O w Vt㮋jfi{ m{s.T,jRx~z}2C,%m ]wlBM Lނgqi-8D6vR`z\[xsױ8@/=|^N;: r8H; FnAW}#t1AbQL阈NVl쾯!-C$ۙಿ7w : 8hF=*lL2kh/k6=@ º8b%E <^fHs#o4y1ڋv4L`9w_L//`1cZ02Nfmإc'lOCYHd!j .l«ء!;ЈEl qQ = tÄT[<@E$g*R9~or`;bCn=B}í,B0iImxu0Uo8)`|$ k@Z(w6A'[ v8&)JU1/OrY.hXyͦʺ0 q|F`|'[|>ohr/uCd Rj :tJނ"}q*' t`+*P`9K:ˇ4y_﮺d1U0scP21Ui}sSWC-h6P85 Sg?}Ϙ}')FE>",^.h~OYYLRYNG>Bp"$&#tRk{o^5/Z/C{NxnS=TQ&f[CS!Om)՝Hi8t' Uc>^ݕ LH_;v87!1ݦxorw_ p)0!/zCd%S^(N^W-5 nIgD훊5|?Ԑ9Nğu֝˷[֮&; ]|DʪFҮ=G?ӥN&DC[IТtm)VDIpTŝ2Gf&#kg _ 6& ]+(=4[d6~neU2p!bq#kP z_\Z ZD'Dѕ.ٵ6?uiO&&FC{> 8UKTG@B Q[qz{Xu*'0:nr2vˊ:R297BNͩLJIhޠ2g v#,bQ5^&|U<#ˉX[1"RN[cQy`P G}'&uйle A ?_"m镛 u`2k>*5yKE"FRE{u7H\pх^oqmOfԆ)~t/5j,ak1|N&sIwejµ=`G/@xnUG23=9= e-yDgD@CȱDt/n(UU:WFbHFb6Ô[JRsT>)/BwPm+FhV@#%lnNciH"&錌pdQ|ȏH* ې^_j%TYjBX o#N;.}Ȳj|ϒKٛ3jx}M:$g%KW=y0V+66c­(oEZCHƬsvtxu/$FQ8[w37ּ"*æ+njVV<|%"8*hboaK8B^'fsMyt21-jg9Ia0[LDy4b#d.prKDW_3YھWZ62IQ\ᵜV?@&},ZVh֍$#$KA5qēVuW`&+;QcZ(ʥJ+/kOD!bQ]kxzFG8!BSv/vFZ1 ET ƌؒ;çK{:NmBS\HE )3?u\wl srQp񣾐2{CO{av;w]ۣN= ] 9ems k3CfVwjhh1DzaZ-CjaUy! ña,aHk93\4zvҡv"r»_G3eaaqyJpnӽ9s&R01:Bļ{?O,q~MAI:A{Vͷ )I0P(;WOWD\$ż<>p8͖(HYF"Ÿ}G:mV?jBA OXw*+2S$3jx%dT3Òh'PyK{Dux"]4{%ˆnThbvARߺ#!5 'lzguY>Jֹ5b`du,䆮nE-SEgESN1Ii# 3Wy :u2!բ&hn865a'74e#ͨcX+tE k׾Nvʜ|ֶ4"5O w r3dy?v׏B+B}G| 7y볞Om +6EL- 9u6dqz2E:L3Jb|H{fMj< vX_[K|=̏R7fr)hYH_<9aZz1Qtٸ/,̛#amQ_ (/aƼ-FGlN cE*cj_ᢠ.i~D4 Yq<EuL}-Ky(<>751$i arjkf`H)-]+t `AR`z4Å=2YNft@Ђle)3mAT~&F)If5ZA)|;]W:aψ5.t_^sjIN`s-FG,M>vMo': Y zd/! > Y@Dv{;b#x?OҦQA]Wk<7d_ny[ܒ5(a_w C$`jRT '(dqvTbPyD0jV@¬κ q2{EeN-kى A[G߮UwZYZ,hc /[ >=7r3%y9 *Tks~6UD^8 Wq9 q4aE3A_?]a/˽wC_< c xބlڬrrHU]De'İLJeO U^ƣp[G~֡I%ʽ,BTGIװq:~:*BѥxtT(RHY a/rj!:9s䜬^fTm$4-ڛun",HE=$X"zu64[-AUl\-(8fXw{~8)oZREԗ ,Z(P8\r-;<ԞM9Q+BI=Zz6Shx& ^2NhB֭ TiH4 2+ Cv_t45z{δ^Ch6I1Av4SD $nK ) +4qòЁ$f{Z"tlڟ2^9w]03"d+UP. 9R]KOp}|` %m`Ƽ9 g`+ӳ.ӆh+?-WeDv/?@>3ddrײcQMcg2FOp*=ѸU 9pkupj( aj~Iz+א(KdlXCʢ`ZcřT4Y ) $玤#v݄Rڼt6CKY4CiTTKEqkccX;$.C/rn8L(q{pY^32c,Tni`Zߕ9:Z;C ,Z4FE(-ax"iT7[>IԮbOUhɈC,AT'羖PNe%_^Y 7[E`Iٮ9v7, `Lqvvrֶጔ헇1zI ?/Կ ,纠FT"ƥge{t}l>0}Dzng~P~=t]u1[Wq٦d@+ϧ~:d%WU8裺Ȱ++"p K7 ȃ(BhFXGP4z\<\aiwM}OZmQuZt{O|GcI џ3dѲ1(b+1]?!t6!mV*FKpl#8#sqZe A5_}$JOB@>a \@ttkJ$Ӟuud.K (m4tx;wvd;nkX39gfJ~ ̳ˏg7 CY@oSWQy:ZR`)rsnAX}{ 7_dXy&&y[J#}jrKI_KW,`j. 4ȹgGBtxԜqC3}al}hEbm f{eWf0^ U(;JS3u րO8Ldg fW  YX-6_=`5>6E4 yIXG1MQc>90TiO-ʠX9hM8IrK"t/n{AG7D֨'}FVĮ tj~rot:{?ʵ]hhKYMsb}oJ0eɞVsEP>k88\XZzOrFJS74AP. x]~Q^ve&IMڐ.oCnט ~4yd1[BEyQ2[gV%Z>WubcWY$vڳޥ|Fxe3DDhD5QW+zQR۴sim6:ʛ:uuopZS4ͺ!Y{  ?sSdewQ'A ==o{4%7ԺiK)R͉S Z:r*M;ű=g_3'?0C(R**ñ*D 1;-. v"?|,]U(i` .U JMes)[=l(gG,ϊ$V{tʶo~'yf* tL\ޭ As95]K*n]êcxblky'Lh! 밒'㥹R''* OY~IJAؚ j00t;fI~V} Q;öKE}_"W*_rCrJ yB~-H~x(|Iz@Ql7GfI3Gk0dN5_h#,!|SAA!?=U--ջK AQgk7g,Ja%}ӗiz^RĝD~K*Sy9,/xoWMvh9QgRpfo8G򤸲Z-UFӗ%7&.+\kԗԽ?B튙AJj=9E }) Z6ɠo{TMcSqLT*yq,OMOhN2of*cz#uMd5 ˋMP!EћQ 1.9Kaug!QZJ^17I&U9'5t|OteLu0$Ԛ <.]nY>wwx n9Dy j4ESzohJу*۷My^sNEXD eP`z)x5{[.9G5WS"˕_7f#XQ=dفRzGEnzKCKT `jsUK1əЧ9WzE{,ן{y xš\Ϥ(cuLJj9aLh+NOq0jIl)+0Zloseha.)N̿mz‚g4ݐӁc_ƛ(4}~[GRjvwwwsGI{3CiQS-j76`DFpZ TOEMS*I|Z=E1]kП@v~Gue.a` L郌ԙ ]w*L!n?hңKJr{ w Mz_17L:TUڡ$Y6W# C%+L(3RGz]µAQb5 Iq>bC] !cD~ )bȯM(lsJCBV8b"a xmI8ml$=wA2>`֯=ȵXW,`ȏH^/b-" {P 9]vGه;wx2wIFwϴw_ zŲg?qukZ!g0jа)*=i(4W)l$HxTv *"4/.S)  GZjaUo\s,Vfe7_-!)iI3_Rt.V*˹*YH=pE@bKTc[pQff )"U & @1":<ٞ{aKYW n^P^\ѝPr5 GC22v4)\rY54MUc)u~X|@V[HhB~u%a7c{c#OWiWKm5l@쮌mVDU" o:oHKPB+dV֮+IބH" `.b 58۶>@oi3NqPͅ xxpxO~vNq ԚGu}aFDDA x@I$U\ ImKu,(E{$РPم0G/vT kfمiKXO TCO rx}d>Jˬ+~$ JF`Mûn;3PF kPV7Њd { |CCXl+K:S`jS(5[Tu@GҪ8Tf=]Kؐj[0Loqsr6|nWba)CVB3z9K/ݱ +>a"*vfR eB;t5S('OKI$ߓԃu݃MhH*ډg7Q3{㠶-İ4ʛ-3;EGaflw_Av8$ؐ۾)>o̧&iTNm>miQ/bN\u*Erd|JD=7C/i qdsN9nrGR6D9Hu'^Wx=PoFr_p㒜˧044 Sl*TS TG]AtKXzp  Z=ӆ;ZJr[?wLm]k$}J4;#Kx" Vh1-,w6CШ4jWURZԗglvw(: c|:QCxڪXqi+C9{ dmw.yy\*/#_bao~FS-$HAcqy0*_Q;|+K'@DRX`zITxDjj/5q"k0+{LdS f8oOW/7oc%GW|]F񏶙|'#܉zbO UP?kGIdCƸmb?G=eK@ixBBp梑°y6a!e`"[|9@f&,$/4L͙[=&K3E U_Vyw1ǂm ʂ%+g ̑I$v37ŏ@30pE"r9EeѨu- g @v![H7JS~ Wu xWKh(5!O^T:x8*Va|We> stream xڌveTٲ- n wu< Npw};v~,VUD^N(jkDD QVVfb02322#+;YhU6r8dN S[`fdC[n1@ iktD sw075s?(L\\F6'35D#+9((?99q30X;:Q\͝@G ;m5flM\ hrr1:@$rv@6<&z/g##[k;wsS '*MD 01mh`h 7p1020@T@`?:999;[Β7 "6B@'G ;@wg-ml]m<`scߩ;1ؘ;%c!lnFf Qvd-igk067 x:N@o+!01͍@Ss? 1o s7# Ogƶ6VhiUI9G-(hcf1ؙ8\&70O r1p/P?sBo.Y[P]r5o~1:[YeA?&l@-[PoS5S,46wZ 'XؘZoX6[{mW @fd Z'K*bcdk{bfcx2W?ml@.P[W`-  ` " F`X E @~ 3A(A?ZB9+o9-B5@ qߗ`dU_@u1O4 \@q_ն>db7BmO&♹ۙ _TCAP]Jwу*L e?elm{+оd%_^LLAg؁6+ɟz2G_ef`t=`2eKfnYArWb ζN@c]HZ3]Uj6#hs((6G+GQB(h289ܝ\mp]˿  y`pѻ '8k 9;׃ZQ ݀FK F ~FR~A'4bKxk^^n 1TNGfnK" 4QiBR}e'B 2\Sݹ!F)wx7Y.oEh!|>:p(`G' .3HDعMQq/ Yxp ϊS}A6'ۅ" +$OCТZĤx@k1U({^.|Ҡ j/ -"pϬVegS[6Rd_һOUԸO=s5iZH+(l&^a+<+b^ҷ+,ײ6(G4^b ZޔdkLRA(U-[V;Ǔ4S+`.:ߧaQM[Vl7N-?o}jzYu%dx%wYS3=É>kiPh6Q2  |V@p+]M aރ.T!뚼NʀWu`ցjleJŃ<'_t]^{S#kX'#qVл #VF=(ƻDįt ɣv`})h@1eD8ch?n e'Am"JK5]ވ #^tJ'7X0`">|U'Bᔳօc``3j=. S"{k Zڹ1]3ĻDd8ZrS{iY??tic6 `pY|R.eYzX[G$+p+-CUY~Ty:}Xoo5_ÎưpgEۋ r5-4 jZF߰gg$:i  6RbCeB;UØE)e49Cm(a NBJs#f3B3!O(kM߭ڲkyf?{}4l I-EZP-`.wYg NЄы&_jt2f.p> =FiҤuqa*}C.Eor0\ ;橆}/IrןiRByk×E6塪'CdE-GgDiKl&qPhupHqPa&Y( l;z>Ӥ,7n|ɠu=yZ{m|v@ԗ$UxtQKmw_O_epjAY;]Qr5Z[u$r45^7Qۖ9 ,QdymҦm3N ;剪où3InvE I$vCՆ%VK+}&-}sb~+zLuOɊ(gT>St7־ցm;(4 z坐uf8n"XoZfz)Ikq;Óqhb$;醥7n/'hg|Z(?ܥ2q\c8#[3pXԂi`M^"?YAG//&JL4a6|8E"?mE)b5XN cTl$.RƲL+ۨ.;p#=&^^BDQ&Rbcl=YxP3켱UĖU D-6ez7G`{۹\W4d?1ߨHuzTim|lRp90pL~ciCa8v%o,xj۟M$8iֺp\$BHE[_ X9'6F;S(t vxsf~RIj $[BUC풭]USL^^MW!Z ;A7jԫIs39[#n#!l($xX0" ]~lj/hGΑ-gɈWO!%ܳ }mqc_N}t\$8|ys|D9D0[wB?i]q agF{}>'2B-+AV_5 /U 0e7x0[EB"Rb]}q'#Z7Kt胄 )6ACq2\͆_Y%[/>i=}i~J67چsd[.b}4?'㳺$toRS/΀ %?6cT Iq\ĕ糤`,Ô0q.'0pgo6,[U]lM'yP3$-eGx653" uG±-)?WV :/e5f6A( ,y!9XWo7Γ#8(6pn ar ,>+X\uM3+6WԆnT/QB[I I]17h!;%jUw,! aEMSłdm^Lv?'?HV2?^.![X_Z·(h 0dۖ*kEhګ;g翆R ٙSԱ5YRXVbBkZbd,~-؅ wv,X+V8i1(H,iRhϻmnQFΘS{)quؼġ1C{r(8* # y'z s,f6"<º9sD*+aG!Hvyl\7_\E!(nicq,?CY"*'\e7ZyzL<'n>a8ҙ?]FYk +G$Wioaiр@Y]N5I lXP_ OO".2ҐHEQk ˞ XZUøB -^3j^iZ1zzf[ ;b(Z |+w&s9";қy9TXfόp O=b3[y*<cԍBrᐮG&vx9QB|iR^"sq/WLQ8>}y$ ?Z<TfND業= 82z,hf@݇cTc*HJ h!M=c:r߹_]5fkF2ҕtۗڧ+8LTZj!U}cwg)4-x{xoX@'rlDӵ= e݈ !zVfט" 43 yrURf-gÙy^!v4ֱ>Y~+Ϙ6 L{ŀ\ܥ 'E͆doX1ˉtW$|zH?>lioQMf~yP}#TRعBt{ҏXQ:n`KoL3-p!, Y_I480O吟LrG 6WLz7$Aj&0_ʆmلؖ\)]r]Ziَ-KSF/0dE>VG37[SEY W:۶ Jf-QmiI\x##*#ax-|BޅC^(ӶTEc(s\`IRݿWO$c|ds".[R͌\qr O }KFcrAw&|˕9Sy9 BKWi=qOxPjRZ:>C506 @jZa-t >:䌠/ֱBԣ݄" oMEOAPMK}Q1lL {M,6F;ۛh@M! /D8=K_o8O@ȑTuTL3Ã'r:3?(F,37 ML?)0Ba9V[ӌc[IUAʈz?X-?巘M7d&t'J98dDNoClu[`Es]9sZ~7q9Ho d*sks{=9żՎγɣ"[]\+*t;Zw1rf;oG[ 6FաuU*4e*,bW() tp(8)cP fÀCnyv:{淡|UcO4Ef8oi!:FBYd&J#{a^xy UP3r,.B%IYڱ̘ka[O.vW T6좏]a&a࣬Fۓ{"+pV<-﮳%g-@0BB5% ȩ]~QnK@~*?R< c=`œ҈ #vR QeXPk:t&8 fk)SSzt| w^ pѬE7G}yU̍q&HMKޮÄ F*&2H8}II;!M,wMmnV! 5j3hib>K1&f7fnW>Gx(#NUJ.Z2%?c c&׀wƳiIOMX2[7򭻛Z| \)6)8Lp(ݱj-0 Zʛe-Bk#`&QXr]S!{>cb BG2w_RV>ЪLs!}.iQFq|\vxXn zխ C\NwjU,~bg/7Я i{͗:$`9=UW^IJ8F5Fh+SM-iֵ~Tk4+XF7u٧aWd71XZs=Yh*OkJ]gG&!_>_);oEa"MK(,)[^ݠUR\zB<,k1'2 %"{vR.,jټza2*e 2\ϓ6Rޏ<_iUʰ>|ͨ,jG_(Y핚yK`/ۋks,BTrxӛX,eMĈk+_#y1X }-by# G{ (V"BJK>gѪk\tqCg@dU綦)?5U;fQTqjjN*~$@+?; #4{@z"Hjx;7ӑ4.GޗݬY˜Ydw[]#6NG7i'DڄY|tȔ #G-gw{d_s4F7&529i^䏣|+fd b9^xui3&02sYKyhě1JznaG:}v:j\L>Ķ`Kw$ 64JqDwb6'YkX'dv[j_L嶎rC;iB[Tޑ=\w_&?'b\+P+DIuoc? )Uh^}H򶿹ɂ9L_<7r꛺#^?uЯh"-UsmiÙwߚFֈ~ҚH鼄['Q/Px@,>18@Y̔򇹬ϵVPsw;2nt/CU6st!BQYfmR9\-qdj]㓂d#\ $;Ef4kPIRW$!abZ ~2e.p\sκqɱ;xBNeA)QU]w'`ml; 2=M)dH:]mB?G џ^mM[#T!g ؈48_U٫B잯qY#ce{:9'S9JS]>c/S,Ī`~YOZjpE8u7'֊b}׏D ߈yۚtw45#OXTuy~]LIV})4$AfU/irD߂YClN$ϵ-x+lg>jE|%R)"yc} giUsNktG9H~vܰp+C>7nw G \?H*oG2W%-o f=r{QΒ+ 0NSDŹΤsz ՙ@1ޖIX4̮3"_Ta iM8ơPaQ&o~5T/ $}s-/vqdqc7肳DHJ|9V}! r K]+<+Nbmx UG\Si|׺yjOk1Mnڤ0{l"UJՇ-;Fzy$7&TT%s d#F5IB~ͽ$ $ -)'܉?0BQ}h<{zE;4 %ۣmT;7\@Olw7=3wuξիau{VwjFo/{:;s|2s8no'CRz{6O𘪭FQ?Κ!Mc"NNq]Vvm@lnErT;8CKoT_0jl";>(n"ܦUCl%wعpp|,qQϱ"')Drv(,(Ut^bTm'E~^uZ=w݊)@J灾 /D4!f%ČVLpᑣ߳Z+=q[!vf p1 . )dPPAƦS6<Ĩ,ߕ?s'}ujx؋:;VDoF8i͢-j1 |Ob2m2E!,Kbd (ڱʶ.dlb/ΥmyKD!nF;NHq(b@Kʗ3~˔$|h|]ϴgq/YvrX=)&΀R$ߊ^{H͡>Tumu)SdPx(1nӦ%,+%9.%qI\jBiN=vPD?ƚB~}C'GDt%D۳ZX9vM`0pCG,@^ԢK-&yi JfO7TtpOIJdz+`BEGkQx8sd)k0d _Q(Ղ~0/Vn[;33jsdr ِ5p{6Se*c47{Y)"=-hX Nܠ":#(mW9Emōo;W~=R,]wϒ8wx^'-6;L*IFJ5)xB#їxhH֡?_mq . G$MOQfsP>/,H4) Q+8 g28 ɗ!!hr\Fv.זF8,a40Ӝ'A0CsD{Uq _YAK Kʿb})+ zK&"ӴxnP^XNΚ6u{< ׼R&-"aJF{%ph1CT ȥ 3Hz&FddT,քirZ4Ǭށ"R>i {OۨY(UQ[0'wq<÷}# ,ZnǑX7"j5i amsRJF\G;#wN/#>62e|kWnWPH5֚r !~[WaRaeHICy xqtYFWdlYsNȽ>keg5o3,TU(uH CWԎnByEA蜶AO&?Tf/9v0^kwxHjG[GHJɞ>wm#5@{ժe"亏k l賌y7kc5|ϩoVNT& Dp{/* ̣u 54{ƒOS28g+m R1a@@| 잫5Ύ-}L6qES.v|n~͎kDI32iSg# U"NFk8dfVbYA;xD%!=a~+N'>w!OD u[nSެoWOQ$%;j>m]M.pԚeRL =(,WCnჇ+2zzcz,.nG\qB8k{9Е\Ջ%/[^pu0|E77[i d&/}~fruiU'î58؛VDm?-ack_?3KcoAU.uwU8zMkWIۧ.L]siq^w8>r us,g| (bOqDD>W~Az;=i1И"ĮP#m^SP{#2 :3J7U: sLI%sS8N`&H'X1/ ס{QjU E6d2YJ[?yj!BJ/k:a(|ȑ׏V#ϩa$լŴz;⣎&z $~qW3zQkFwɮxx!a;;GPٽC]C|I @Oz\~#}W1=?݆SgKX|c2SU0x3V`:  j鼧b= oɝ$j1-u2F<5īl5Iz.79D@Ǥ\uAs8}܌Kck3̋^,YgQ|!}O8i0vd@ODv1(Jz| o` ;ܟCL \iHN=]s>2vOϚ[xK4CWIϬ\6 q~%6ha=Aa*IPdkJr|O?mbv}4SO&7TMx7Q|Tm۱}V FQ[;ypi=LI;Qli%WC0 xZO|"] C\-NߍOf64X.g99maǪ]XiNōkҷFbQ|'nЎ5]{pSUz:AUs BgޕPFV0gt=ӟOyO_{bv?W{vEB,{[QjN./6=+l2K~\S zcD-ICc\脕H\QE|`gYyLx 6\c5b>;ʱ÷L)Z",)K%ЙP};)~V]}ړ>a`H0I{-mJ9 7R8 \YM{wy.<iو=w-aFr~F^?qSg?*Nd4B~f3ѣn*-ɿWiH4#ZmYh^= tjn=jwip&>` >h2,qOBʊD8h0nbfb-eh2l<}`~Y ɓdv;m{qwUDQvܭDb\ ~tMڴX*`Ň:,!IM*.2(:?j"aX6T#f:[] k!h]M/g4hn zڋ֢vO~Nl}Ȇ>6䝞Ox{(X8],HD黤teP$0jh|ygV)khWf7ՎtsO V[I|HoGv(Tlӓ*R*.|60zM(/o-гUVr݉umJVŁjbb&J ёibF~c c8y#R3i)/p;1:Ӗ3ͥ cԧ/Gw5r#:4hR#!~]NE6$% Ey;+) UwM䬽pp1[P:%RޭrNp R:WjٿO7T/8UQ#ZAS6&;${5.wEhy>Y4­=g`tߒ1"ŔRM4kbw]h,$ t}1VQ|wO?*gae \ƍJ-C ;s'ߢF!S'6'C)m:2WMicf[V6|f/8jJG^P?T[ORmkD*2 .D爠[hG)u1y.e{Ի\'ZfM5ݖ8j0z-nZ-/]}av.VhwtJ F)F (z΂ AjqI|j_诟L0(!)ʊٶ.o0\uisڷY=׻JqaQbJŖ;r5Š!(gh^WA.;&6"g 98.C1/swm̯\=sR!LN1P]|ו+,eE'; 詂_+Y2BZiD0i5Я^Շ)60ڑU,6-qg(;j1j2;XW-mah3U8uP;xQ[8 JJg endstream endobj 78 0 obj << /Length1 1788 /Length2 12854 /Length3 0 /Length 13983 /Filter /FlateDecode >> stream xڍPҀ ]Bp`p\wB, zr{}{EG&adurqsT89\(tt`=?r:]Q_R 3ȫL jPtpr8888\1trHy-*E'G +?F & ? +b9f0h9YA q===؝\EX` @rYhj5v: /x؃-@n. Wkv2@_p{7`d!^VfnNff`{3W?K7Jh^;?7 W3č lG?¼G}`W{\;G'OG6,ݝ:`w6"d CKr,l$vCڃ3 ? }_BX- s5b_z`/;p/ tr+JKꩱN^_6n^/q/_G+'_從J{^&Ruz\A7x<?HO=_?z3y]pzVWd vwZ6H8ZN4';_r, dX55u7{#H tKfa*o^G '?`jzׯ |JKן ;:A^]=\QX>^P_JC?$9@T^TK?jCI?N h/|/|Mb/|b/|-z66?2k)@k2kdyCr-]]_߿?wu> eiB8Զ>Vԓmg}<Io[cO#XwADnWK>_zҗx+-V)#7ew]nSnGKkaK&BV7-7U6' M`&b`B;%:%$ffh'~'ܥq\.go;+]f~#Q2m=$0_vg4w%F#=u.i"?;+@},˜᧱0sti[Rv7/CyoE,2j"zJ~u#In3EcMZ c!q`VN@XD{IHNaG<*fMn!79Ap~ڷ`u' xUwLlUiJJ4julKh"Ǻ_̏٧zi/a:u߬P+'\b-X+S}#zNŢ*>oZgsy:L!PkT#w73,p 1# ^[:c50I%w^Dt=AnM+T  c9e?9hq kUBS@޾VśU(:U;,\ϖфgY*PmPh`^]jZF9 ?͛8Ew^d*nMCD.tH" MtO1| e%rSn\|TX_*~cI8@Ner^m(됛 ,^qZ+ H}>fCMNan3qp-W(NL+˕ : ;wAex}bg]#4 Boۛx[A{ݭ¶[!,NT{%LIop?[ ʣ3SNҲ'%J~ty˝2.MbLMVT&V^rV&J%CT"}t(/-|c_}eLYSfm=ڑc"P$u~YBm>9PSH|t,í) Arp`D4&俄)TY2ݎdou^v/;{ @<' LŬr= 2vi3hHF }ZS8ݳꦴWboMxF km0?MqJm͹@[d1k)^w"D+a$_YsIR˙͗~N#Y,|żn] ?j)ZOݬ΢jjnz 0 !!O,R'`4Vt`JCDDǝ5c=!"bҌgh"=AK>y8$h/XhԿ0.K>Q1iҍ ,ٖ!&Q:g6 hIQ}Tj}~_uhX,M8o)Jve"H]%Ba";gٰY , _sGLpvJp-qUkZcՃ&=6,ֱ+㚗߉辡-3Ѷ.Q>Zlr;12X(\UmCתzw]*F9VsbM^FSGt-O=b!xfp_<~WU<HŲZ"V[1h'ȅ>| e*8ɒZu,OAt9%ֵ/v>opBygS FȖ%cAAwM/6v"ZYv5C!K L-abІPAfLی[w8/|=OP_ƌ+k*Sc H@}Swo?w2mqYڎDǡDq2ľ\_;?]7U *5J`&'xbqŠOkmz=ntY=רyx̫)nXٙsTQ4s"ҁYMjGK^kon ɜ.A6婽DoLWH@7zkbX~`"_yd&~źY_}j2qu 0y?,9e .],D?&kvby1_s5G:7XK (At zAV,LuwhOv|1<,n^x۶Ll(ݔ{~oA]FjM:=k3S) <S*gfr j G⸏ gCE4fO1/-ʊtj:{$HM?"fB½aYve\zEUo=O̥=o0e}C'VOw>Sݱ5tTPCM zK3MW-Z\q萦Mcq%Ή\7dz͉-ƺ_tR Y2c7򻨾R9X7 d uTk-)Q7|=p t/͎hDZk {V>ߔ>-) < %Y ^mɛΙ~ǾD_32@^G&x`U~_nfFZEo* #HEj7< F\k:84N]UTb*`G+H ظ׭m.NK64NYKWVS ͂4odq+yPG; 藵SMPa>gNň:#O>屒Mb\;$y*lIMS(`!ު)$UdQ u&Al?#w<44Np%+PyFSEI]}%~@bx,oz8Jgɝ=tڏyTq}]>[ 5KޣG8WxCQoo{'^> N@gՕWxasUJw|Ievgϩ"͔JΟۇor91.CF QkZUWD`[0fhjZUAXtC{&rgqGV^2yqQ/lƒ%}3.{8Xw'c" noE ?1DqoYe8^xYɛ< bx$h(^ -'ecAe=T`HbrC4o54)s.*TtậT~\)/JGZ睟}z R&-kq -WlVP]Jj%5xk `|^)k+۩,$ӽO?[9F4 cl=Q,wG:`N9WP}d4]3 \_}gFG)IUpth4(HwSXzeF*1UxS55+__:|N,tk"Olw RV5b]rfYݷ 1o4#)+Bx=lBqpU6jRkA`*JϊsAB[GV+ioJd&uhX.(7C6 RjpSYǔ/ Åz'vָ.Gi(6|' ƶd^13b Ijas} N;f(~p )sEa`L0Z5(<"+h1Ls[\aaF6e+\s8aōޕZpcb2-]_}ʼn9x`$8X=DزOiC9]c: 2`eg3&lΑ5OF*CrZwY5`yh$:Ǖ"()T &9e'$ZZPN$5Hr DmC@euBLAJ:c yGF)uhiWHd2dʼHl~L={y^D+]0M B 69=؟ ]$Ei_1  J)tFn|^IYi:DP9آ3]˪R?anӧ[MD058|))J*6dnsfsrMI nԎjílѬ.grORs#Q?ӗGo#n{r7M˳پ>SoHNy ,Ic:)W6۪]t& 9${|01d5moԶ‡X ~h3 ?k8&Ѯ>O%b@-}'+Fب%Uhd8` KDNOPKȖ.m"bHH]V\#}hJoC)@ZxI$g\O؇=zر"'N>Q3:47wᮿIzTv K{3yHdΕa: ='}=Cy^ ?%؃[pzc1mDvx) U,YNi$dd6ᅽ&ӮMZk0)"ʢ_(HgyGJO۳U8'&7yQNV ?~VMbdUڐ "˯&/(ǯk{7z-1FJٮ3غ#Hmb:Z #P~pg+زss֞x~+v]p /cm O|Uƻ"Q'%'B3:`/ !e,JS):0X?YN$EyRyfy]#>+(ocQ?ɰC4<-9_/g ͆7rաyj,I0V(@xJL 2{Te=beiJPhtX}l̞ٗSC OSb ZXOqU8Ed48Y>O%"NOq*kLq6ߓyEh}:˭-"hwӽ4+^FYmg [+ wWXjkg H^ST_I~R:3"=P~=#J" "[_Aĉe86^·ftw;-Xd7qWUClwzԼ8 m0w`+(EQ 95 wMӠgMEYkovw _nόr$Ԛ13q}]bH/׷`z.д<!OXϏ{n",.'rRjQzhKe'9jQ} Q/WIg$ZmK¡ %sy֎U},fq#gG"Ɏ*{eߐȸ",<~ZT;u\|˄^%I_xS';g%dtCo[вW Cn֬PϮsPv 1>3gїZKgЎDQLwH|ey{kC R K&-T4Yh~cMTwd!WՃ! I)#~7)R,ӟ%lϦ'(: qMk{2`NX-MUHL[K)(@Lx?`.Rs$]"oZٲ#J~ȱj.)LT<y>*jbFH`zC:Ν s\'eDxf}}{ {=7<{ߙ[/]?s,e"_V s2scQ|ZE>򊞷6Te9 r棛!MckQ:^v'H{z! FO7]﯐qVRqEe~ o+b Dⓝs|hQy 5wTG۬ou119.(]Fɻc8g>u|AAKIBV4*uk#sF 7^NƩ]uc<>% Xc".>/$m-'rR& X$fM =AX24o HNtBqzd'7 qvkaP jl씼5^h+~x BO $H:LsRL!MM0QoMS˳`H⯽[y#su֤L߳MaOJ>~iJȓf^`!DBWT}k(r ꠏn{u tʸ~Ɵ gQ[o{N֙~u0W@cS7[# i9XzEɢvfG0R\>è~~ش,D= :*[LR@!i n(+N :×;q?mgzu蟀A$:J|tYYDi ]Tzg'Bb!mk~.u6T-UȕŌ),,Oy)Mi߰(hqrd7S?ުd鮗N X|]:c5 L'b5U&AOzߦi{+r^6ImǒS35rȌ$IP;;'&ggv21AAߎ!X1R1Ah8W{Z9x]yz ]]#Bќ :gNdRZ2:A[#+qc2Ă΃v:~Pu,K' KS~xo?Zt&-PHaH9Tz1`cJzSIPn`0jFWMN%3⠭PZaZhǿ8D@i [eky*]!4#=ɀ3j~_/;-?<)zrf`<,' 9޿ >aYbw$cv]Edg?ڏæ^ bQ}֧g;Z#ΥPu-3'WJ,TTnp~|g*M:X}9ϤW\`eWGqC-Wf*s#eoZS)m/d[xvK*չ_:2vV7^ԋӲf(c*)wV"$T}fr'/xoElEAmWg@*o۬JN8X>( !M}p4vJ%4da"M!,I!C w=g|?N-VSDM! +K5\Ra^'˲Kr[sEʀM~8Qjhe#mxqV!#Ce̳x=FBF/$B6F+n r(YK͆#Z5ɧ 8'̐K xL4ϧQ•_ c?ٔ~L˵ HSWV|[rĄ3<%ydugNCjoB_{ ɘu.^in7RkZqU4u`RsA.w|:VGX!=+p}^1 !cb9(2 *Q9d% gJM6i{ǘ$J8^2UC!ڏϜwEx mX"~ά!wiC7J^Bޤc+)0N\Cq$U '2UԂAݰ_IcN)~iMC"p2]jI$+'n`·?, 0?ƐYk5@:v-4{A&&] -Jt)&<(hg0C@͠O2,0^Jq[nc#&\A;I.X}Iaci>X3nyҟX.g0s>wWfIqr{Qŗ*{0o78EoUP:8Z)ڇDB:Ժ"+n9Ur5D[lKQ2'\pbsaXts }noRbm[F^sTXAĮ-N^u_a CmǍ7V\DuUw~(*}`mZR*JR[oF$܈ ֡#GЄWUՌF~JY ) "Y[G1]{&ͼ<*!X(UDCGP֕;o=EnYgرpQdJ9J+Cf߷u۴zCT^:K@Ub6-L.sSUca"B>غ03.s6}R>ϐrW.6iA=#z%\;>ǵX} c)EoDxkݲksB99+M1OxC;\}JDV]{>6iOZRZB\ClUK-zEl.e1tϨ. OC3>2!ݑqڊ5:Sa#Ѯuі'FR7Rwi4;->d8=RHAO$0ՆDc,EN܅-Ą+@ƧQ1t QCj]'o2 e{b ;0LacI¶Z>%TX/U*"=E [֟~QXu^xzc!3l h*;1"Mtw$tx[#p>u 4^[VV< I=+.Ơ V==L4X Ҫ`OOW~Uڅ]OaeUsDj|hny,a^ ¼Q9RQ`=WҬ3]DmDU7nK;Dȅmq`=-g+׎3N5 Ew 5 v4tWO'U1h rf)ImY>q%rQoR^Uu=5?@1(\:;"SmPּϚtEL󭡑k8XOl~goEyHVew.Td=Gfniޝ*v8Zx~J:e{gpsL,8 xfhϛ~rI2iyB!af18dHX0L_xkxUd9Jw-hix!,M9 R@sG2Vٖh?XȟO"Ȳhxyll$/;. ѻ3qcSmRE|ǕR^U>%3>^Stg&@=Pr;YzxI[<g2$Sw2әm 5 :3 IX 7]zVF׿URwbiу"Lݺ{h88`ڹR4'$vQT#˿ݵ Y`1?~=!8-oT{O`3f,ũ wC#mx*ue0J"PwNxO؏,zEu9N)>@'f8`Ne!Ib HȄ9.bY^yl$ :ɟ T¬ޯP䍆ݻbVBTڽL$a'J\ELҺlo#BᳯUYBZݬ+%G?R~h g)E.g#5YFzQC7ǕP<D-J.Rar. l TQA%pY2O! mâYBMWw ;eq?,MWmB, 9CЯ'!v\ϥ$|5%Uxdsh ϐtB;/a{gby|uLFW~پ}ɒ#/T鍈 u^{AiX>˛sP15 endstream endobj 80 0 obj << /Length1 1791 /Length2 12149 /Length3 0 /Length 13271 /Filter /FlateDecode >> stream xڍPiwo݂wݝ! efgwfys$UVc5%AΌL,|quuV ; "%-rDO@Ȟ`죱 b `errXXxm>Z {"8lea_4V^^n?v@=@h2:{WKgg>ff777&c;'&Bfl P:@3-ƄH PrB df V@{7{3 &#PrX_ pyOgcSSPgrvwfۛahlz7v556y3tc ÿs2[98;19Y#aގYLdgwvBV`۹{0u6 7{m80k[9e>e&B[ftp% =*Yr2} z9`?M3+Sg obV]c7 3zm3((jjbb w# wec9bN54-c){X8YLXO#u"I[?42l=x[Pmj @3+8킨IhlljlKǞZANVs+0!Y̒+Yo0+M<fϛ`VjYoz}#- `6qRfJoXJ?-?-W[^[^[^?-? |; -ǟ_o=\з7݁ S`D &g)4i.p)ف[є ^'-paI*mOφ {mKS8'uD"޿?@@vR;*wr1_%\1P:GY`3 ֙;,f+l=i g/M؇yϵ/lN]xx:o0ǦSeqJ7sYh 6IG;Ite0qYl˼Rc>muZ{jFt'$ K f qp^[nOnNj'u=GtͷW>sR%.*굞> 7 L'bWFIc gfqa 0w0b%Rywï\a=9t_*n}]*?nڠqSv}kD9i+r28EpԹEWQ#c⣥GBjr}jsn$]*Iq9^Uy.vPi`1Θ{ޛ]yjfx$u(>[:#${nlQ$@99$+/,06[cb$džb{9|/Yٜ㿾4[swSXl ŃUgr X")"R}%ANU5B;j߼עg[ƒb'"|4 a/bRx1Дym+whb 69eT~dY.mڛFBF4)U԰vDHrhr۳(Byʊ | ^jRl"\zyŒ? pbI09Zi]=W-+~p/5Tୁ5Ē1q#NC0> Q $pOӑTj-\4^ѦlzLtv}<*7sva' vq @]FJ#Re2"_sK#:")W0~>&Sq9Nw~s{q˭ϑ} ׹f۶&&k ҷLo<[w:}RW0p.M5 POGsfPLp3_ṰN h"y_&KIaH0ꃇy Yf0RJ隧$jOݯ N,S+e_޸erB]ū;jc$eH*_< {$hyD&@_P,Dw`#ݫbOI|Uqema+dDÇؽ}QM=FYYq-Eӝa qTrS0Y:I}U" Eq;@ˣI,$F4jBA |B|xt6s*T4ml,} [i٢* 1!#wM<;&5E+28>#*t%VzJdž)j±o~?5\>uG4ٹTֽ.gqhV׈ګ.v8IC5Q {wxsw<>0TQ%j*wd͗Z:%δv+p@)rOiQ驿7*d/?]fԟvv5#g~P?$?ˤtLܴij6\^$`as]-7)wb(^o9/t=p|5fpeb/wA95mTCuF 7^$W;OF3ws1GLnٍٮz<|O/ϳ6&K,5=%;2U(AQ N O$ay24O9ҖV7J´6ZK1 tM9]S"iEy[G7\rB'|$Mt0[|31 #?֟pN5BEE)ʦjvSubxk<OI# Mp);jF'\UV9R¹ӑ){-n[*pXί1%E>aw3T[(S`?UT}%ŗ 'z afQ2~~ +0WI%S!HvsNb㧾)7(qD)Յ A770@" 1D0KT'"f 026 /2hÂ9ý9J@% nC8Ch :^Rqr0 KrًdƊ{BI"xfi՜XuPǭٜ@쾏MVPN?x^23KTUgK{W%| k uu%/;6Va{zSK+ VBy ,>$eV@O[ャ^n>W9 e5gW6SĐ9Vtkb4h[~Be2hyڢɁR{r,(vR,?v B3n\^׹_o"^R (+l~h]qA8{9[KX 98M4.n+碒J]xrk1I=P]ɂ_i*- z|ݕ` p7C>&7'S` 7qdn`$j؟C7y #I,ıdxKD^Eg <6+}]Y7DSn, !ibE ;͎~|voO-m[M%5Bt٭#ZW,RN %~~lx^*npZV.ѪPY &ľ'GlO]x&>!y攸ѩH&.ۡ;ʈ[!/y,e )nR^<|KqGiMWafro!򈍴B3"Cn'q]]嚂ՄYt*_)$.Q͌r(MvR-1,BdĬG%gzU/2jNb2MsWÆ6W[ *SG*`ЎJx]AVO`%GQ.UuOlbq6(=qˆ7,3dL 5P=3#OsOs%800d^pe!Xȳ1eyϩ2dCktmbeUثf >.&MU† 6:>.aY^]Ќy~(/ƙTRx8Cz a}( MyKFFhGllVeΣ4̺}";%`y`fQJ8*FeFb՛ĖsGyI& :: ^dt&({d5AnDW1 D?+!@2 ~[-[ En4Z~-zI,.VqCSsF! n(J2.qF5C\]\8mZ9 1؏eDh>X ,J)NU^^Vs8ˌYt2_XZBE#7t=dEzeٿO~&rŁF6AlC\U^;(B ikPk /yjn]C_U< RQEoGh @iKZUi`xD]jt;D$ڭ3ှ˱Y[%p: NC:/  ( 63]{if-~{:+):\TzѨ&(QN9}UB׎ap N [,! pr2}5 vkz~'svUg;uW>sș#nM$Ilː ([u){̨"%D"\(պfUb`N.)햮<#] )`m1aWOig/ljl!ztgɼ&M7lq@xS}mؤ[Z3ӎH#޺Ą 9;z!)Z3)*?/|՗{\6L,ejmTM&too(x?knAy~q6Lk;Vir51Sq}JNY?(!0iW.ȅyӺs>{q!X ' [. j.?N'a׾Xi+cFI݉B(J#N˽mIj J&IN4ywOx lv+no[܊ HW'U͟w'B?K<\~|D{a5"5d2ˁ%Wfq|?8b0XvؑƎrB5`?nP)={ټ5BG,*>̞ q}p4 KlM{qvќEJ(\lҐ-k2\FN9)L][2FToجކ<\eGF"${tC ՉU㬭5unF m-W xьJ-NB ߜ=Э\ߵ2thA3Tbʸ̩&[TuUu.uq!v@)?Te~{o[ yk.Uڼqc|zQrZD66,tJ_ylYƥ,[ y_Tq5`t`>Mq}SuqMɗwL, u0̟ڏkkkD*NW}?:[>NSgv͜ǿ%MnK;g/4:VT)7SN.[FU&x*ʼ::*'4q97ΧDk0 @klLu➮v ⚳S,rݬ(F\7[)B 6y7Ts \([f/jORS#uKBLO RlAV79( = vjpkKBt-D<-Z!;S(\Ϥ~rvfOuyaIMY DOd&0de)Vv_7V)hYٵMMc<\-^fk#{˒PimD( m6]l}w/W?TAZ"ܺڒW44-[\7 !;)x1*ћ X ׯ 4ntd@<[T aisKĕ@m稄Ē#J͘)ZMQl1fe0;}u15-X{j D1 qfбP1|{bt\~.F41GDsX3*I[mq[0q X9>!FhtͻJrl]:-."=݇3##RiQGep7S͈¨Zz6 ..;rkJz!_SF3Eh |]:zDZ7Zz{J A 0.ӂvH{VËB!նlcB] e/9 E u"}f<rlH"ܗu|tB:GMMRfiʶ~j2+ҐIݡ?*m[q.[ $fYv+,qɽ> Ɋ3\{D9MTM ܘHKÇw4n+'DHwV汳GVeL3@sλw̱c~ 3e#g۸* avWYx'|8B4͘SW , $__Cu{#[1 ?+ eBօ5ܡ,U΂ 1œU6s[:*:G7BHi\`T9}h(^R_x;4`+Š=m*VV&yE۞,5%l54|ke8C"@X**C/vagWmNM:Y)C&M^nԜ's&6y$S7R<49s:GOfyGX;la TgEw?w/kD/&DV}uD[YD`q_! ?1Npi|Q4XCKZ_ |q"J}ZQiB˪qX|Ɔ(#'î߆Ndtw"b|8I'Nnwmc ֛.$+?M/K;`ھY,\f&lks@^# =jӎ2zo1{8"u R$5eoRz[^8 M[2ZWbH,y͹[!fTb L7eEj uù+I@CNE'):]rQuSεΕ&o|ƵN^oGIrL-v5tNs${kQ;鿮E\Ԍvja~-CEgtr2/+#)iù}9i47LImr(ݗedA |޹HslbSXn Ȯ{eGtH̕K4ONۈ<#΂SxKr9ex3k0Fq9C!vr#oڰG)t槸"VK6Onߊ Qο6QZ31Z_ʴ=P=bTȂ zlQ8))5 :[AV&6<{^Uwއ|E+iz]+ZXIy c>B??Tk΂Sᙣ6ʤkV pK9] #Hee[ )ǫSsuŠdVǑa[YŬ7)OX1m9 bco7pQ>PV%HzƷAv͉M ۽zNǶwD~~5; Wj"nOb<%[ ]mu9y2G H!*M"'?L,Г#z/8m*B0 !ɟ 9hw_ Ԫ"SZq(bt?WD3DEA*aOkλqs c*~z`xv w]N1e GN}-SFɮ?ve}OC:P0qexbLYK6wI5ۓ[~j 8 G2v#%gtzE$_ܟv$T5c}QJ!P.N&4TԓM+5,"kE?Zbۺ>̢n"(ýfy;#P<y9L@ȋ&[b竃nLJXxg$/tcp@AD] s}ŭVa yE zc/WekisqT leVQ1V(VʌEkXh݂mMwb'-4{[Cxi[[5bgf75(juќe&+Ԕ˪1jϸ:to9|<{zKr,=!/?IXqaB'kC-=$X!48g-P88?+)hxFRm$HFSũf1wy?ˊ}{S(|C* L< ̉_qDᗈZbv7/(ٿ5nȡG[u_{\+c~: >Pt0[" ΘY9,ZQ5=vF! Oauz}X> stream xڍT 8TێMRBISXgeɾ3g8f}RTJe %z J-i{ﺾ:s繟#FNLw 4X'UmmqxUb(qq[?( ij0 FlD!i)8e5 X՟D:C 'Bd\0@&J\\`WMp*+ဎ'ȀHD`N@O"Hl$Jpa/5 Oɔ3\5d?vA&Ȁ\M%غA:#2@1P!Hc"!>42ꀍ 8~ ?8yܯtˉ J0D{zi@T8`HaYH#/T&'!*!N u"2|LL<#f9 r4Lr$!Y\ݏQ < cG}@UbB0b)(70l'nٌE(`D?T 0 7Bp" P#f#πXD~8vQN\1HBWfu_N]]?$ ^PVUBbIV4QfSٰЫ!˂пeU’-MYO#C*uŏA~'D Xe 0#@7cqA2o LDvAJuɖLr[Q!hIgB_@ˇ,z0I@dwh@#KWR 1EWRp6AyFBdBgTY ,V BRU0H~!`(@$+`!K"E@E*x57ɇ@Eȡ+_I6:I=?U_u 67Oǰ lhuBOcnF!L΃Wl~~ }3j[:ʪ8(.MϏ2 >f55hd-OXڭR[e_iHTR6%Y>&x } uR-i}Um9NuJ}ylP##X#CE̎5 'XUhs˸k";|gT|π|zL%BpN.} |9w%JA )T֋״ڙ&A5SmoR !p%O֯I)xv1GTl|{|vQiydg4f.Cq8$c)8Jڗ9Ν6l1&ANJk;#|oQ"kA\zI=IV{+fJ/gZ7 D?HISzb<;sAxE$RAÙ!ڹVͪ)4amX_>W|žh{K.ȋnގ2J|nh?7&XOḇ)h'K~ԶԬY, g2fNEPR"ׇ!EeۼSڨYJVf{An/dvp2Tl/@Hc #g/^!ʄmmPkǥ0]$n[6$d&gw{_7@(?{2rF{5nE wxP.%gi(JWK>|osXh45&e ;3nX%bC))iQIځ3q*-8Vj;",Id}E(~<&sSRL»a-E,"2l/j_RԜة[tsk7Me!Uk|(_[ʠ̣!*[%u}=Ω'[t&DpjR?[jD ?XY%=P.*;p);ۚǪJ Zߵ; g #}w8r%evёuba{+VKb #L#(RClOƩC*o{q?cLRiEMY)׮7;ztt,g1 5o~ JӬxjIʇFSуfۚC9wp%0sG8* *hb/rFz>u~(1e'gզȰwC(%JOuj3v&f4?x^l Ix[En%׆~x˵vQښP~%_|cI,w6$7.oN#2_U}l,d|&pCQ?Bhc@:a΀ţ:c;,ArJʯw*b"傋GeޘGMMxd O#PϤ"}l%AK@TW{Wn65H*>۴9mD9q %v˃v}MoQxAsS.^{k.De[Y Z^A4[J7_ֲYE]+zU5bmnjy;+mnţS1g=͚"%,!Kg!Éσ9g 걖x`#}dMs(+}W>$M>}DtYQcNmNw#'`A;Hiϋ>oydM~yE/=#Lǧ Fqs;'5/-VlhʐI7>x4^@TvO.:[y!_RX>Włќ9q\X.,Q^Nlv Oeн\f0Uۼca9ZM/ N49{l}i?LdomlEVߺNg]oShͶŠ&/9G=U|G=8<ӞE@zԡOIfk㩯8eiƉ,{#Ҩ3ݮyYnuM-:) m.,Tّu/:4܈>L]l׍VvJȻi|]bGӰDBQ'2K0 TT?B:#˟ϔFZyGd"bi"K;JBXz9~{7˵9/A?&UD+}b[=U{ WFO; vFI Pfr N7x]K21v')''9 !TmՃbTdq#|OV/d5;yzltdG䅮ڞ] 﨧v.2wXzSfH!' _isnԎ/->3^˞^vr]G'Ux8bn z*oE>>Z-Ɣo<0L<)N3Uaa |6> endobj 2 0 obj << /Type /ObjStm /N 77 /First 606 /Length 3023 /Filter /FlateDecode >> stream xZiSG_n1}_[T6$.ז,DhdiN~}[hl^] {_3 0#L\a,8&1/ J<2sgY0ib&@L)&@)u,PaP>:=%41)1m1E➦:HYaWEr'>c+ +{bhŽt20A6k ,z $Hy+f|8+ajYÌi6,#:COZ-I :g0Vs %AaF}5s72{陇 bCyXO dQCA| IB!$0 dv) fS 4ЂDRapJpaUCƏR׌?bo5U} QS) i}9Sx]8,OnpGcJdѨpjB+=zrRN"?naoC-&BAncl $YHwtN~P~;& |gTׯyӌ7EG9)&ۧy<˝Iix5:)?Tgը?,Λ$ T?̢(hSDr8|R&H]@J *nTBM nrg3&7;IU|ǦӺ~9?Й|T2t* L*ëdHkx,yz F+mlA*YH2VON>TMYErr-n$m QxK!//QXWX0c@1@EZU޳Lda)M(YeQ򱄩4LҏzfFgWq5(R1!} FA8 tP_I TJ߳y xKV/̢ޞg*i LMA)Y)46T"1ƴ@W+,GAB;E0zTTp\|.ܑ]E~Xq:luX]FZFhSmJn#P-S.T_Ю՚kmZ} ?@z1ib#&Xʼ==sXean7Ӂ vـ”3 (PSȐ#iP M`Lyж3~˻t9֞Czv,Hx櫭 x:Al'=P`H# )Z6h>M#|izgC6A#Mw*E]CQPDpOêu$LfyՆ!D:&g->غxb=EJ,Yk67OUP¡W<.RNGfgci ;\>o:P Lz)hlfuzitŦjcf !FN _6Hi _'ÐUGܭ 1Und"3yh;24kfc}lEgjҸN(|Jo:Tvi^@G tmqʲ]ry﨩TfYB@9F).]u2a+1rp5HM1>[_Zk;7$x;ܕٔ 6ub*-)Y.t@[;]O.mI5a5 KGMyq6 |? V7䯰,9y /)??ʇx|§7~?߷ZG;/~Bӈ^hĄЈN#uUK%ZdEuY~>~j(XzD!n/¸.=hfaZ~hY_ɴ?:aA=uP_\$_|$9}|^V\eTJK㘏irX6jnUeݔ' `~a٦o=>hxs>)K\K7?IbAs /`uFTdDں Eײ+6ۅ Rw763%~ad頪dpy:bI='K3~S`/mw6)ٶ!YpXNK3./I_Z$[v<{7c7dQ*R a0䷋lOʹv%viWslQ^zvkRZ6٥Koȓ'g?~Vy U?P׀$ Wsۻk^'Ji4[YnMi e+kj2mFz8/nMuҜO;Vq%NY9m}sڝ!m?[ȭiZ/:}6+Ւ9n]Ʈ;Yex|9n@w_pR%dRz; {}WL;ыfJ6|l5IE +3'[Fߒ6ŗ7fc f bTUab i\mѷKdz=^ޛ-V8ODPH.ͩ>am uݢϩW}Z:ձE:wXjSk.>B't9,Q*3f*R-^gmw>)i-R9?U endstream endobj 91 0 obj << /Type /XRef /Index [0 92] /Size 92 /W [1 3 1] /Root 89 0 R /Info 90 0 R /ID [<821F4497A5CB45CF41B76BFDB0E39854> <821F4497A5CB45CF41B76BFDB0E39854>] /Length 223 /Filter /FlateDecode >> stream x.a5<Ϫyc-qvql~y/'I~eH ih:)B# 41AC:9A+ǻZz``V"LA `*4,$, lZXeݿDm[OQ;gԮڳos:j. Ʈk7 . endstream endobj startxref 107160 %%EOF GPArotation/inst/doc/GPAguide.Stex0000644000176200001440000002700214557733443016475 0ustar liggesusers%\VignetteIndexEntry{Gradient Projection Factor Rotation} %\VignettePackage{GPArotation} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{varimax} %\VignetteKeyword{oblimin} \documentclass[english, 10pt]{article} \usepackage{hyperref} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=TRUE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotation} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \section*{GPArotation Functions} In R, the functions in this package are made available with \begin{Scode} library("GPArotation") \end{Scode} The most complete reference for the software is: Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. Educational and Psychological Measurement. A mirror of the original repository that is referenced in the paper, with additional material is available here: \href{https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html} {https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html}. Rotations can be performed by providing an orthogonal matrix to the gradient projection function. Orthogonal matrix for rotation can be obtained by extracting an unrotated factor loadings matrix. A rotation is done by calling the rotation name directly, or by calling one of the wrapper functions \texttt{GPFRSorth} or \texttt{GPFRSoblq}, for orthogonal and oblique rotation, respectively. Under the hood, rotations are computed using the Gradient Projection Algorithm code, which can be called directly. The key functionality of the algorithm is included in the \texttt{GPForth} and \texttt{GPFoblq} functions for orthogonal and oblique rotation, respectively. Calling these functions directly works as it always has (the codes have not changed). The rotated loadings matrix is the pattern matrix. The structure matrix may be obtained using the \texttt{summary} command. \subsection*{GPArotation Functions with \texttt{factanal}} The {\em GPArotation} can be used in conjunction with the built-in R \texttt{factanal} function. It is recommended to rotate outside of \texttt{factanal}. \begin{Scode} data(ability.cov) z <- factanal(factors = 2, covmat = ability.cov, rotation = "none") # quartimax rotation GPFRSorth(loadings(z), method = "quartimax") quartimax(z$loadings) # oblimin rotation GPFRSoblq(z$loadings, method = "oblimin") oblimin(loadings(z)) \end{Scode} {\bf Important note}: \texttt{factanal} allows for calling a rotation directly from the \texttt{factanal} call. However, due to a \texttt{factanal} calculation error in the computation of the correlation matrix, the produced correlation matrix {\em may} be wrong for oblique rotation (orthogonal rotation are not affected). However, the correlation matrix \texttt{Phi} produced by {\em GPArotation} is the correct correlation matrix when performing rotation outside of \texttt{factanal}. \subsection*{Recovery of The Unrotated Loadings Matrix} Recovery of the unrotated loadings matrix is consistent with the definitions used in \cite{gpa.rotate} (page 678). For example, the unrotated matrix $A$ may be recovered as follows. \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none") y.quart <- quartimax(y$loadings) max( loadings(y.quart) %*% t(y.quart$Th) - loadings(y) ) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max( loadings(y.obli) %*% t(y.obli$Th) - loadings(y) ) # last equation on Page 678 max( loadings(y.obli) - loadings(y) %*% solve(t(y.obli$Th)) ) \end{Scode} By the same definitions logic, the factor correlation matrix is calculated as \cite{gpa.rotate} (page 695), \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none", randomStarts=15) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max(abs(y.obli$Phi - t(y.obli$Th) %*% y.obli$Th)) \end{Scode} \subsection*{Random Starts} If multiple random starts are desired then the \texttt{randomStarts} option may be utilized. For example, 100 random starts of the oblique infomax rotation, \begin{Scode} data(Thurstone, package = "GPArotation") infomaxQ(box26, randomStarts = 100) # 100 random starts infomaxQ(box26, Tmat=Random.Start(3)) # a single random start infomaxQ(box26, randomStarts = 1) # also a single random start \end{Scode} The loadings that are output have the lowest complexity value \texttt{f}. While the lowest local minimum may be the global minimum solution, technically it can not be guaranteed that the lowest local minimum is in fact the global minimum. To further investigate the local minima it is recommended to use the {\em fungible} package using the \texttt{faMain} function. When in doubt, trying random initial rotation matrix is advised. For a detailed discussion, consult \cite{nguwall}. Additional algorithmic considerations are in \cite{gpa.rotate} (page 680). \subsection*{An Example of Target Rotation} \cite{fisfon} describe measuring self-reported extra-role behavior in samples of British and East German employees. They publish rotation matrices for two samples, and investigate the structural equivalence of the loadings matrices. Additional context is available in the manuscript. Structural equivalence includes target rotation, as well as calculation of a number of agreement coefficients. The table lists the varimax rotated loadings matrices. Performing target rotation of one loadings matrix to the other can help in interpreting assessing equivalence. \begin{tabular}{l c c c c} \hline & \multicolumn{2}{c}{Britain} & \multicolumn{2}{c}{East Germany} \\ & Factor 1& Factor 2 & Factor 1& Factor 2\\ \hline\hline I am always punctual.&.783&-.163& .778 &-.066\\ I do not take extra breaks.&.811&.202&.875&.081\\ I follow work rules and instructions &.724&.209&.751&.079\\ ~~~ with extreme care.& & & & \\ I never take long lunches or breaks.&.850&.064&.739&.092\\ I search for causes for something .&-.031&.592&.195&.574\\ ~~~ that did not function properly.& & & & \\ I often motivate others to express &-.028&.723&-.030&.807\\\ ~~~ their ideas and opinions.& & & & \\ During the last year I changed &.388&.434&-.135&.717\\ ~~~ something. in my work.& & & & \\ I encourage others to speak up at meetings.&.141&.808&.125& .738\\ I continuously try to submit suggestions&.215&.709& .060&.691\\ ~~~ to improve my work.& & & & \\ \hline \end{tabular} \\ The varimax rotations for each of the samples may be expected to be similar because the two loadings matrices are from different samples measuring the same constructs. Below are target rotation of the East German loadings matrix towards the Britain one, followed by calculation of agreement coefficients. \cite{fisfon} note that coefficients generally should be ``beyond the commonly accepted value of 0.90.'' \begin{Scode} origdigits <- options("digits") options(digits = 2) trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) # orthogonal rotation of trGermany towards trBritain trx <- targetT(trGermany, Target = trBritain) # Factor loadings after target rotation trx # Differences between loadings matrices after rotation y <- trx$loadings - trBritain print(y, digits = 1) # Square Root of the mean squared difference per item sqrt(apply((y^2), 1, mean)) # Square Root of the mean squared difference per factor sqrt(apply((y^2), 2, mean)) # Identity coefficient per factor after rotation 2 * colSums(trx$loadings*trBritain)/( colSums(trx$loadings^2)+colSums(trBritain^2)) # Additivity coefficient per factor after rotation diag(2 * cov(trx$loadings, trBritain) ) / diag(var(trx$loadings)+var(trBritain)) # Proportionality coefficient per factor after rotation colSums(trBritain * trx$loadings)/sqrt(colSums(trBritain^2)*colSums(trx$loadings^2)) # Correlation for each factor per factor after rotation diag(cor(trBritain, trx$loadings)) options(digits = origdigits$digits) \end{Scode} \subsection*{An Example of Partially Specified Target Rotation } \cite{browne} reported an initial loadings matrix and a partially specified target to rotated towards. In {\em GPArotation} the partially specified target matrix is of the same dimension as the initial matrix \texttt{A}, and with \texttt{NA} in the matrix entries that are not pre-specified. Both procedures target rotation and partially specified target rotation can be used to reproduce \cite{browne} results. In this orthogonal rotation example, \texttt{targetT} includes a \texttt{Target} matrix with \texttt{NA} in entries not used in target rotation. With \texttt{pst} no missing values are present in the \texttt{Target} matrix, and the weight matrix \texttt{W} includes weight 0 for entries not used, and 1 for entries included in the rotation. \begin{Scode} A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) # using targetT SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) xt <- targetT(A, Target=SPA) # using pstT SPApst <- matrix(c(rep(0, 6), .7,.0,.7, rep(0,3), rep(0, 7), 0, 0, 0, 0, rep(0, 4)), ncol=3) SPAW <- matrix(c(rep(0, 6), rep(1, 6), rep(0, 7), 1, 1, 0, 1, rep(0, 4)), ncol=3) xpst <- pstT(A, Target = SPApst, W = SPAW) max(abs(loadings(xt)- loadings(xpst))) \end{Scode} Note that convergence tables are identical for both methods. Additional examples are available in the help pages of \texttt{GPFoblq} and \texttt{rotations}. \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock\href{https://doi.org/10.1177/0013164404272507}{https://doi.org/10.1177/0013164404272507} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Browne}{1972}]{browne} Browne, M.W. (1972). \newblock Orthogonal rotation to a partially specified target. \newblock\textit{British Journal of Mathematical and Statistical Psychology}, 25(1), 115--120. \newblock\href{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x}{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Fischer \& Fontaine}{2010}]{fisfon} Fischer, R., \& Fontaine, J. (2010). \newblock Methods for investigating structural equivalence. \newblock In D. Matsumoto, \& F. van de Vijver (Eds.), {\em Cross-Cultural Research Methods in Psychology} (179--215). \newblock Cambridge University press. \newblock \href{https://doi.org/10.1017/CBO9780511779381.010}{https://doi.org/10.1017/CBO9780511779381.010} \bibitem[\protect\citeauthoryear{Nguyen \& Waller}{Nguyen \& Waller}{2022}]{nguwall} Nguyen, H. V., \& Waller, N. G. (2022). \newblock Local minima and factor rotations in exploratory factor analysis. \newblock\textit{Psychological Methods}. Advance online publication. \newblock\href{https://doi.org/10.1037/met0000467}{https://doi.org/10.1037/met0000467} \end{thebibliography} \end{document} GPArotation/inst/doc/GPArotateDF.pdf0000644000176200001440000041763614557733461016756 0ustar liggesusers%PDF-1.5 % 3 0 obj << /Length 3378 /Filter /FlateDecode >> stream xڭZYo~ׯ w#@ή71r؉XX( KRv /:7AY Ln矘03*2(@PUHw^)1U ԪAʵBֻ0rlEW-ޜ :!?+ 85k|pk6U8Yő5fwGYɦohMXcv&{{l 3luNu4NVnVYZ,b\Fsxp>0x,yKneD.W(/)NFz> 3 ehX.er1Uh L$F*6C)FI,E@eD 0CuOQJUp>ۨ곝w>$ (J"'t3b,eOUӟ+KKsRv E2LPI] =݈{H8hfV%F_0;}] ^yuwz vv`6b =x*nС4Fxj#w2 & cx@)wEx5R"QLs6nhPoX4kr S* R&X 37EQ  3<+`1CeHu:",AQUM?ݗeqA=wt(c]nvcG3"(w|>ECCԘlWCWG7nx(>+V fM8Rm'U^o)eۆw!%B[ O$/QBnϯNmFm_ʯ8:*bq&+#2Qp(o[(:d-$v2D&GtlM(m+%\0#@׬%'.TeO|:|e*wݶ)!Ǭ'AUOab~ M⮜<_ƦEԵ!; P5T 6tf'}P/4Z$ =;EBᖱ| cDѷݝKq%|g}B@)Iwu?_3pMvO%Ji;cah[֓m2[rXIxQ?SM؂vtD݆^< ic5Hzim7цD6J_ӕm.PhK;mG :?F{7Z:ߜ<?N54m|9ע$"WhUAAzRM/Ʊ De8!Q2lgT_n텎 HBRN/ٿD #(:kx({(\N.C rm5ȂXCyl.YV  3# IԿg}Z4I0+B',zpˠҶPM? uU*hz`m١g„+ݸ'JdWKlft%ĩ6ؾD|hm3W@C[FDž% nʋO"u{Iⲣz8_/;8jAokY%KaR>8m^qJ6uto8 E*2.b?+ɕ*?+ *h 9pH7OU9zbdiy.O}ZSZv]- uLVp&ྒྷ'BqzB:RK!\~xo)!Gdw*Do *#Y4̭\&;68:_Mvq\ptC4 ZIhB̖'<{u ?; 2.3NA؝fЃM XHqU 5Oi Hl6ee5=x.5^mDG09S v[NQ.LJ@)}t4) }O%A@؊$6PC79SP'D*c:dR@Lpj*[oHG "$HyOM7Tܑjw9Ĥz1-Mm;!/=_3 DbMMSbȚ endstream endobj 19 0 obj << /Length 2792 /Filter /FlateDecode >> stream xڽZ[~XIh /à^ܦ&6q - {ns8Zo\RÙ3~3߼yeY\5YSJ)U\UʚW?VRsͯl\o2۟\)oot_͕ʚd[b@h Fn^e`51u"}znOHe(xݻikX&7`'*OiM~K_|g?c: 3#3r&Xމ,_'~Zp4N XvmOc݇=?p'1AFo="\t䕱sȇz'O<ܜ3[ SxPZz1"37By#'Tڳac s-mE`MMm.l\xc:&X_`ҥ\kҔݞ&ъ7 PEcF~® VCa .&Gi1:37&ԧiϛ^wvN%jVrs %I3ˁ2!.TVi3VЋA0֕c#*SH萌ϼÓ̻Hai x p#I}Nry&OB `Kx<~Fgn']0x~Vi )EOqi]] ts)kd0%pStثgv ):l|sp1LE" \/w` Sx {!0hB QgGwupc[OȟZ L-k=9%r:S؎W| >>pt: G` *r,t (NbUuV`[)+!"_9a_V|OUe?dž?l|r3GWIuXȈc9Qr29 EBtGO6@^Ol--îNvJH _<'e6ͮw9~t͟OWd*NE# }*">q*rm m1 8Yo0S]a\9XP2'LdJדxR6H cu<{7M1m3Uw3i=̉d"LUeǖ9t>13JveO*/UìqpЭPĻ=y,3@X_b?|0 DvZH&4KDηiC}MCྵ?"t3 A?cn۰dy)0<'Gk-8<7u endstream endobj 22 0 obj << /Length 1569 /Filter /FlateDecode >> stream xXmo6_adC!jְvذfWX?dزo6ɰQ$%IZ4u<>wܑ7GOOxYb DhN `h>8vC? #ˡ|nd+/D% )w"yBƥ%*^v(RoTsvB;]`$z?}C]/_O8>dθx(;WQj0>Ce|dՌowv]:Xl] RAլC'l6蟶/+' "~d70 ʌuV e>| = BItJ<`3 #۲=bvo6|D8cF[FZmbv8|N8EN W7-¤%dLy;_dcq[h\RO XbHoOxf}͛#v~K44+ƶcj.GL?yŐ+;]}4Np0 (wY[f$ |5ݰ#.xc̍3A khYJQ6[9ڰ*gҳ3^}o4nE@2]Shkw1"='>>*HQe B{dZs~U KvTwEg013a<(lB(R$]:gEAef^i}M:`}𳲝|.;@>=q !lp O<'I4s?pb$cQ<ve!t#nCxIfNdb>Js/p I?ے(7R:8b̡2c{/kOa% AEvζXxKA(Jq9]ˑ٤}MKJ_,*(%P&ǩR㦜TМu#|%'!0Jo )͛M)tvhYh}͑,f+ HU?1)~!"Hw+Nvy+/,2|WL9Yu暘DЊ>`V$AݤXMD+Y[ER:ZB{TN.b8jߐfsAR9mhaP^VWjRIy{%@ա~*LY7OƊc1n"3dl @l9ho^VgZ/){a|f.[+~b9@A־fL^p@wFh]T$#]q e@˂8+n=סd؂wSq!Ilw7roZw>a|8~?|}7VgU-` (1$N!ݗ}ľC.yG s6n0;!yrp%ĺ囬4?&9*( 1!|b}t endstream endobj 25 0 obj << /Length 1781 /Filter /FlateDecode >> stream xڭX[o6~06X!%Rafb+4@Pl9֚X/M`}FM@xG==}l $JӁ0 ɒ0ah2x5A m m6lF~v mH,Eb4z`'5`u[u)6*4F"pKA*>Gq5k( ;Z I/y"iv}Nð{fQu4BRB\z hY2h#Ŷv6U4<遼r\w Te! ?sڞw,UMXUyK^u?7͐(ɴ ZUT]oyYmc5g]+-jZZTZ 4{#V~;r^d toŝϬN;9% hPׇ{0(1Mpd48h½sHLҀ}ˊl!oR45yJ CjfCӗ;ż=BB6?rߨu.$ ws帒{H*[4.)LJK۲[$"SJ Y t0kuŋ^U(4Z֯6 Hz j(Q]+h)6Us A4lǯU]h<{e#T':(vY 쾞I].EE;r»?Sɫޙ=kz| n;6{&]& nw<u}_8v?JXT7mF";2DTx/ Džw8s7DKyyTAbMfXql0bt&%ڄIm&S/TKGb"2jK̮up"a؎=ơ$xo=3u"f>EƑRɉ,UB1$jK6_wj8D W=\Ary؇<1dP-{І %d J ?A|mbnϠX 6hOQ11C ΉS+޳e< ubᎇPjjO\ OLjM`Uo eu @@#7F(bAw> stream xڍP]0`!!h.[9^^$UVc57I۹02XY,,L,,l`fDJM3ގq'˫M5N j`errXXxh0qrv gDJq{O'6yi .ڂ@;%uG @xW K>ffwww&[g&{' !Z; r9 P4U"%@] x5؀ ;vf '5Yy` t#&@'`\<\&vf8ۿ7q3ۘ %0y-N`g&g%2%mmAv.Έ;Zٻy `;3?0su`ְ;d%y5!k8YXXX GhGzuOП?ͯz;;_A?&n +w"++ t,vf5w{X^{'27e~/($MW=ތ\lF6N+ܯFڙxRzLQw=Υhڵ ͿM|bn?uYoM\mltMl66(ؿjZ1{ɺ?v{̔.@˿z/3f);xYYX:X@kCo)i7c8&NN&WJoI4yf&;{%|N'Y_ `YR+Yy)Klf/q!^nɿ;|m^??·7|U _ENߐUo|%OK!7|7|kVbWݿeCo7| u _U9\~WU*UO:9Rv<@@Y{ UMHm(;4V:-w{7_VESЗ6%iDHބ7'<<%Nn x ZK@Ĩ.h .Gʃ\u#Qiq$lv[e+{OS139?\0O=f1_H}cًu~x-Vgs# ;]V*8?ғ&>2SH9`V|;EG;0F8&,6O$,3!=%iC|qxNόv'%:v =uFW=SxI 7~if҅/KIʘ ~ɘףdz18[3OX6MX;516z1nqj3?nIF ޻er\U6Cmnx 릷CjYN(ȏ1J%~[ZnG-{%[ٙM[2}ùKl*`oVPDSأ ¤uY1B2D]3 | %%[Zp|F=(;qC}) '8{, e;[%QRtG*ݧ_F^ ;] J{?HBvecqJC3Jws<,\ɕ7d{;(kAv>d}ShG:J0-Gn1. MlRSeaB &O ۬oyaݳ^1t0q1C nzc Q[mr׊E_+[5#>Hor/ )\'fYO?]vg~'Ff5p9fN8{+A4@\٥T\̞ѓʃ朶S о }<h>ߩX⤛3ɞ!tژi sɂsƛTQQɬ oga "]+23¾=#IB-ɗvȲIk/–ae(7 ) SR˼Vkr-kd|b?`xbCBj\ā qsіqF!E |DwMȍ]-,R'=V @\K>.6Z0CpkIܹHzڛUSk oiJ ڤH|t.)VxOHe#j7'U(z8ArCV?ɗz}>t2A7 E$[PZI6]`;GVL9C.Eګ\s\[> D>A9,&aGkpCsuP|3p}',A^y}$&DŻ*|r+꒏o?{ް[1pi;4$2l4Do*J{d=D'vp>vl^0؀=/)æTq"¤߳~0Zj!vfOp/׭]+7[?Cqa"U.J# 3`]`g,38_e`mͷd\ol+]<=@ LNCCԽQ (-wTF2 hwCxo7kVog'F,RP FQzb=:o4z M=y[CN8! cTu u݋3_|3MǛ'8HBpFXN\i8}W.V7$ IgY%o#e1yDOg(Oo%*RLܓ4ʟ>j76lPxc43U_Xo ܑVl]v V,;fmA۞ }/g%c \$K]@,=|l%N#i-BIV2JűPA9N:_~u^'؆ܪo`# A̪UtnN/³l`L O9*h87 }[Eܿn$jEGnd[> '(#F!$ Mbو#V\N@[VIi@ZeEy}J s&}s*XK2;M>F(ȸ>OnFO ٦%N6K4Ww 쳛k+ÞCjlp9Q"SX?7캷*m~ˤ&cOg{& d_ 4zRÿj\g,Th;c.WF~rc{]yIViyaAݝ6GR+o\0S\myʪmvIs@ ח6# c׆Msu=|"J61ZUVkuxA1`?k]!=Ԏ8GZmYгrX^Gk0WG1?$@efD'[j'Lfʷb G!B KУ8lrʟ @%zYGS#??Mݰ/[&`E|y$ґkYr#tjTVh\iLma׌ˋwЖ:b>Lˋ1T\" ]]DB7?j 42&޹:zi@* 1dT9$H"t먷)e m;A 9~VV#*UqU8RMD]O t 2kF~Ttҝwɍ jIB5荞c92IJ|qPiR-!ۧ,ʄ tu/%[sJ8\UH!>°j"ӣQ%}Wִ8^ZAPr K-r^<26Rѧ61q&1OJz<Uތ;4tGxLRR4Dž٪_ҵh;yx&)po?:Gk*A{BV[-i^Bv >(F4%׋ MstN͠O%aX9ڰV'𶺁̨.u-[l̪tUԢ %w.dz{HZȻB9t &x }y<@Cp8oJP3ȜT`^,^C亸Z [pc'2׀dh-G~qp2;Mر1 R0m8m z7(hrm~݈**IܔU0Eu[xhf)9C p5tBx$_+ViȽ"XP9[փ(eA mJ%ٰQ*ZlQ_?gp0Hw%V{L>䉠܂7y" g Z7/A3[cdh԰\%b^7j=fX0bxR8pj !AP!hM|T"d{/n0wmg_"/&KUiF]BubٌɜBb]w2MaH`#o`c%SI$] 3V[kˊfI~6qaf|idsаC敱*t=F8a+%T^9{ԚZ!ۥTiPb9. KϧpFF1@=j)vooHV/P)Ӧذ -oqB566GD*%^OCNHc ݿ^Gє\d:5_] nZ~wOe\{R!o)<ΏuqPҙO:xd| 9nK=<&- 2 #m%2)y1(s.6 P*s>ޣZ/9H%"0U_Ed/3 >ŚNפx02LwU"G, 6T]pDWʴE[DZJO>'9NnEtC5gZPp!NHy3VZTӊ@$7(#f1l9Rϩ>V]`A^Jԣ%?wcP(k6XCO'Nr5?푉f9fiVtQCtsMf{zJOmVPJMg4h" ⭱?ˢ4~i<tl>={ L-rܯ07rzX:H1m-j_'nѠxůzqrc%ﺡc~f0n[䷭E,=`s31o-fV]"vՁղ5d.N o9 3ŮlpkYL8S=/ࣞ6{{Y:.~4xDbtoC &cAP%n'Ŧ)X .]y7 fp8΍oc ZxvQMjfjFyQ}QȦ\M= 'OpϠ~ Ι`4ZTuo.2`4y={yySF tg~e%hVaXٖU$|i> KO~pˊ#+\km+h؉}=hׁ]V/+[?S@bk~JQ'Aye\SJ0Gа~'|m<]}/3RnPZ 6ҁ:b,%teI@.¦v`%LN0B_+ 0̼=Ll^5i]ξ̊P4s ɴ(x$ >?QlI2B̢Sjbnfqŀ*z%kzlPY$SVPGz~^P:a_Q`iqM&oo*dpQâ8؎e^+ʄ{/Jm;MYދߣc(ׁ@^ <z褃Y)!"_c>hc곤Cvj8OlHr~yyF097j[/U!~AڅJ*G8> TRa5ZkHEg1AJKm*|m{=}*M)NиrȠ3F.5М76Ch|"9w /dbQ8x–~@ZJpVnS s?Q_1js=?_0Pu.](m<:P2~wڽ*Iq'O ,4]v)`V;/<h1ŊThGBC{yK }qpvō[R/cBގsVb2P;]̰J5%Ґr+CҰ6(4Hc[Mb&vq&mPaҚvPY)Z@:S@󥘅 g4hKjY&ڋݫNQ$]0]6Xx: ~V.t9H 2CfF4rLB`"[ե' BWrtF\&\݌sFqx +r@Ď(s4r7(œA9]lDDaKO+zuŽ`Ɨb !ʾ]7D=kacR9yWQэ&2 b*GK]B*Odq.7^33U*W|["UPJrEa<>WV ݎ9NAS7tL\6°{UF5Z횖JKP ݰ!4Vٛz+}ԛI#0pmpeN(roh76|'S{v͊)*Љ62uNdTqs$Unot#3 _dQ㽯?ų?bQ{mE=v9J\$#!A ^Z{\Jw'g8GKOn;هk\f^822wYp(okN{8f,qz=ź#d jQ/b 씮ƏQ;DerCk KVꄪx5i޸%"{NE zq%)`½r\-IOM`~ЅD~9zulQv#nl]d>fd!bHa2V41灯<+>?K2vL͌;Kc)U_`X˦dE՛+1mu1 Gt`V 4noi3)aJςdBEkoW[Vő:ø6@xQѵwQ^Vփߏ?OE4r#ZF(RA)r>HTȽW_!TNK(zWaؚͰM7B%IyHڛ{z-R4pcOyրX<2}ߚPQRh`XER!PBeZuN"V{p-J%ʑ%N?M/YjSVqޤk)B<7oO?e哒3 W ;i>fC_%f'%wRGk&YXw2cTR9bz^@>tx([H0'FW?55J^\ё7.\YlK1>=;Fv!8?%] dE Z&?!;3Z󐮺PqxχIJhrvwZE Mp#}l3|ܔ Gtª<$kd!Z|EzkQZ'c9^Zrp>xd=+J29/g'x(n_Qa1Xg{/t{=3$?*q\꽔)ĆȒѬջAVj+FpO\|ƧvqГ>ɣ"5</9ve>tY+:.E:]ԧo-Lf܊m}|7ElUKbxWAS _#2%Nɯp1jL1$j!ej~ ꍽ`\P!&eS_¹ =5s0)T](-l3fB䆣md]TK|5wܬʐ?;ԘFE;ђX7*%b3" L'oWDUkK!˖;i! AzlG q?eJtծ] 櫣QE @oփ%k U}O`?pOad獪am}-=.0q?AQW[27p"9eBYh6B hZS_hi|In"-v: 71W/-?cP<* O|5B2@$lsȩɶ 1GJ"Hfrϝfw?=&[k}*Ww Cozm⟱>[ 7%y䖙Mc ϊ~N]ɽ #ʞ{o8 ]M:#s,2m,#~u ^ |boӝFKWdO-!t]c9$D TF~f(sKsͱJP ;K TqkcnT`]X2{JČ/^[;R;B*%E >N \R^ jHW#bH\+<ҒU,=aB=V9boE>1쑟Y[^j۽xyj8f3y ŎƩQlߛnP|h<~u/p`g,N(g}<-#0Ai8)Y5o%L_<'r@C4X}-`!Z𑧫R c&OҘr'^*Sc{©}:V`X!tVPa|$2dg6U__J!R#, endstream endobj 42 0 obj << /Length1 1387 /Length2 5975 /Length3 0 /Length 6929 /Filter /FlateDecode >> stream xڍtT6]ҡ" 3twJw+C0 *]4J  -(H}{y}Yy}]{_02R"aHZ$ kX PL%6aɸ-`(o8!5 W@}$p$eAR@ @Dp(@_pyq!=PpgU>HFFJw8@C>L8 .h0[rV.7 ~ 0{&L 0s{0E:(W!>( 0zl$ W?ѿ#NpwPSO_@7* [4U A=p_3Js@{OA=@!~+'8k 9Q2 C$2RqU, e!8] ;>do/ F1 @4 G;Qp-~ ?+AS;FSUDe$8@JJDF`F#XFݳQ Wԅxt;r;G_YWwG>?qE] Wb@7vaP{u+9 poM? jGC\b_v_Zs#`FHo w%0 }E.ؕ~YRAB MTBFdW|`@W "@BW(_g*- }Aޞ_TPWr̈́^6 L!!rQϢ:~ԫŽ(|Z>̠:}~RgF,U2Pͯj)ϲc[cݻo2m'ex_J"dtdߊuGҨ_cܻ)zqd$ "ǼI&N"+ ?;4/ I+,L.TzblĊDn6Fu#GLEbG祖Y8'G:l+\F5sG xg$ג /W}ZMK̂s [MA!!զTD.!f_X*I?AAґ ^OqxRwpt(5 El𞸅?z/Uf"|&j=:K奉F]ֵI<n{Tr2.eJuqfr.6moasT&L{[ip"Լ}ѷCqA f^=?9ni%}.9qysCy* 0ǀ(9+!Oytv3~v<o[t)V#vFp!\gL|zFu q/0g#,'~~et~6h(]6l:>Q>_)so5"*@pKru!vWEJ;M÷:ߵ."$o9#,:,-CE|4xcw\_ٯ%|gff%:z-^,*?*~?f L);n?ɶh )ySVƃ\mݬq"ױ&rL62(9$y6TRVsv{9InB*0~ʾJ>Bq}?&]~7.9f@ _X:OlY^%UJg-XcΩPi#k y;b L=)7YX{ڳmkthC>~ȀKgNfioQ-g 6Nx[Bf@uI*Cm ]g~2Bޭ;o{} 0b̩vͷ k(*).oOjel5x'Ns߷=g3U登NmQtע^.8noL?)<>1hN w д۩bP-|kIܺvGzֹ#H14:̘#+ b5_(o. d#yTwSu…y5get/Gb׷8qhnn~>Jq(䀃0gl׃TGY I{ Z妛Qg9k;*2G)ɸ5\=$I}HY)F?JK66;5LW"S]8C= ^҆ٷq 7OX=3 LÒ26 CqAulCѾ~#(QQVx)5U|C ~nЧ26K܏cO`pp- |#osLF;:7nY)6 #WplRVavwBɳ"n;=l,(K@v=E!jr;Lx.Vߨl ]{LeG 9mk ih"̱ȌѠD#2e磴oBk+o 3co1j6M$ȤM24WShE93,G;K hL󆤙-ucCw#gYPD{K~tu>v + 8-بC[ڀ>%yPFN|{ʱxZGdakZIMg L:\h^q9}g~5>0fqÁ9{9dG;Idy}hm~JZ*^5(l+ě׊~u{7Е̃qUK4H}OژBl8/mw;AF O{]ŭ5~{7v^ yqCCwO Vc{k)ܔ)TxCw/3@K%M60*ce+hU ~(\T?*o4fgynӈ`^-tPA2DY~jqmNufۮ O*Z%"sY0z߯'oP,G|$JĩԿEfp Xڈ nsa._8K L!5hdURpLj;(N6(ۼ.g7FaϘ=v5EkY&X9n(ZGg6;jZKkQ\jRoB<zGGLEWkD /"GͯgvϾH(+5s 0LVhy~YGY:=Oi2$HIMvxZmʒV kbEW+I藸/m)(aXKf?~z7Qh}Q7)5\P2Oɨ -O{)(TRܣZU/A>`1\3Ij9V~HtZkuHz>2^9#KD=Na(!}x9ښ>|}N,xI@LŜͬdrwO(9ފ̓M>w1|Oj]O"5X?39T`G+-) Qyc ֟CP2.J[,Q2:kIцITs?!4G^_Jdgٵm `H=r6>^y5،olK8ڄRS6GjDưw˕nϺjp'o( x%9hb"D-J:8z?sW:>rB)GL*wc13[.\"W[-=kxEhj_f'ܘbmcE8l\_H91_559xJuvG۱u"K=i LF<%p]mxg׭xKsUіbJh ׊B};bUH% ,Y62f ;\=WXO39zPzOC^ 9<~O F,sZggLq=\r3~29yP|0a2/R ˢ畞X?1MyZ$bi`JYkߍpM#{w/3(`c3Ea[M"q[ԅ5^Tir"j'WzTwҶWEf m ,,AP7M])fÑȪxWdLY.WW \\t5ndgG QC5riӻOȔTҮܘ+lMT ʳIԐeo(P.U^w]OSODYت8..|4T~,Ԗ}N{T!<ێ)UW›󋖱=ɽS'i/[.qߋo$ljsKSQ y,ugSGظٕ%wCuɒET\!le'ŏFv`P좔xޅef"VD6^.Jy&wsk͛&Hu%iNK3sL(,{vp7Iqyf(e?0/s'8Q?\OV_[q?V |Ux:𡢮<è$/OFgA\ģe9*ҋ]E/vZ{Fgy~y\DUI3e}hU-+Y e<۝QZR [YU-̙䭡ryݖA H&HވڂR᫃ͩm 8nUC q L&DƱHmΕPykjʧV\j(>U^`d/q՛ 4?8p@}tL|Ƚ+Q?/8}qc'ɒь%<)]؊Jϙ(4G we%LRyB 3cqK՗nxtݤt+ĝ:z?)g+tvRѵKSBvO}yk&ڑ3gbh 8{7oCLu vx:Q.hr1 ͋RIrVMEYNk~l0` =\ endstream endobj 44 0 obj << /Length1 1460 /Length2 6741 /Length3 0 /Length 7736 /Filter /FlateDecode >> stream xڍwTk-ҤwQ$* = ]zE  IJE.U ER G|]ޕ;{3̞wQVp5N,(jAHD pH8 EC 'ꢜZO,. ݿ(@ꂰ PNp,2 AwJ(:1 ' H GCKBPG c++pEp,juiM C`rlpP  yd 4uh_d? I'W"`( rDCNW¹P'_D(C]$ O]:h;AqX!,G4kVuVF9:pX_ 0p݁ru۲A8Yj hp~TCl8HRBDR w`v_@N/߃'ۀ{#l/ O,a= Fp+-‰?0/? 0k?#***i )("  QIyA_N6(d_5ϟ ; /]8?J7`?oW"'Ho?_uD 0}ï. NM5pkjuPtKZ,* G`np{/Z8$ ~Ez@ `W{l8~] ekP N>xkj wnP Ç={lP_꿠ߖ$2@La uIhlQֿU{K-)|ۿ_pF11IW6+ .ʐl&}LRX5H.V=oSr>̜TQ./ȡja|bXۭqzJ7 0]#mhTv|sf*T jki+/Lafk!,{NgfaIzeZ(b]{z;#\8qҽl*L_Ҝfl2xZ3*lXwM<۶[0&ago qNfRJd笄[Oep`M:Rog_aw v?}h;G@7Vdw WsM0aD%U~tuoݜȌ}1SGvM黁ED]ɥ4={ziiQt.pbJ}q TD*{:.{-8_{49}DpdF]j zj3tF skBWǻ~QM".~g= tI+&#=/( &.~5&j:* Ejz=[_27.1ܥ_B4*]$UQ\'GvTp : 5n)q:]q`ϺfH턘 m3qi~+G1xP;ιvAėQ tAbdCmjeOG9\t)w;:@u^>J {~c1Sm AqB@#W3ƠyO(`vIY#jn4U=u\8CǮ*[ p]kNu(yy24BDOA~rrgwhkmڊŮSjX6]{m4#)g 4߷7C@Ե:^Þ &K98Fa.7`Xo#X.\nǤqq*{׮=QeA+cuݍp1o⼻W˳nCiR {k~X3<5,om;(ņv#ȹ_ ͅwIM3VMC+tU͚}n}g˾QRѣݹ {E4KۑJr~h ҇wkjx"(^=?ʗ!h[t~FtbaV#AL0LlHQ~s:L[}hdYNu̡ҷ6-/zb!c[ȹ{Z'ܸH< 2,z4r'\Wh V %ּ>ϙkMHd#r#sCB c@l?)䚕7PnṔ۵YחbY> 4`ֹ'SwUOtHw#b#\ }A2OOM;2T%W#8G3o֩ ן__ԉxJamYۘ5fGCUƃGb}l $r|3 |Qf$@"~&@F385_Թ0{AiǷU6W #|'!LwO6 N&GҖkW@5Ho"3={KjoB@*LtFM<|Gv&kqs핬ׂLdK=&D& Z>M+*[j?Xd0T&iB_VS_W {d|GB!c$ ;g,Xd5hn0[f=g`HЉn"“?]99>*.RwL4E|9̒Q}T$HTrVmf5ϣ9qv}~3ORT`|:k 8kGS,5WTy&H!;!oaRLjY\;+61 pTjBonQޟSoއ9/itmsc=K-R@J«^s}ީUZY[_ɉ&ֽځ G"Js2Ny][A/)4/oHR,,{"T)D%2/X[ևμ y&CPɧd?޳ճM6a,sN\Q&¥5~ J>9x_}nEL+b*!\\)l11,;ؽ '˙Zā?{bdN˞sklgﻉIsчmtM8v:{ 3`YJ2t3gI|4;Xb岶[/cfV7>y\0k[Ԉ+ N);vVŕ$mޱJ)ܻgg_xqvf9DzwĴK@0HN;^͋rC;S ԗM7jw|n,}+\>SsvAԟ.-ct-nv=y$͛4y!)]˓(>ž79=" c}ޯ58Ыl.5uGO=_TJJXJ iج`Zƺ(;WAA-Zf 뗈xJl*xOЃ*SIڛ>aV"7e'lHWٗwMh. c_Ν(Vǽ3 u 6~0,J|T~Z$;y>l͔t~#l>Ӛg^U/A~R>JަaBoyCV{B^hE5-',c)LS$m-B>Bl @udwO9KvDt(Rp26tʌ v .cfU{ $+ hU׾afUe\VGIZXVֻnkω:ņ& o5hgMz a5> ZyT~yRݽv3j2.>txm#g܅1geVM=9'K..(rOYOT۬vf{E^OC[ gvc(y/v5̈YIG\Udx}7sr$ي-2=.¹A*, e*k%k)~$G8(wrT(!=bֵPҲiSOzHIaa{Kj#o2{$Z}BI %z sL#O"̑uk"o%{GwƟT6w9X{ 3ǴnIUb.2[6$ȋ h2>ShjQt3( mAU6+0b֭wyOnuXOzE;:&OCNnE&RQ\ouRߞ>jY *1tu2&%=|X ;zz_UYѩ>;`L]{TAHT=#qqR_Ie.ʣDqu#8e0ȖчYKm%ՙ,~nv=MR=MIUMbׯEXHI8fr}['[AxgSJ `Ѷ8FZ.2,Pr‰GFzR/rϑ.Zq L"|Wũ;{|xc)^w{տRg^zբNY?wg= mqun/_4<&)+,j״)$^?Ϣ$5qb3ΕB,Rw]^GL:uiKrbkwn0<ҡpw&VjP'vtcbn͆MCMH;gH{DȏKhDʅ .{jZnjT[:5۽d7U " x /3A-ŝܲI:g{kw,26p(,'&D"ƞً21mjO_ D>[=Wf)e%N|6a1ҁJTK #;w;Z^rR}Us¬mzdL>iX;u*;dors0oo&(ұNje1Z-zѨ'*랐Ǔ+סӸ#j>T\|)̟ R`ut:9b=D f-h8cwLQ?gnT`CHImAב+љWkt%wmY'.kG˫H8M~{ n.ϻ6E|9۬?)n('0Dp;>YJ[Tvi?l̪/ ~.6!):('-*ֲ Rk0B)K9_}yf!%$e*Njc:0׽T tO n$: -k o~S\a!~:LgFuyVBoO_"Q7G3Ķ{ .y\ilW$\N*8$B) u '63D,0> stream xڍtT>)tC ] J 03 ! !)R)Ҋ !%)|Z߷fߜ9@F-]Yk%T GyAyu"H`HG_n< @ A| $ T`XX,"A  PAnx@y+_K,&&; uYAu EXH`C"<==y!NnW[)n' iЁA]=ր_4 N?zv0?~]  0+( Pt4?`?n_g]_`++3 l`P/ !nT>sX;dԀf sF ''(?+ u|ne6vwӇ\ܡ\xlHHTD@u@~vQ:#6!0(  ]ݡaVH%: c.x Bq {e57*h+jr199 `0@g-.@> `ПnQ="_Z(o@VL)?Rrwtf'_iݑ(#P27GPkG#!(!mQd anJ0/ ie2Cn_o * J_V ?!Jlˆ>V_@\]!xGYB_0JPHT 5? ~o_z\6ф+Au/KzAgV!!mgղ)̌R^}Ycߵ uނ< Jl,gnOѧ}15&=/rCӢm.ȏg.ѓXID%M]uqr@o]脊-{)d4&UɎsϪ_{黢T<XĊt 8UKWE;i$H9Nq$ǯ`W 9mpϓ)ʐRG/La=~ۚ%!*̠;mq2Yv@tUeUpIY}X١*f~]b}=#FWOKE&~:/JyrgM)]I)wx(8)?l:!>ג/zY3vN#=Ck`»}׽擔l>1dp^$8I&E K7oi J#:|J޹MgsD<c/HiSx)̏`aE7X>_Uhr|ʽCæ]#=?8oX wЇɱXՀHD~ K:Oaļl= Q{&+$Q~G X₫=,.ZnV[[E@2[:M>g>X]ئL=JjBkkS5$0ylg~c=ZixV䭹WjfnG lV"L?-T e/8q̏+rfﯛ*L}yѫm}sߐ_R-w1*"|EUB-,tTx.f,>by,c-';-ܾ8ЄEAFQ xk~O=4ϳ$[,_ye@P3mE)JSՏ[E98*k3TUz}#hf޳p?"t/LHސ#7&9 (btV8P_.BxjإxdEרŐ,g,DZΜƝLJo=\]@İS)WCNk,x 2c }( =fz bC8AME*|D uKnjI]U{4#yH>8vq1%^ /;DZL78䬔#oqMF=<&j&HM2œgoQ0CFPaB* __ $C P*iT44 -]n>axS̻Iɒ-kbX64E1Zb0̜9ۡH-A#lPWI`N9!iSf%䂵>}&x$U%kJwtӮၵuf/HΚ7_*Hצʜ6K7B0@it Q~f-pPt!ۗýn!ʐex^-Iqо]VU48yuuBZe0}wý^'y &Clɞi("qEhԌzʛKvadkN/Wf0(rW 8ps0|߬4oĦֈ0~ ɝ[X }.\y&6SA:c=2?f~ j2!rZ3ޤm.@p{4*D^(1Ƅv-I}ޟG叭]E11Wm >|?x$0 茯x;qȅc˃5sʼngoi2756+U&;eە%-siߗa4U!~3AjwSqmn_ zWY#k+u@`婅wR]Kpd>e>LV,1@yC6xN$=ϴ6A,5]Y[1=ۖ:Cw1[H8 F#x6Xkh}c \6{gZ^̽"ʌvǞ$, gs2/+:[i׼}{J}e#8I=9̔TUx&d}*`8Ps ɻCfDrʨΫ^9k}v9=c`-xNQyʤQKğE}@|OfTr;l:7oC:1Ɛ9i2UW+v&d6H~a >Kk6uB`?@H!hIA?nzK5`CHnJj H\7{lW~(9m%}C|}# jOOŏۯ0@8 %3u 2s*Rb$a9\췾cfT4vU65rM36KEhoR_|ZNPp%i5 N3 Ďm|ASd0m]$kyvBQ㫾,ʠY})$֙<`>ӧ=ⰾrgZ) 0ۤIPk")l[JZ@A]/[G7/: 9H%(Ri]7։AQs>[ӚƐ"Z)}E^Ak`ϢA0zx/`3mQql :Jw;ͪ60@qwI(Z7fmBfk%]ydƼS4Y .1@:Y;mIJh <ք$] jv*`#/FPKʸ co;MçI)ՓWě܊_Ͽ|4P $wsɳcu|CƱՎ݄ޥʡ8rt|ś?{+=c-hzirY_t§X1C2ٺ:H/eEg,%x}bG.+tQQ.^9b}MFޡ>+_e$oyvY [+<?)^En̓ɇei$f_Vmk|3|}a?v,[[Sj{اi3Ig=,YROͩ2;mU~յ0O@8|'7=pXkr %9Z{^3sŪi\04.6H]K7Py2Wx*F~ Zg.aG<щll GZ&mGw;yhwbf7*|p=b?1yF Lɱ2[cQN=w3=燧E}9?Df%ڠs/tDh^ԕh,h+ORvXoݱG7`tfԨ^$[*uǣuk(8jqt9a -bx P&ωaSAA'܇AZ|/vB%dbUc6",5bo Xa+4@NPY7f@tA4ԊW"k{S=:9_t3p`3]Nw&*sBH2R6':E?hm-֑N2aǡwm%R6A}/-o}58N (V)/,C8$Be8xD^oSءz%WleW .$S A9Oj8WX('y uܐGp3= ᅯ/=d!ܬ ԋD`+<9*iCa:톃)sFB|4OT+>n',i33]"LkQX-̹Z_~gSkNƛ[p M˘njfj@ة휞Tӊ$D_@ZP3{;2e9#f J1]'KZW66t  #-y;]Ns>>&T[y8sLo;1glQ$۷ X8݁+RX?o ]ȞíըZ _U6L^Dp_S;d B}bob\\wxjuP[|'tsD=egM)7*d3E=u'#]~44}J:t]zn42N}oݡO{#FU2o6;h+Oij_t@,E՛*TpqP¶t:u/dfRM>ْ,&D\KPIb=!(ħOaLO̝$uN't9j7g73O T(KsUvCzY]2} V2VE`Z#E_`n֨[Z<{-YYƝC; hvϮbp>XŒu 6&2Wŗr*x|n5} KL#IZ7zf^SSbW׸tqGֻzԠ؍GOk23̌#0цCQEhˤMI-knN]אp ޚFP|Q1 b3i6Zqqo& NIܓ /gOr|+W^ f_&OhnkO|3-a0Os CjH Z@췉?ȋ}  ,vmėRhŒ8Z?(zJq I  b H4–ƾ*,ٲ1g3'o'qLd=y-oIq nJU5e"ӯ$JbTL܄8<ju>zmss}ŤN]:9/Y ʹʟvz(2*ȑk_)KJf'?Ky:GVL/&v?0^ Np-e ݨxQ?p=X'=ˍ] I(>SD/e2~7aj )Z#o&#S?yGKFl?rf96xrƛ4npZ,}LCJCkSϻ'h E<>B𣵝|*OK=>< Iqg^Odڵ/f?K /`ͬ1̊lVк{LOC'Đ3ϡ;-ٛx7B\s$~jW[D~m v5}D_f`K柖d)tumTF?-_\h4ޮ\l.>tLp~< 1 {4&NɚreFademqA=73sWs}7_?NU𪓭C2!i n0J 1y'$b˔L#~m;fB_5 4$l"Dϱ/WV;4n!:OL?) q endstream endobj 48 0 obj << /Length1 2468 /Length2 21127 /Length3 0 /Length 22557 /Filter /FlateDecode >> stream xڌp.NضmΊm[ڸac7Ncl8n3Lfuݼn= %:Pѝ `aagbaaC԰q[@tuqr@hIrVv++7  \6E&# R>hi r:]mM@PFsS{ BX;13{yy1:19Z 2lܭj@7'`ʘ(6n՝,ݽL]p@ eg? 0+w 9;98:8Z,me)&wowoCS{7'_MRSP.ݍw̿À,h!ttwCOhj?str7q]3PV& ̿k8d-U   zXY63#Ÿ 1 7@{?dZ/ 'G{?/?G'& c`0qX/7CQ17|e-{{h}Z ϒp~?\6wےoBRRKQ:Pt6s@ u7h&e Pq7g[k2{G 4ZNO `j2qXAhA.PyK'W0b7Y0KAf n `2;Y0T f bbA j(bi.Π Hj8qtNO3g D㟅>h Lݬ⷏vl ,6ewp+/*Oz Y8[l:lA};q5OdN#2҃sC_jP1Ԡ`Π=OS9X-u^sXm|@{f?wprZWDv? RZVP=j%' *)P?++g,+yvr/_B :(/T2p_o hdny_'΋qRpr_-߲k# l*mmvhHڮ$/Ӷ&بdՎ'gD)oߋNE 5D_\B (\X$7iѺz%{q_@ֿ83K:6Kg_mP1 ˀO"] μyimZ!Ϛ<#bg<Sgo(c-$<|hq3h%Q)hУ N)!y?u̕y[:_q]f M-BJ.4n~n& Z8P8#X8]yjiWj*|[S(rL r`r`T~0CzNd%2H-ZyIq|R} qRWS &|qՕ|Igd ޚU4|]X͛DžTbfisܰHsR^|'󇩻`ʁlm6CM@񋰬Q)\fV4 aО rY8#U9l]m} 0uK쳵cӂߞ2+6MsI0ىÀH|d5^QÚ%'hGS؁df_9]*l|Zb 8?81+sE 7rkziۗt`b>-9%hk_B;)"c.̦ko&v!)qWuݙ(3I]|a!&5GUT\{Yׇtzk'a_PI''TZ-FpEjM}x!"dB C*~K%Kj:XRG+wj^-/ϰ(C Akuck8z{?;GcK\.vMSi_| lX*Y*1j?(9GWÇTii/wNDϢ c(3)n0tT.GFX}4!z7{U+$ު7gl:~b>m~`Cn<PɻUZ:^ Hr3;/pHrDM셃.<,[ T7L@pE).T(K!`J,dnў4:dQ26g㵶4`bQ$&k<L7?vo/|G):j7!Y>/ chJy1w zuHoռ>CfH^y݋Cq4X*۱UWZqBlN)j.VDq)N&k⵪<y3F[I~ f8f[O @dqI뭮JlIb'i ]dw*gznQ&g+|J)gnRZHD`,Ð#80}(ش/0#))|0\g.1SI1PM{'EWfsd0.T4 +ThTjP GpZgU&Y\OUdUvy˝p--G{scR=.ܪi+jנ fGcTH9|D84üLf%"?yK#,>Ǔ@sBCU"|Ah ϝ ͵䟬(0]D~b}vۄ,uu锅e&賀G*.NkǤq9Mm\s7F+=&[`V#@z!_20m1ߚѨϑ?ل=:` Nn:-F뀛9 FYy-"sK^E|Ȩ~w[8J'I$ey8jJ EZ|Q.ijy,Ŀ*MI.s~[ƀlR5m2ܜS鞷Y[CٍG+ت (B5;<n!|$j\XTя>561#Ƕ}e jbFDjbݧލy9| P?V.b`1:Q=1El .l8& [Pi,=Dw-gZ%YrĮtMC3yUy]T Q1-6]x8b +90x⺩2yK.5V#nUHʂYڞ/9HFmܡmEN+$iXG'۱WU] ʇR4.ss>s\qWgMpN#$.NG &+WFSif)ہ#J/ե -Zx̀e0Xiұ4Գ9pY9GŒgOYb㲆aX7( ꮯ>E])~b^xBCi\]0iD!Ĝ ۨ]7lWP4>v5+Ti+u{RY&-C-`"2TioНI?`>|WX椅ޗif_o3[oe IWR[*MΑhk<64!-RN |UyH 1~0y#ꙏbF@ [J-n-™!Bq>V{%\iZ̈!dyl ޤHs|dqQvՄ“ƤZ>%ht9׬J_Jd Ig##Đ%Se;HYH<>ki7.>/9{P4\}UX1*Ii}}u8`޾ ˍȒ3yF$x8HJ{pKg7/<3!nŏ\"lqgv6~ p?ⵦ z,7P|0Y2i笄):ړuu-c E [8uE;&u!~łVr,ꎎtXvݴhz;\!|~8|ŞH0w.Ca>P4ioh ==;>zuwTM^|/fnZ7 :#^X>jfse>F0r~d q>/^3SwH6 u#"O1Aި_a3>8ҡ Ħb 83*LS >A^țL(L ~wU{u|32x~O:]dfAu#<׉ >~Twjt/GTt@?ΣzkE#]'İaG2̉L)SC'@⤤)n3oE72s!˪EɎ$"_~>?L,,ϞyD\#yV6tM3OF'΍ҳK\ Gx}aLD5,z$D_y!+M)˳A@C=*^BdC,ԷgwARZ 8nUүE[F̭.MP#qUzU"Hw@̬GK"B&{Bo#os- yvԀRqטaoâGx/ RTcf;og'5ZP  ^! 3{O2s\o/SsTه[HoH*lW)[aF~djm{ֵt \|m6 yt?%ŏƠDUFiScP+{-37sGX\dIP V]w^zK{&òMJ*R:4#qW bR$8;D]x5h`o%{—%WmNRY޽a:ICQ" h\$kB|#ZO#B%t,8d~Kp+߮F<-!Ǘ"~lJ~A },~Œv۲%/n]ÎuB,tsBflIBKV:qPлu (PoRnj) w,ep6 hѲ,mֈC\Mxc~3Ֆu[k vMt+g=g7mhB Rnß+usFއ6W/ d7Ziwk4Q`hcw#H]u}#p]E-dߙ0G 0| Q_b9&B׵Ub'jD+pqf%/ؖdT{qkuGF0$ 3|3bPm; <6&=? *"_(q'y9!quMs R7T1= y߬TpV-VR/Sv*zV`]M Bz~$jDP{(X&O6 }Nߪ݋ߊ@>E#7eA ~@O ΀RJgt`=(."î긳00b#N e3gq 4@<k/h] ~hB9a ~MHOoz]yU=͟lj53(Zԫ?\EoRe\T-c<F嘿P(1HYS|k|@wެMsb Vϓ)ffsH(J2P槳oBpʧPgv^~k_-'y|~Zo:l&͔j'똭5,ߩGFsY 1s}X1Xw 2;A8N.dEu'A M oM?5}A>!\uH{hSZBd'Cif3CHj8OJRCyxե`ڥ鸾vv\ o l*Ν=3< "H kN+:R룊,x=C]O$gD $ep 00?s> m_QUyo %iA*٪ثrkOp!AƇ<wP`yjRERБ0*~u^Dr ; ˬeeu|:wCFܼ[ダāIA%lܹqYJO&QگykÔ|d?YG 1}^6pΡU eͲL{?iq˺sq_| As\qhMoc8GG@Z*{wTK!鏻KRS3ޭ}hZ/GPYdvG4"o j QDz}I9`GTW"^0ZJଭNLM I@0FAW"y&'@d툣ˡm䤇tݺKȮb#ȷV{}:-s=y!>u13'C[)Éj)3w͛~Yka_^ L릍}I/SjzC[wL^˃}U2X˽QV))0._-bY45k+$f:Er0O?~`=*_댽Gj<7}2(S7$cCwF#z1y4gc2kOJ :7TdDYd◁njX"6kϥ,j&<Eby`aƒ!LE,VʞL&)neyT#dg[a+ȡc@~{XT1{u$i185]Ydn=cw/恸\8aɂ)|Nܳiy46`阧HoDѥ?9Zr1NL|/I8ؙUR_5㰪$ϳ#Z,ԛ1tOT˪"J38Vn ?G{sZb<1z C v;({V~BHx'[(t# O@r#lתt`lG7%UV~&fI~EaXWM[SwUT-^:ͦEsM{ޘ1 [-)Wރ]IcTU2;|>-J*Ӱ*|h\EFQ܌&ZtQr)yxTV/]Gfuy=rqì0# /G)^y)#{IO2lVl_ euNIt(V`mUK/鵜NȼGX` x X議[O()F_,maB#@@RP@>z?tܢE \R$,7䡉7|;Tj( t~w153_DB_S❕雩M`<Q NNF &¾"@_%Eą[u3h,u2L䙰tvM^CGv"cfVFGc}\4l%eW(k6'v\7aYz7 g달w_v˯.Gt陶 zn-O4YzBϬ?gQ0rCKAdnN-Oud\[}7Ejuj@[4s8k82r{dv'Ig$z Yk?'?TҌwrs=So~LyLàB$y}uTlzh;o~&-7fPׂQAAG+mA w~bj<6cGRE-#}~[3SsGrzѓskf=@¹ 0NCrPǡ[]u҆$Y9VEfvFA|1? !Ҝj5+QdDr p M8 Ktx0s&w$2yC\qE% RKbkwKv,gNނ9JJTBt)t@v!9\&9{;|ML ~$yCF`Cw+.4D , 9"q1xIP86`/x7c2aa|J6$zúnR1D}Q=?*p)ܘA8&̸ϯ!400 !)[.6RNQLR6HWFԳTF/!K[^J^ neĔ~r >lv۰֒a V g}ѪT"**N`RO4m tsq@9t5Eeͯd·犬EM]C轤'rI,H$|aqǗo? [րZ3=k<@/ܟƙAv˸p$[&ډY_-$$0Pwܺ ,5 >b]XX:RXՂ+,U OegqqLY 5˧]iz$6ik/6݅k|NɏO=UnFtC~IO&߼{M60aMSJdeQrg{b- HhR[n\@[!5=B$Hl PlZjg7_fWq̟~&lAiٝ) X;4W}v{ ]{a|A\;Pt 5D7j~t4\7=+7<9wյfCt4caH/īDfѥt\Yt"aAuH,'=c*d _Sܨ_wP %bM|=wE7B;YZq!Ogr7v#N3"z/d֨t\@oIfuv=>hKjϏφ桞':vƒrw^yp,oX;n*,/uN\A؎29}bbPй+0X1sCdAVN2!ʵi 9+c(/ ]AmȒ10p?eKձlu>n`R>N9MzUșv꟰/|7ݗ1,8A CVή#~i.80&+ng%K#Yz-g1|EۜP~y )1\FU C&}r7嵼| OS*$"]弡}~NȂ_-]*|"46l'uf\&db\\GSwڌzN8aH'|'O,wwUƃq~pMth*S)mbVrYlQõC?H7t7'f3 PՖsafp&8T|Sn齉/ )2tIP<Q\tv ( Yh")<9~w/k g`gOC}&|_پڱ#-[5O.c4h”-W4FzB'/MeL~ZV 1RmsU[*=-9ջNm5؂t(o@V5ǿ2O`2kKNXv؜+F, j)"GKD3X `>XY^NJP=6}{1NW-dIp~]0XAHCO1n jD.AIMT[e Jt(Vp~De WvtMB>ֹ[]= u&^+mC3mvMG,my6y7ZqBQLo6 Zʼns)HQV]=/et {H2dweHk%9JXEŪkwa1mLew$7慸ܠүC p`TL;Q=r%TG@bBI9Ku <#b~j8i霆'nސrLpYW`4pF\QYb ;ơSC bPyn};>rٖG7}֒VKC0WJOR>jxD"83Hgyif2`a0!Vʳi2-5{,$wt&߱ͱv ͮԴPђMM&r:R;_́޷.{oYp'kKQꨗ`u lW@@RmBnsrVRT 23:^jdX./ o' eoWdL]#X pj)L閑EkV+y޿ $>%;v\>q-xrՔuq% A)!Rh Bc$wq_A=LF{ 41g#cpѻYЖJH5Gl VQY&H,OoN(&2GV馡QR_GyCFsUY{/uHJ]D/1?f׭W i`|TɊ&^Xw)ͳҘtK_FƎ׆R*aݒM ɨ`pEQnsWLt`﷨@]5/·l$|ˏ=h=F랃Y#W%R$݆h͕5)"6)uܕFL懄$pfEj/0Vc$f)r/fJPbJ.#b՝WK}N㷃1!ϔ罾D3^uEjF|>w0qb@A+:B7vDuy+$TIFpɵa\~~H6ކ3 [YU噦q.ã攙SĖG88[ޱu`i mDιey< kmI=w5Z 7?(Ǥ$DhP1ѵߪ]ef:ivrL/^aj7\d_ElVܵ+6<,]/ 敻eSXR]/*`Ўqv{x\ntբVK^~jЈqء4홅Q9/ƌ[}$>^;[%>BApx5\t"w nitńe$: bf M=%$=Z$QSHqEKG#9^(:vYp >(K} Yyɓ,8%ʔ!Uv:KtV4\ c(zNdž*%OͤؼmyEmYAXgp7m%4+wJ'/٩ȮH1;kkAꋧ{rwH @ Z \o6lt46ԃy;^ s _ V?az_RC̑&h-'+Wb,}f-$}B/9 hyfwKaocuﴡQC{] uG3k :Oew43n5O\f'R5^ɖ?SEY6nNܨJ~DRHqvmឮP +'qɲ,_6uu^'cҹ[KP@B,@_IZ."S6 ] \Ps3K~:qd/,Žiׁ ,8;>dN ܍O^iu\ !p|J 8cxb]&'~E37u(0fgW05؛ڧN 2d GTvX#,}!RF]٥|?iaV>A4vŠ?cpOʓxE-`tH=y7cH]of8dV՟CZdd߆/FA>NH~b}-gߺQ ]BJ)`Ѡ(O,@''QcAִfĨ6߇x2}&!Eٳ fhAJSLK;4pToqyЪZA@Sx G^|A( :7잩CaDŽrض/nJN;WГEܻ?K$"P;\^K-Ȁ)Lv=Q;Ӵ.l7lduB0}TIwx8GR ӣhwon1~X&x")/MY1dtդˇS9lƔt"`,,8(F˄ ~To2f{|֡_hJl`zelZ˽ЈM$x=abZ u4[yJ̘s P2P0j7&b1w)f50A('jp\-6T+T?j S i3|o9>H~o2uṨ"wJh~wA{H|Z t/ɤN8hݑlKԑ e5^w/MD{Ìֿ]rU\Fғ>s#f3gjܼjC4M!d#udJRD:'ָW0/ˡ\h_M󉒤DW:]IG6#V[>Q;,lT>6b/6~XS}sCRC^4RnZ[O=ˤ;U(ϝP BKV9 hX'h{ܣ ,DA]Q+ڈd7ɨ^&Jܡh'2r$zBƷ&_W<v~Pt"ks7]Ϝ!Z3_CA)4iµ7>YPEUEк CQ_wq+^~.}! dȹU1TX[օ4D 7>u柋{#i~!*=Ie_D-qss8mzLꋞ^_?~IZew n t~ĆK6]!K5,z"Vj<%i]ƠKȥSқUc'ZYVCV.!<~&\rxG% įÀ% .&W~Z] @)r3B74тoQjM?0C!mǙ5C@bJnʧ`5GJYIEhaŭ06wt˩rz$0;%WCn z]#8R_B+Kn?EĹ*`?uأt@!xVu;qmV[xXH&Ύ8G7I|lCoX-?Qe]ug9Ah;O<*j)&%+OnH*6~7~1f6H*3~4-I>1]{͝_pʞHZ (Qz/б{UF`<6Gs.eZe A6׌6ƶ/[#,A~FyR?ыto4Sލk]1k: cؿ~| HA|*炓5X8j *nmm-~hcRIh`)(g Ϭ+M񜜡ͮ,l+U:ZbV[ܢ&>uA"U|m OBK[rY tFbսB'\؏KM[f5pбN|hnCΘIӽ\'-O1^lb4g8Ph:.Xn 밝Mr6So C9y{WYx{P۠--KHa'7IbooUL1) RvpIo\[7; 7Oʘ{R I0<^A((mj]=3 5 }Nkʗa Ň[×ͷ$i/ ʄC|.A61\Q$yUNDN(l- 4Su D B5/Z/!푩AZmG_&4߶j?#ǘ!Ak?pkBqu+~URRhMh)PVٻS/bᒡ `vO-ĝVk[ǑT̸4}Pv4|beIO ~nu!7(^ul.NWgߌSQrjJOdCVR.ao$w bOm}+nVclohJE:^hw2k7c!bG@wkE׻Ɣg= ˳)!n0b~2%X 'VC%ˈP@3#`B PzzV?R'M~zo^9Co'[ q0^JKʈ Yu`aQa8D't6@G! UH# ]]fЩ zGpƈ6"a?x/Ldn0b:5'L3TlZQF>K:SP9khO_Z"p}mǫ7au$pnEz@͒AL$n2Wк`(z$M 2t! $ډ_JlSR;uTS`/bI;Pgfܙf<yTS3w}@/w| h _8%N xtz_s]J'cq ڕ¬COഢ!( =@cv>6i (8WJ\ /g=&br髷y&Vy!P:)qIhMe,knCMz&eD9u񫶡x8h7~fO")+Du$, %Q uPuoQ-фD> Z;s嗈u6-w؃ogUޠEepɋοG؏U ݼ(3mUWZV:}ept2,p#']JAbq#"hչ2Xj/Ę}H^7T=*ά 5Q9\)g3߭ObZ^5 ɰ/ucm#nmu_ŰOzM­T0}*d'қ&1-2JZǮ"򶟻pC2.}5yE(\_ Qا\lm|ܥf*|{%dl7t/?OH '{y#Myi%öqBBМ{5,HlVM]X`FO ӦQ\GbAK=8mzO?0ܰ07՜© an+*&n*E^O~$^_okdR{ lF֠Fna2r[_uoʄ`?9qBU'3nl591bZ)ñF.9y řwHa`<03GꮨQnuЉ iSewE&/v0gĤE[z ?K9(>Sdy: nFp`p:vdp`hrFu*f-D^gK,:s\iB"?G쭘߳;:~KOERf}"7#y5XwЁђdgصmAPڥZǪ4E bPsd)CR|7qzGqK~(6f6 / HȌ9%@fHi8viE%HaP_EͲ%F&0C+$'|*N];dwy˩uBYqȝI([l={ޣTS#BHt!eĻ3y:8{&Q`-RyXoLϷkPJeCMc^۝>~e‰z8%q^v!!$ 8Rq&6 6k-3[ʖ dh'؊ Z {Sg@G2*6U ;[t6=k"j%:}9Jnp gh4b:lJXMjvՏ٦EX#h,<ðɮZ,w%Fre޻#jYpbRb˫3pC endstream endobj 50 0 obj << /Length1 1376 /Length2 6157 /Length3 0 /Length 7101 /Filter /FlateDecode >> stream xڍVT]$id$RPSjf`aȡQJ@RARZPBBI %U@w޵]Z8{<F'倪&2@0XB !q?(FbrͮCqwᦏAu|< DZ"#`9br@5//~Ug⊻8_KLTFH ԇ\ᨋaP)B+)AQbsX_3W@( 11?6 p~P,xx apE^ 4z8 D tEJD`'DH8PCO Bοޘx/up]8l ^Ww0,-!WKVG;bP(8 U ]zX?4D;#~ 2G#|jy\@18(ed@sJn m/{b<H{C}@ .H40g1y,x|A<[G ҷ40M** ^T\(*.B @E?AU;Ve{qK* % A?s`.  Mp[vw_Yg=> E!=_w}}̅j #W}3VmBh_"[w6B`7%0$nFE!`.Tshx_ ~!aK\Jb_준xȅ  1E{@ 5O)q B 犅#8 ^(7. p8 05ݼV}A2(CAQ爆,MEVv_9㒺4)~%YtSqqЉCɻf5=\fJ+A^Ačm:^>7h5kzfFM,֥<){/`o^<Ɵd'E.İO;?ʐ3|έ, H(̋'~zf&fE5Z|IlOQv;͐E [U}WW\~nQ6 E4c*ӟVxIrB\e u({]^~z*޴/4.[z(51ːuC(yO *#&Ũ@SpB"c_^{Ddg.>^{pk(ZG "k")+aޞ-cRSq3qQ$wĥ9n&+9Xa[* mJņ>ʯۗ-XZٟ|}Y`ngĆhb#i(fu7C?3&=xA;υ,*90\ Q8.شW&a2)YѸjgYۺ;~y_R& '}SwID;m. W.md(&x:;$s׈喐on>>L`NZxa=& ֆ( \ 9 Fx{#*s# x#yiu"ɽ17{!B~MgO׶x_FȖ$Q$(R&&}-ʇCc+kW(./YJ1POy oj|nUqkY}e1֞p]6꺑[&2\ kt\Vah$6 m·uN.a2p,/g̃G0yJ?nh~/蓒~\,iN3:P;ebfCC;EڭDٷKUCi i%k(b m3Kh :X?v=TƎ$Vgs%v23毾|&t(_!q {IXګ^Iպ 6YoHpH0MuJa]Ys~4F~a:z 6"<"͇^ՠ{Tx}^3#ɛڟHrgӑ~tuu׉ˊz?/۔sYٕEAb1b&(I&&?F !]q[ʂ:A[ pFrў n3fV@/>:d7o(V{T.{MU}ep2.<sjb<|Mo#dY) ? ؗ[t2̘M~K+1e+i.9k غjTW&=mwЎNwUiZ%&/8m_}t`W2Zhtsw&ۡR6Wx[m7molKl-ߖT ۭ1G[l[ʗ9#Pyvn,ڀ;[I 6WmֶMCIRsh*Xj7^>GЀղ4ZÃ"?uOrAA'v6Vg >{WZX)f0S_?NX[8 %ӨpRN޿'e`ODo06*hBR\UY {WB6W@z \iTrW"uTFWnI$R`|pi53F$|ʡVJ@CHHO/1 v8L&R*d`Oxv1+y0$dZ`xLzuHf{AmŚ;2w-!;ϩn-Ac)uZr?~}I=G|7wVWE7Ks.:ɔTCI$oPr"С&{iZ%%LsX' ? O9Mhye %YT:-aQa ȯg5K5ss|0FlmG p9\aaKj8sxqnepZH|0U|vu0WCKӐ434V]~7ӎQ?7=KeoۆwOU Orɤql6IiK=̊Tè<ȒE;]lݮe2ylhRF7y[3 r:sTeAaȶ9Es/:K[ڳ7-sxڻJT;`"BADJ2XS6 ӵ;'}段vʲ g%u [UF;Q.G`{o|)-1f|QQw z6N_i'  >͓ ʧCs_ w+e=AL]ziJ(1Ռ]TcRUӝ|OѶs۾a|ad욎ɶ5SZo%u(%h8/'Cfv+ȱ^@]6ڢhcRhd>&R;ћ7J5~NJ[kӥ&iLud* Ss krfˬ%41]I"SQҪSHvS'mZ~ff\M+߮rU8_`Fiڳ,)BȣOE|Na=o;yK4wk~$foDx MDYiO}i pnyRSYN6 2٢X5>rՔ+Lu񫶛#X&O1>63$g (yBi|ODŽ߾HwOɂh:WL}rրos wl[iT͝V)ĸoAH|kϣu) o35jCZ 'm>5Wpd5u/5U{zA pL׊OvcJ73h[;:Bi5jG4Ux=i$D'W菧V(Q>%0S\(g@EʛSB$}J62ƁyՅcf:R~Gy?URx~/GB :$YwJ%+gq~{a?_ACT Nahp 4pW h}mFg%[f:bF wnZݥS"'N\B Jft("%j!Gbr9Wُm {&_坣6~BZh Ype-BѽAYM1OȾFINXtY@z;RV}g Ft P Wbk4¸ٕuV/KRܚ )`tLu+kv7jLl:rttQdS`?NAw稖\ƤJDv{/ٌ] A,#δXOO Df6w0W1;6&@„;gdnVgswvHr]TJMTq%4NHr'sO GVR'Lx@O*b8$bl<^Rk t|kb3=(7j)rZ&x2ns4F h2#圹 쪊=bHo ^ 0k6^ Rm'~MWuPbڻSp*A_1x0) 6iI̒R|y0j((zGڝb}x|ȳ%{+HGYϔy>04vϠ%kbihA.#Al@qw4bfEtƚES'0_la*鷾{ IR+djlפbp;B˒Dti /Iܸv>(s 웋lP d0ٻ32iݪo%PpλfnzX wޅ&Oљ6rq~$)o*eOh/r[~-BoxJLceQU=*R`)sc0(!-Q>3i@D5NǵȏAm1SGm%&3&Gb?oW*&>lu@`ޢm~RxstD3^ѽGL1xHCxlVh9K8)kpԁaaҝ%ֻ>Rc4*[8a'ϫkZCE>kǪgno`"j-Dms荻_e&^„d N~i!F}w{ᩍ4W 8} endstream endobj 52 0 obj << /Length1 2586 /Length2 17047 /Length3 0 /Length 18547 /Filter /FlateDecode >> stream xڌweT\۲.i]w݂CA^gswxǠJg\ JR%U;#3 #3/@L^UNM̊@Ifl AttGh :vYk ?D;G^ @ kg tBp03wEjc _-@hhlh P3:{ j~sgg{^&&777FC'F;G3Az9@tt~ P0S#%@o#X[m@F.&@G(>@UFh,7XYqߎ,l264675ZXrC[DCk';WIe!TdhadaJn@5:;!Oh ?GlekfZؚ.Ş飭 PF?, `ff,t76gDT=T Br2t]>^V7B`aX;f@ӿ1h -̠!d0K4g&v44?Esx12X,\NN;R2O"25/Q?{Bo_ v̻31e^?$bm(?8A{&.6[+lZ [3h30s-pp(Y85&?^;k [t]3]'NKG53sCGGCfPrpX@itkLv B#08LI0A<&3Ib0IA&? $Ax(S@ P?}@A< dbE7Mț?rvrX8Y!\XXAy9sGc _| (5hIOǍOmlޘY[:).cj.Aqқ~Mj'qP=́bd,A'i/ş9AE[ި?zP% e g @9ݟA.@AuQ\ڃu,A &Y9vP]3MOM k 휁&F:_f-;qNֆN""gr6wk&@9_|AP5 kA{AP=$ t;݁..:]z ݁+v|"n l\Hb!oSrĕ!!C %bܬxD;a10C0 #x.ᨧ w~/LuxC_~+k&85O̻n opZl.#3tԟsN"cb>TB?Nh EزzٱtH[ݛu¾$g~#>B6 ˆ짊Cs ="#xCwtԙGv|%$մ;X ݓV xf]u.{+=BgX4[.`;o$ΟcKmb@&~uN͇'Jg aw<KѦak}l%j;4㞏Ls#x&Nr]](̯\4% ۞f.ldf| oӪaxArW=1I jiQ=)ɳ|N+R沷ȏ/wnGj! gD}mPԛ{o^qk^Au_D[@R }O>`9 ;]??r8( ]jRZO ?3V~/,jx=4{48$|@vF7(w%3Z}AP 8h  n4~zUcuaq']qkwl UvSFOZ,|eWcG%Oc]մ{.ce cHV_Jw7e%?A9 )ߢ(f-r'!~մr#/Zࠇ!? =1)C{ΑέMu^Wy }׻D<TLONWqqcKIZ 8gm@*#xj J }Ժ12WaeLt1Rf;ثM*bn)[dLj`h& jѦay22Pz3]_K:YH:w^P+yNNϿT82.SIpLؐ5Q+l@1'5evo||Ʊa$бP:TgdL, [.bq׋jEu8H1B!WE 3k@XM70:f뀋ș^mQg"֯BG1nY]$\ѭ7 Ci t-]P1/:]Y{ޭ?^.Z)N ˍ*Jy'}NϔxnSSc]͘>?C+밧Į5*2|jpuQydCǂV/|V4c~۽WHwգW^w(v* &;Ax]кX=lMyXf5C X5M);m0L|̋O;&p/Txy?F\䦕!-~*[P.am3'}lFM q3>[Ѓ '2haiJY@s3;|Yq/d6w+;'F,yKZ\Ȉ#DNbʕ;D̖kRv[tLNu+';ͨhI ّ˹BYlsz1@2(ɐ<z]/:qg/]wgGDYH2JYWo\B | Lm ȵgv GkuUM]F\e~noN=7Pö$U~;B3>Ei~MI,uKێ#*^v[BDȚq z 3Gz;Eb&w!& ]>DE'p=2ḻ![QEQwjd S貰Rȫe+2TXoNs*z[sIrW#6YB,+lQ wf5>C %rq!%@3~9C~0ЯeH 09jO}BoWp2Zbؔ" 9wIa ~)E]PLZ V3\=cgHD Ðt8'gX+EDe@q6m$g|%w$}si&k0 {gM gl0[; Xt&#g^zXk4qX)AV}0_0ֺ''iW2x9$|j{M7 /Mm:x2KIw0_prC`@R {C`uRAr ̘+| X[#WeNCW_G^sτ鰳40 $eCټ*BGOG!6>V;~|1OuI8GLFY"l5MդіA/9ۈ4ꜰ)̝4_⟀{Dh,${2[(֌/s _%%9b"^F "uD-&Y%JXv9] ^"ġ-=_{44Zs=:-R mr<ە |{ P -HqzDž3_0j >%Z7Dq5-I%!çj :pA2c<+:X$_EoVB~ I2SKs)d$JWɕ/ZAž(&]miWe#:}~(z,Ǩ9m~ c" gkfip_X5 %@rt:)don,M,mHl/{чӚBm}w@)AW+ě-v.SG5-X\lY[ ?؀:C+mkO`rnn&O-^Ah?F3`a5ٹ▾MÎ㒰̅{Sݞp+wò>'7ǐ˨~_q Lq'hL:;ƯwuRH, @Z5~4P,L6Ѡn`*r FvGO].QjWc <.uUMmoS>:ZXYtåN XYDvKV&LnAdH'{cۦvL7(4]Гx7 >X#[CCÂ!D"SWʘdVk.U뇘k ⺦Gjy.cw\^]%0ZXR +J} Zw KA 3[sR2>C-OBc6;k"GtkFr>du0FfC5b(}uUwȝ&%w6z lRRYrv`]ŵzcV-l b܋!Lge2XK e"Ha65ld^l6ߤ!?Dvޞ;c|?0eKi c/2`Nw9 2V_BAODCuF[.f|2C16R]Z _ܧ|uVh^1lj4uE F|ҴZ%-SQ댥/ sD}t-Mva!o9V+ & 8y ^"25|җ< WDsMyB"[L`PJ ̰޹/͵(ܬ.TW-<9 +& ,oT@TfVyjȜslj?#$-GNqCA !1H$Yp!:,4}v[ +[tO{vl.GѽRuܸ*2mD~VK#u߯=eIHH#z!rt1,5xsqⶓ>ߍ Oc8aا}Nk b,—pwtAv!%'+e 6Tؤ]GNG[V{]b!x *Doeʲ/#ZT: p;T<_ߊ}m_Yý*oT8\UEQEAL3ߤf# <ûDH>#ITU/&_u`~pKf0Qcω+0aų*G:QK$N؜f9i9ĀcJ`(xoxS+}x)I+12-Tg^ɪV2i5;)GU'^+ǝU4Ǎĺs:YG 1=^K 8[#V.cNHwGu+ZΈv: ]I΍w|$!E;a<05Gsbdںe̢.y? <["b[S9 bbkoZb |hlFf[x*ګ^M]iBFEAhd%c;ىƪڥ*H_!+Ji&[bf|gG6NhViwY{7FӐM8BCLo%^`ho^-"FD.|8.V4R *l.)Fn⟪ϮGnе|ֵY)RV1z]~: $q؛34qXgP8̭9A"DB-|"LϏhQ0Y.hOptzi}dqXt?ЂB'k_~bu[8k^eimFg.cg2c\wCluw)WLj/' hQ5yE ̟!!o=o.1D'Ncع<4Am '􁫀~'ݝth2[:;ִ:EndSnFxy[9W(JtM Sl6fP7)JX~H|Ų±v/”dNȵr#.J DJ y8xT9Ŭ wusg^]RoږzיZA.Q( 7X`ke8=W\~3> ݅7ҾntϠrg`B 9wkB?nw5mz$e'- \r4Xc٧i"\ N;B4Eo]f$JI4ʄXy8N0߷iHF pOkq!Z6Gs[{rWeѸ۲ e'KD(\<`6x.;ЇqX[i[.߰Z4]V36*sٷꚋ\e53a}kW<-Τ~NGK GNZ&PBUo=JiTГOhcBtX>xFvb5LGOw>AtltŐ ͮ#*@U+ mۛ_{zI{#=~lv7} 4 Q AĦU~Au*.4 _׭)*фz &E._:hQN֮b/ţaBC)$y PZ67qJ 1I< :nlk7këMtUQ#WДZ4;j`:ެ:vأVɗA/dx˄~2/E39ݮc 'D.8PlҪxhz5R])ӄ[U 9Q}](Q3"Ou@Z$*~? SE bz"9-aɃkگ>veu4|kv :K |T*m$n1o-g5nIj-vvCmfWbVG5Sj:J0&qm쓝`rV+{~@+.L#}"PsקUdeC5pC~:/fL= 0i:bI"pA/TiBPČ ZbO?uC\îRcD,M(~hƋs@:i-gf=%-^83D E$<Ƒe&z.M ߼Ҫ> /_#V8^RUf/YmUkוޫ4hr1ՉLfNSL#HfK?]*hUK+~_zؓ;rvgW"mʟU7' X+(#KqH1JzV{U42n*Z:B5zOkƆ2VXfX/#;G|qE EiIڥX7Qq{uFJ}- ##܍K=͍cGțkKZQɈ)[8f؇n}sx3nKa4yH!eEq|1e`R_QxܽɐgJp ?nS̰{)ov "2w$r4CCBENfg_jȾD!F6h{!/r[FHX)7 9!<ЀgPt=%}O ƟytvH7ph"lTXp6@ 1hUܾ3BKh%.}WhW -ME"~ћyEoD`}1ʨʪpgA[NW.ˏ.oG-{{>-K[ũB%܊:*1G̾;RJm *j/1edS"O/CӵX\%..DG&nR)Ry`CLtFS=/I,s\ՙPgVE9Pc R߅~bHIPa,<&0rζ7c8Y"@l̷6z|K@q]mS@w?U [o<֤W&<@͐oܸtkbg7c9@F5KRuNiJu,3><Y͊UcHbe6&+ ]x0UIH m1g-lDB-a(GL(-XH,lNt|5vw8 M䑦r7ƓkI-8B)qј5x% UyyeO69j 8>Mn Iq?dPJeD2R[!yj^㿜kZ#䮇,t|xKZ[Mru)$o_dW '-rb dM[s[TkMO<~RLG{e{!id@0a.=#C [XS7M޶]/״Տ/= ok"\+pr.W$t}{p}'WDŽ8St?_ֺ$'9NoxScZ/.L5B匋yo]5#TP,Mf-_ 8 Щk0v\ֳ;0cOa|Ρ p`-ԈQ{Q v=G[4XgdVz}QA?ZUq#0O) B˻(F?hΕҒ،;r,u2/a$@o&8Of4(&9U*O՞$.u(},BzF Vxϼ};V}݊ !<倐 0wg ѻQQ͸0"u{6nLd?,{5zZx>/lmi` `7z(Ol5b3* 07d_aa{So*JXn2csd mYUA\H X=ƻ*ԯ5TwR|\ڲi.*ssy|#71#gw &am\U6QgǮ@T4=ͳǏF+Ǯ)Bg͈X%o?ߣ!N\qㅍej\OzyP``#Lu'|N5u(I(}Xy9Y|,i54*w6\O½3PAWIɥ$3 gMl=iƉs'ZaVO"Q"~)Niwy=AzѠ|v37}MpR)>oaJhI9.5N9Հ=IC\\x|ss|Q }hP*[rk̀]y I]E (OcY0u8cоš#;b4H^/ٿ5}VgR gb<bM'jn~RoU8 |C}1Kng)@hLbQKa@x_ςж%5+r(-RB`Aag㩽UaIKSK/ڈE;@hJ^>3r݉}k"5Ԝ%: ڹ Fo_>-ZAwc:en Y;W٦Ҹ淘hS:~zd@7Sv 'j.k49!on7eYL)cJ{?w | 7pmK!b%9-$B!Pll a)|Q4I*ԝo?BnN1 `닿Z 5]l%;~‡CtY9^%x1% ɰ>{'C ƗN9>WdμolNv!>흣mMYefӶqB0zwWxCwmۮbяx'p"k$BBt \u+@i SI;ǓqPx8 '29{ lrM"ZSΞ™a*ƺgJh`?C֫k\BZQňk]OIR6L^GkiУ!Wg%u/ a89 TIH7d38.lKa l\= UZU-p 6h <5!䤒%*W:57C-lxh:O=kn1yCg]NQWZar#/#mO!pZ\`ϵыR-Aՙ6m`v4XPgapyBg)q4um6bLĵq`cP-+ ױޓF'ŝ;0#f/[C^8ڍjd璦opf쑴)SGj_%a*)=_}]⿈n\~9/^o&} ݟirԅr9G/K>_foa?aGQژw$mbP;n3-_Xg.}k=qBi:ǫטܼWr2`Cſ p4{ ܏?Ք8 26xef?!~e_51߯mfVdźMPg#( =ZO%Nɱ458`{0G!uCeo7 o% WҤ\n u]m#$ݙR["Ma/"2fE㶸NZ~?Ӳ4i]]*[]G-.3W*%u1Kڂ]N2fEc$s\(<+,ӛb ۭD/}M0x#-ګ4IZwl`ax*Z&iO7 i[GebE!h i:Šְ"÷ po@P!%~m*Ru+Mp -7*$2"2ߖ+"O%WE-p;JF AejwZ^Gq7Uւytj_ fI762{ަ(4M9-  hC n~^uzKgiEEAOUeI.\ 0S\ UyW;NN)&]in:._ri~ѩVG4Hq܄^ݨwu"{uAjB?=O?n;oXv mG4D?1kAH-K:~Kiܼ[LԔ3|j\#5{ىؠmN޺3'L:U"sr{ѳHm |?D2D4rKnۖ6!mK} [=a/GjIXAiqޛ3/Rb'xHܗ 2ii?Aq' }Zy}hu}KXu ?Z@VW@K#/h1ȽtD n$xژo5LU5vMd Wip<B(|M"^%Ixfm_q,F 4oIW/9,l*5 %w DTb[ݒE~}ؠQG=\)>W*x4ܙ@H3Rw`ѳiŀ$w/zcۃ^_E^ZڙϖZ;=nk>esyLHMiGWk|LTeWUbkq;zCӲ4ߴ?sIN;MWO(jm*6Noo!>3GMa+#WŻ։BʔN xx/Z^mg}zvjPKO^IAf73nGf2Cr}Mudĺi_qlȢFzZw?t1k[ 喚i$<-wػy-\\k8\_v~ U|95],5֭[/T}bY&S.`ŒP/(eCA=#2Ѩ04c@ K|4WtP`T(K&(;mL8USrj4:;)EQљU L 2|\_I΁+ Ռf'k]x)v|WFR-z+b5RaE$[Nɠn 5'HD|I+8dJPb@z&Vvq"˞ƌiv?%*(_dĬ]?4/"_t\B̿oҀ_JTcZI-> ,ʗ&FRZ;ex9^J]#Cxb/E{6# _`fόʱ#`:[\;~3ASʞqt~UȒ1`>)>!8<+D NZ> NZMfv:X3wt7lQqY۱ dY|urKQ{Q+Tt&ť;p {js ٥0[&_oXA(a{vp\ÿGӏ-S^`>9?gj5}͝ k#()M'?7Z?Riϟ㟊Ə;bxcf}A֪֔:< fݙ`؇Bk{v7OD#P9`{`ͮ~S<R-v\} Q )Rbݐ1ÄyȣZQGQvK& b*rA[%CĆ܌(0JE΋ѩ{Yd4ѫWYQz&^xq7"u$q_y1zAJd?hw zM#uE3AuՒFK3NPm{"W!*ߦUŕhm`DAā!1J  pwtBZv( ŦH:SnlK_y4Yo۽ثq mϕl @s9#5P#h۷\6r5k}M&%`ί^"Hbg endstream endobj 54 0 obj << /Length1 1396 /Length2 5966 /Length3 0 /Length 6925 /Filter /FlateDecode >> stream xڍx4\ڶF{'ä5z.D'A5BѢE 7ɛ_֬g?}ykϬfh"t#hAPgb@Q! PCqCP0$B?*^P0cS1@=$ D qi4"`@OD@QD*HO/+S[Pz `@vz`*Bp G{^._` EA|N_#? qL]a&Hg/ 0„x#^Lu. @gs !п @`? pu]!ZF8($&u%#3P/'%(+ fN*H("՟* 컿uG }a'_c8y{ !`Z0ѿm.P4@ (%..@~W_L=͘=g`3E@h/ohp:"N0u!c:Ɯ`0'$o#757S3H?@@PJ@ 1@?aX-3 W}e?#^?s#1̅xM{@1 s;_YWwGpo?_ 0FcThP _Ճ:=۫cԠp0ZtWx/; :׿X0z`aDq\Z~cOdE幕.v&o_8$概Dl3C(YJJt^(T .qy*WIJdsBչhdφakΕ gUI{/DEKӉ`8ʅ-l0yJ|n+=錢@h>Y;%m3|KI1w!IkX3r.=%~8ɈVsx!`̪x_ϫ ,Ŋ{9锲ˆIOFC|Rg9]*NV="%|H-Pd銥ibvGq6&Z̧iNҨĥiONNwc&(T]?n)g_\(D8s/=yh aLo@0H玠tC'ꮻ93@Z!R2'w9|ql޳>|1>=snWɱ-$<3y+2pښo.f({`aq6˄X&y;$HI8G\YbcN4'a=j꼊VL0$80vjpXh&!S.u<^\o8XM_uʉVsTj{_JYk~c|(\k{0+@.g4鵯7y(b1g3<*KLYuv<;?Emf u@>(R8Ҷv)1@CE3<T˶ݐ5&8ZflEϮ]Ijw˜k6{Z_` 38$,0&#b/%xy=i@R/`jmpzRț~!EXGYQ "*{7 @JskFɳ*Ip)R% .OUDTE}ymƬ>}v"@lX d; Uw6iFP'a<RZҟ Iz|'Mop3f=|Ε&47zPiG`]C<3AQm3X.eBV=F엕xZD3uWw @껣іzr} ч[u#To $ Tlg6l=QZ%m|J?ߪ n׼.2 C0G5=B˝˻|fJV&'9?šj`=q'[A=eN?Ms=>=Z_Ee>$pe{ MX~ԁh $3#AYH:}3*?vc_઺F8a-iqU >T3H4H",OM#/Ȥ@[jy87\&c ۱f +{4.p ?s M0&{4z0K^E7Jyo"&n5ΚvfP,ꎯjة*mʏQZEzپP/\ae&f$T+"+A+λY).Tg@H?1 dƮ0|u\?Op/[{r쥛m%DR^8@aAu?TN"F#91><$1J Z@Iz/IZۅmi(qc ͞@4a 9B4[ wwwۻ{u4G:\,ѓg:V{h>yd~[}JǬEZb-@ٞB0uě ڦ'_h[@eg܏ D)MjJ7={jJo^ l2ʥ' z̡^6KSu؜AQ2\GxРr Yμk&V\P#Sڀi[7eg2n8*洹;W8oⓅWJ/\>AzkK6,Z,*KN;&mA/CMÚEg&̚v3’*YpUKFʹ4 t3ΙƳ4)Ě^ƇzUpؑt-W'7 3aEsIdg2:.wߟL%ȼei`5ztUABvDp:P=P~ L$}dfJn/ jgy?mPPhQhU#-Eًڧ/%P6G LTĮ4\p$='})JvuO/2#=MnP8^ιˢ[5][EFoZs9,y~G|z/)%t=Wѱ QW +KZO"\NojoI+3SM !n"*6ek $-.JG-#0ʚU<(tr[D#%'ι%k mD#ů&pO 8`{V Ck yaǻ$2'VVYg?X}T;9l9j ndRkO^+$,$(%T~S%x5H-k6`A)#MFhh,]7plzn^tN]e F3VL7k2P,K _!A7GDchv\;3Www z o9EǴl/v. bhtz2GT44lC3ZdEI;pY~3@3@x)_Y(C37ZqZ|v򔫉5e2)θm#r|7 j8 ÍH>85`<βh(7? j<2r?F!5P*{a7 }s!Ll]SҹV/&0bw)ӒVZlߛ˙[HGnˏE8 |`}_gj÷= bdzk<2cpK\?BJ{;z[S?7ubO~ȿ v^[x] y<E_[r^ݐ{ o;,vdJ>7 |ZDn\n._ݰ Hjg9+}vR L9ݐLW4l+>M!H`֑D/O*a&S1b=1O!#A4l߁Mt}Ofe |aVo0w)Bn\Dz{5hWe2u亣0]5<,@![;Wq^z/{\/ffF&9y=.^Vi&V]/ԣ**fr5zpzTo,r83c/= O]u~NK 5ӣCRR iC?JO3-Lb߽Ng'1hapu*.Av!H$ƊjU]= 9Mgv#Z+ WosVIl(&}퍀)c- r, ٷ=V,.7{U8-7x\T {+{}0Qf;,X( qdBk1pbQ*sph8ED t'SȾ$0ģ)VuCyZnk PqD xMa+U{Eʗ̼S\x!6Ϲ$ij<&T}|,MpMFި6 *c8M /x%KO > stream xڍPҀ #w nepdp .www !xp `;\ޢjx^{Rj[A`'+@BISΉLKi qGL ru; BdyIA^ Nyw;;?`W%@ v!J]mm y ``pwZ9 6 ׌f -?!l g ӓэ j-An W%f[cChغ[A<\AW xАS82Vˀ88o?:lfavt6suX:*Ҋl/ C37𫿙fݟ3čy=f)'K # G} s}N`O'mX;l]ArۼY vvv>N>@k`gk [+?d_73 `ikm*Yůj0`?2z0K?^1P\WMNV|er888G:+dU9dgao,e !;qߔuoE2ћ9:xm:-P5J K[wnDrps%uYB,lZ웃Hf tKfa*o^)' `jzׯxJKן 9!.V`W?.-!@/(J%WKzi_c5*_ Z @!7h_~mUf/|_ڬÿ2u @55ۿ-u&9_#{sܟz?T d k'd1ɍ6|}+E䱧,!twU$;3n^x/=F|o[MѝBO)5iKYFY2Btm576-Pa&ccPaBijjՋuѸM'IܥqlT@n9+\f%V0K>|b }`6!J--;}.{+n&; ; +&x9 Sddb.Jai$%$_ lX ~3 ƚnٳ,c8-3Hc1!q,vCrbLփ ZE(gGE~oNemR/ #<XLrTN"} kY)a^K?cK̗.|Zo*4J9o kj}t,%Q&KfCoԗ02qhJ|Xc 0dƱ= W4IT4g * 2(rE9+VAMc76tBmW_P.Cb3>RC؁&C>zTk}E! }nE:L.P{Z1, 71<&.6>1#7N5H6ZlCf#x`|}dQz40JEȶ2mkJ d\=qՖ[ ߿AJ[;{Vj^TBW6miOMW"$l2GĹ8 Ϯuʲ>Cz[rkǤbaGcKR93]1Z"Uwk%tEd^lM|@I(=db%0(b}0HCFӔC.ݷrHEV:uJ;#C.N+8mt f}$ӧM]7q=T8m^^djtͩ|E hrO\J)EOek 63NF ^ٶvC4w(h#=ז[F5-ze"J?$pOfŒ٘ XYg!@7SSSCK2ьTtfao{{U0}>e{^fJlYR?gB}#'Jz|at=*`߳:_V,.`; .gO]niG>vMWQf,8s2gl-pv*vP{O1cG.Gb`Tۭ.V^O?W҄$1zwBhE[%Td(!AvŢT8ȠrҎ h'JBێ.E "0W)= +*S"{xT ^ p*IÁٮYd`2f?Pi^w꼐^zo 0@8' WNƨr>42~tSg-ߡ"b4&t h]"[3~80 KrLnϻ@[d75[Tlh"P rZ.U/tl&|1{|V*NQIHy5U|P^E|ƭ&>u$1sC4 5yS"t)~4  w.v* /p+,x0쓯M\eHu {aX6Q:߲]y^[ Sf1)5V ֹ͆U\x"zqGJj @yƭh~w{k0xw:Dt!rgkrWW86L},k:,-2fm!rMRţ7>Mޏ UkLJKCFW']KԒ 3gĕruxضߦ%r`kLhT}y Wb"I!ݏWӲ7=s("$Cst[T#n-QO Msrλ펲vQ.O&~f|G"Tw%&G{H`"]{dG\b^TnNj̢0\n xyj㘋4oA,"6]fku*P_ 镻tnq%6jX.m_Բm!U 7e˄F::R9:t{.{[-SBu/ W2W a+E?a1&3&X`M#{4 K٭#_'l君 ;ꇀS&paN%nnx=U3EL3$Lг|(Kl&ԡhv>Bg) A5 =T&첌q4qoҝ|Qy{6n: q y>\x/w,Q˙47K)(LphlM*QehEu8&gykGMd,&uny4zq^QC_Z;c0%"4oti"qκ>DfA jM8,.+qPK'RH̄kzo-PA<>g%Ӕf.ťz#O[Ǿű-֙<{YjlImáo(`oZ)DD@lT-OR߰95۾hn8S3{v N8Bf#"ql|q,&.WXʴ!V# ^One˭[u Ey ݜCJbpJ&e/s1q3IHMMgy*h#>aVvC86nn >Q]:ټ#u~9]lgSFCli=x>7+,N7$qD~{xW:rSZ]W&}p_La֤e dO,_V] `ɹEgS>JƟˇf`j%>>CJ aQ8Hk5W`wa>жkaW!pZ*Hy2َ3U#'/{gZOW+ŜXI'J y)g6g(qR##C<ZC8 M;"+8,ߝ.dMѽeT/6S^ʩL[($"2CTouT snj֎td^Ĥ# ]y3fu^±)K:8D]{,C@F.5Z'zT7^J}:-Jȴ*n23L?vbk"hS{>1ǨQ԰:t5ɣnw;i#/ITq)l 3s55u=\b(:( /nx ZgZ#Yvb]C1_V[Ƣ]']+-hsfI#GA@yW74!~>'X{4nÅ]\hS<{Lap(efc|>57LoHWyO<7fU*?2؈'%ӏψV"<(rp\huRw&&OŪ(9R+kN54&( MaCۗ^HDI4Auq y"x&TqیPSyf|=D;?Sh@ jω UeqܢlpABJTQ6K1}nNwp= nN t$ 0n3wB[ "aj&%)+ِ8J8]vﺓ!&t=( 5;y"Y/݄V$F"~/n{27-+sپ>yBI~̀-?tQ\o7)^o;9٫NAY*< }Ԑ״Y zh==Û(H;Ñw=߄;?Q* D'!ڒ8fLlc4LN~;V1Mqo)$ $.-GRۙ>&RdڟPΗw| (#6 Y+Qx 85I}{zLo^3`:kT"F|<)Rئ uMy!\[_EI 8^žh5{8 o3 4Ki|TՏZL-*}P=nP~fG:)adfE o]<ǟU^c}{§ft`Ysȳ+.W9>;uEq@cS*`$O$T݌⥲LsDL%aj̠dNˋ*~d*Kqg- رwu֜|N=bEԛHEg)PCUKilcjv7rcd%}vJ}+0I6l=/tx.C+`O}z0v(O7bAL2QuNg68LDM/vF_"acmԦS!6[mS/߯ޘ4nC O3'SĈQe޸+v} ]bD cgS q^qy I^F }*Kr+Ad' G52)y{?7{q"ypMyAKƮ"pv x1Aq(dEwo[I?@kwU/~Y-.j% |kJqҽ'6#?󭨂gXIEng`"MaWoyœP `H̄hQҏT 6q[ ˜AvDnWJxs_+Ք֬S~:ڃ"d%".p^3F?8[j|B r|GXa6<훫 }ֽ:Ig栖smVPp, Z1Vܬ"a)kV"`̿!ZY<,tNɯӼr}LPi7V;$VK}+%A5F[|_};f`KHD!u6\.)$0#*};߯X2@m3zU_JoFje8S.l>Bϲ&@%Ί/ȡh#䄄nRڞXN7%I̝TG|:qUCqCI2iD&ThI7]ǦvCXKn_\7&RY謁$$\?aJ/][5*]ԑe,#6y;Sp|ړ'f с{f}vOL3MmFkY-KRSxM/p0xuavYEhio$~>^K ;ڸ(JzkBNGQpC?d`2S~u o=(޳r }&L r.32w5OG8 {RmKϯ}ڑwOQ9uvIpUQr("rě, "!/L\R& {xipU_ 1/y,0 .bG Ei4rbC}bҪ{.C7lq!/S.=⤋q.Z.G_c|޷,C3t˜EӐŦN=z5n{d{l\O|0Ǧq֝&PͶqCefЖ[>$Pa !`Pg%>~0UG󠨴 eOM2KĠL6g5׌D"*huO{kW.lv8x/e0FK:Ѵ;\-xY d̻q`T{J e?\6g [(sJKZΚ%Sy.eDWmPpLW+2{!#9f[PFC48 fGO쇉Hp&pcήhftBߝ gp $o؝JΥ?PTܡ۝(DGlr1%Q,MG4]Ny@Z^@OT8r69do`C&}L9q>!PlǴw$YH $]UXڃb;R\~DF;wFRZ;TƩPQJl2g̛4[XgSinuyv8tW."w,qA/0QzSx~YtrVȗrW?e"(gB Փz7Hv1 džbWQa+W,h6)=rĉ'9|y7x·h.-W'=8FW~uE]!Q-5ˈq ~^Iy<%%,Eb֕cH36^W20?Qr֪&ڴГnL_i>[{ SKXڼ9?nzG2QIx#-KlėXV^0b$OC\ $B:ݷIB%0LF}alSҐ H#Zpg6Soa*:J)~}[s^A{oRpooH٥ĺ\uS\o/;d'( Q4jR'TQJjU|wǃn[7Ց~1r:-]8'լC<@Tkφ׹5>ܬ(}G#k=&k E>/&iʮ{"_ϷJ{ozOZiʞq;x!O3Xeڤ~BĭTQFr=ÖBdl\GC'p@.Z=uvu g:Ae&,NB]{a㦰DoZz"柛cЫQ?iBE;~tҕ % h pڭ|IF6]H ;? gBc_ (Y莟z++7B/Te_\|yHgn(%rq:o11U\aǃYdF2~Ԭm%@~,($7!}}ج}Ԙ}dwS.w8uP+@M_n Wap|iZ7^cyX(~ޘ3O?`e$ {YEH%bHۃec)E3?dRhb*B .! $[q|6e kՋRBc7v}T,݈}I $aK:tݓwNlבIz}¶PVO2@^>Qͯ&A1$2jB¬(=G#򤩹,z*Ī4im_o$d5RPV$Xc wM2}=z9h<\=z]+Owv)'[YVCng yaG`C:H֛~3"!$c{Xffp))!KHB m;ᚔ<6$;H̷@ pwG7k#SGV~DA2)-GN{rcpT9A$mVN}ĒzwS>CWԫǿq0L F!Ȯ~DҸ2s1ù8.3薻K=?%)U֤};h >9b S7CB6(O|~ҽRN;`PHܤǍꇄz-L l(k)sWrŧVĊ^"%{|#:%S L oERYs9$:~h2-Ke|N,y<ǾM³})1R=?DSBr P{(ZݪƣHȏxe-ݴ>=\_VXrr_wcxl գG,,&n+B2y߲IN:8Oy Wm4CC*˵xPۣ")01kQI`p`z ߮ 0%k0w_V/%f=;J *eGIz<06V5Rfe1q.5<Ŗt>ʌW͖W GUFgi9)fncxvi{kIҖ) ^Qw?bu fKOr_4P#>Qn3\:*f¹AW lh-2!s\xR]q/=\-(&R>KŧY%u<j'0ɽtae 30(npJ۪:Vܼ |w?K2h~ۉIr`3wBvͽXsJHΤ6`Yxե`ॿ{𭸫f#w8+~%0TNӱ5L|EJq=tr$#׿(L/->љ`h4J ޳WOCI}ot%ƻDWol_"mj9)&H_ߎck R878:JAk35%1Q2G;J|;kZL#qἾYqn'ӃHdE *,vXE#qͅ}"ӔBi;%4Z 'p'+WNwm~ǓG!~{+:*]ڊl:pzIav\]ҁu Z)Yg9pr "hӢ#tkEe~ی%9ڒTI$X^"ߔ#=2M޸a}+J@wl"UAfdR R7T' 2.MWYJ+1F^ 8WQu,_Eܥd v0fL-B?3QjBK"0'e\)çtn=M r U(@=,r2 g2[%Htv0R˟ȝY*56'5(BzPƮeϩ:C3'Tb6'"M.n'tS|,+܊"x=i%=bkö$( ^KfNL;& Y.>@C_BV<Q{왅/Jo!v|Β{c"E3 UfoonoKѤLOE^pUU*w u½KnA 2ӎ4IK!RzL1F +|KjR~*ƪgEh:ԯinTC 柶Acf~fo XOJ7lOKJ1,3WpM?:6" f+)YF88 ;,oۓ)u)QnWL>d}:1ӚI.J/2JݮyߒRjlyg&~J8T9N0Aר8+b$fyX>2ەkĊsԙ/a7/2ʼn}A4—&{ٮQ^ѯf0W#+e+ q Bkщ"Co4nod~^W ffh|1=qTSѫ9uOf?̘fLcv$PSʥh?3ab;!LSlN^N|eO)I`ݬf<w=ۀ`T~ƀN>p> stream xڍP-<=xpw 0Kp w,\Gɹ*kw$ 6@1kGzf&n 3Bh 3dc/ a{LP d `f0ffb01qƞ b 22l6n S3Ƿ<`D ` hXd ̀Vo ,6F kh```cor4(@c- OP19P1qt1 #Û1,)Ze,or @ ? ll @֦% /&H06di`fg1AE[`dut`pY#ah6Z;:Qhƻߏkambd66 c'[FUkPRo7o)Ff$PqdCփ- pwzy[` 2rMA𿣿&Ə?t&O(Y-qxгYXY\^Ew6pUK) zP;~6;/aoHO ?z+os6o`jWh r_.ZZC$A 4V99U3K5Pe331m,ޮHg61cX? &@?`mx `bcǓ~d0 ! qEALo31#Vo`q)Fo~Q7`zbbcyCormVovMY5|v~gsF|[Y&oLNF m& 1`/Fſ[s|+a0Zo[Tow_rߵھboVKfq[Kehd}chl/ߤod;[IF /F.!˛jl݁2rk]~e@#y#@jA| jz%vdD uۢ=aCZ[=┦wZ& Uv=ݡP{ZcYX97%kk飓I---8-&5;/ Y].|^c]W2S'dxTiq5lpU\`pZøb+ v/l'9c)ҏ.. n hMFq'ȩtYå5wdR+ƕӭb!{z9ƨdG|M¡f5|kJV#``\լ /̎Da۟A cfka*UA%4<Yov0cfx@??:Sul( ]!qrOQ6_yz6ZSsn#k2*5.t #ؔ](Aml3s\\_lf޼Z1]%$_/{ au3H#!hM]nXr v miWsN3P51luנj*=d36`j7؏uaFzg5̒ vV=d>d0"~6tA,Oʣ$fv٥EyWN)z1:U29ѵEt+V+4Wl3w Н|YpJ[ .1-k6x7þ`%8;Dya}m$Kq> 2|3<%^uvayέRzp0n3ٕ&- ZjTt!`/B⥈_Tr4@P(Bt_!(L;].il<}B'X1Cc@ M"5D) 5CtCA[v#\UE Yv?.zYJ/~壥Aiؒ\x~EV,W O= ).$[Ɉcm}~#**D Ht|@9.%PT;=f0=**1ea@˟_.o{nR|oTD4wo²U;ˋU)) <?Hs3ƥr;]%ѝDf\؉BwbZ2,٩ G $nprrַ}аvNDM5 Wݦƌ 6'4}Ih-(#@ ۻxZ/_iIgxSۄT̆Q/hh`hźߗ><.3w:gɂ?- ǒ|`w8@s "Zr<]5Y*B-U#@_,`K V} g7O3O0Q6yp0]eo;.OM*B,,5$]K( dݩoKoէ@9AU~⡘ >_.yF-VϨsv{&_Ͷ/j)v"2m'C. HRMTaK`Q1Br{T@LMo1xb$lI~ڣ[UK4k~Vr)kӆ ]8VRrg B9ӐcP ]vt,;S':p26|8ȲC4y7ad9T|{a 0BɡLN0G({-^zX"CƁ&ae}+}d FG/H](7@IWx~C 蚴ot3uV+4c;@T })KYPMVazv,r;{KE*\{ޏ`:x'n"W1k(nla.z1]4 y)UhNH24d?@"T:E/S$0U!5!$&Q~QCbV>T6 ? ҧ喝Y:LDASBәTp=O775̔7~{,!r ""֮O'wWe/F:[m*s~;Jī ?O.yt81ܾ5 #zt5Sh(i"Jr҅܍[ӊŽ}{e,eh:TC;jph! ٝT.*%B1956OMz<=t|t؇[$Zȹ04Dɀ1OPtEqѣ=go,'Ep1y-VaQ^P}!oޙ܎WK[ěr(A]z{TBh:gqs31 3?l?:iaјb72YGQL}}{wYrF$΅Α1@h,Æ*!VKĘg5]ӟ> 9ィ,v:%כ9p'=buq\LsLj8֧/fn땿lSe{66Y([gʩ+wߖ DR TJ>CW@_|̘/G)]:$ zq̖G* _o.hF]4}nr_m槺+<%%2n5x㣗(a&\wkSlz|6BکMdd`hnNfO5S2p!y#vjqUؙD4c0D)n [\r)Qfsk(?e g1L孩81J+*IDgx_!#>T 4{379*H\l'1ԙoxu Px'?yfBr׋5E'փ3N,S! }s,<&i/˿,`ݔ1յ5Znd`FTCѯ KCVZ?cV0LIaV{q8!Ea r,FŃFIabHhy'[.)!E>AiǽH'?469{=*v:Ö v fmաlE=%mv*7mGJO'C³B9& @v= Yt~7JX_W:Rj|] - Tu{?$a}'b> f K%G e"?4l3}4u\P19Zpcl(`:;]Fq$YyAiǘxR{1|8¥Ii@ӪqGj+JN*uμ"~$(m61Gmpp>w#T0׼=g/}-/{ K)FLu{뼫\`t}~bwohf?t)jV%qz%ef$?v~g%爊K%˿ d0SHӳ\xHk<@Nz X1).vL 1"Yy@AQdՉ*{σ<(ZO3W)4 "AJ8|_%CQOV}# ngI}屋P+/+@F}V#{*}nًvbp NHIpkq1Y}7/ ~j/?~%rlJ#p G[3"B\Mb59:K7*3i]fڈBlΟ% t7|iH;6xěL>Փ1hW'%P$U"f/і 9pJΖc&;vxEWi{mкTgs`[~63U9&b8`xw쎰UnXe5wR4| e]n#Z$2 ?dQQ%?`%H1eIqRJ@&MhZPi+0WԬe&4R  +Le-5ml0~g|7^?4p4J>&gsky>CMIZ:W)\-=GhA;O63*J:PGT=giX6unC0wnG/h|4ж#Q ~:rӮ$'& /S]z|CzG;#z#JGcD0*Yz!„ӫI!q!| w)q`cаĖ'N[U8RڎO{A$sn jŲ#rY.xg'ת8+PEL>ؗ1olW?,N ~]UJwt(SkgWȄ0ΑM)pXy Y\P"vLp+r~o8@5嵷gr6>ÒO93~tYyS"i sGi i>B>+@8|0_Q!b.30P cJ˳h<`* tސdv<͐QJ+L?;'z ŮfB '.>j"UR`ΫE"H-g/eI,ǵv/w=Qs'c ʓOnd=0';XY~z^wqbCւG_;|DX]1> tpߟw( JRG3il꾷ku 4~^4J3 w| uə!m.=ʃNصAlKoă+C9$+\0:{+V)5~љd'"~K[1jIۗWYW<``ED&Z}%xHf*VM4vw(kI|oGvIZ< M]Vd[WPVc4aTvMӮYbT4Cx UH='J\mZ$ {W@*c@Kmy:$ )t %GE! "`&*6_WfIo x$XC(Nͺ^%ImF|17Nl:# K`}s Qm2 uY0; %ʸÐW*Mx൥1B6źQ;jT/z'C(x^]S`8Kz8:LM?$(L[ n3Z>&5zo2)^ȳ(Vsb3d@6s'ݤg}Z.dɯl`Y֛D*g/U1~quN*)8 IοXQ2Qu]UHIfKy~EDed0Uw4r}0`a&XitsB`8l\6PFE )_͒L?s 8zɝ_.P7:ak(gL0h*;[8zf@+Q++Rmh d֯ Y'7=z҅S9s&^]*_^tZUzIYN:=/eyVΥ/Uى$3!-s&hj|hJu``%B&݉j/Z zEAʩeq,{$@-&gZ uh=tP~veȸ$b NL[~B2-kY6r["J’wPmxb-֋VZeGFԗ)WzN1JrEB':]"\bqN/r(h0 QF <dJD@>*1Ucb)ɑx+{$#1k#o+ 颴Ţ?,^O~~!b]M4t4)uO@~lMa*|EXEV'cL3#,iSjZK(S[m],T1P04#!t,Q#X}0#1ɇ)( φ%dlL'^ITvnUi}k`z'xϗLT:G?,Lcy)=_,Xᇟ smn-cw''6,:+V@'4M[M_jE I@eصnЫs0!] ums:C2pH%ɻ^YS J(rxla M͸0Y,C {LeWf8Pf=Δ-70!fDmr덷8Bb^qiMgmcZ3HgNay[5+_mVQEe-!>bz'SYMkV%X/ve^@¹K9?%9s`(n(`TCn7`Tv6d+8Qg=!Du+Fr- ѺZ9e)|]Mذ*m?UALcYdQ91>r3NsïX{ q>7N١/aq9J0z*Ƞ3Jzj;ha?9 p(]aF)Iڟg8PL g4rn]j~E8"PSSka m jAL$ʞ y3SW5Mbbp|,+ZM,{nPidкW fU_n;~8u6M"k}m.}ab}Ͷ%<,w`s,x <}TUl %\|$읜]ޭ~9-Lⶎ ^s*3'QG$\gVDUrEp41 '>UJqF9mAԙFyGB(+g_z 2& \F CK٥v.c_6δBGyIޱv;*$fZiWkiaBmݧv 7Iɞ/eFDc[1'农 B Xkt0v3F2~o!BF  bG81(EmaHd;{yE;"̼6MU:sBAR&݄geQSF^ WIY#"\]k98Iȗ㶝 @vϖYŀvii &SH]5 ֔ nLKOܖ͜ƶsw{_X]BXTO>ԅZ5}GCʚ]c2{HsZlOa k 晄݉_%Fxr'!;_a/mFj\g> MmIfNSd"״XOU{'.pAX82DS8p'WY5?͆p+!*OYQ ,9Z1ѩSXی\@҃K\/ry+. &TpZtRGVw.xLr]@quJ:ui扣<썑V1j9HGoXNFg--{1eD  > "n}':.튁=NkGf1T$ӧ,~- M*=1´ضm0cm-KbT%@X91/C6C%Ni#ypdN9BKTHbrD!wlajϔBu3'LZKJ%mtF #|JBgL?٦L1")4ɓ tĞQ\ɕҥ?A '\ԗpd}Cjwǃ$w%&%y+&rDg M?-VD@1HIH⁓@g'2,pNZ˶MybO"/U0S(ʩ ~<1}(C-T wqv[ _KeR,hWNcܶqJudo0_t|^t^g}{~>-.!w̹xz"& bY5kU.p!Y:T' -˓~GpC3Nû)qh[7-<\Df?:3LGylt2H-7r^r7u֎ȃ) ]:ceN(y,7&񁶉EfB'՚n$>V#4擩h2eܧ4"ct#&ݞt*"j\qJ&j}G)֤'MLl!zrݫZ+yTǸHDVD4B>G{5)kVNO!wg Ā]T0+s7j0m@#]# ثM Eom0{w%'IfKR~ħSvs`T͒p{ zֽ4u]odJYcj r0"^xN70nvi_ FӅTWyrM92L HeBs )B(ǜ~07c y<Jꙥ3\˘ONjzĻŞVY$MLs9_/G5pޘ\] bKA_N2I2#`ϘJM*nH8bqزMSr+L@ `6PXӱ"^PޕuERVRyVŽ ¥+QlE\陕f)!Q f4$JƯN:6sQ;JT2.<}YS%8S: X->acHB" l58a[x9i.1Dy?~q+:~3E2[CH(g %]O)l2?˘j=JaT1$/܄u14;{HǵȴܖIh=e*,/>XWBfeGu(ZG.v%ڝ(~W[|.^D[NE#ݻHvR=a1N+ vNܹTq(1ElN'TCB&4f154ՊO#ra#R|YJK('(~Ƙ{hD31Rs?*=/1'`hJ hi㴲Go[L 'ƍ Yst@D9%v8^d!6?= F9M"mkI?=Q?zSGD.`BfZ%NYz_2j1Ord5-^3|Tƻ ri{òpCHD:99h>V8 YfP-#!&X~Ե,(nmBpE|c{qn L`CF b+t~ ;` lۄNk$ԚJ ^] q'xצqwȅ`1 +TPF:DtښţbdkKd&pi<[C5DK<5-Ҷ1Ϗ~Wu5-;;Ñ1FID- }ś͝!1RNw3mS\{^ĥY*+ZܱQ؝RC:7et{v~>r*+& j:a;aؼ uyuMXpNvbov8d +Sgy^KIW|q:֐N!C 5*rdA>"C!L%JK_uՐ:Y#pfW1MXĵ,,eIJ0,/?JbuON/֞QEmfHy>.b9~҂M!_h1Pr3Qr@^gyuHV-L3={ endstream endobj 60 0 obj << /Length1 1498 /Length2 2397 /Length3 0 /Length 3351 /Filter /FlateDecode >> stream xڍT 8TVQL ۥ,-\'ra1 )5f1f\ IEl*R.QTRr% sv9Y{ߥgEc6Gh4N"44 ~#4\fư>a"L0Fx1hw"l)! lChذ9a\ȟ hSu1r H> WR; _Zh1| >I !>py 7s#Δ pa4}@f@o w6/pA0!*% ꀻpာod7X8nzĚ/P :QGmHap=%1)~0a~`.ǣr!σs3lǢٰ@۟-𹇡.7¿#:ĢƠ 8(,l8p3C4m`0Rap> 3DsF@tC(! '~E AT>C,psP`;@~Fca?Wrw]Gښ a=,0F@į]\).?+Xt6`m)}pȂ˙ m-.SAd絿#O a X>vm7@$gցO`g8HGBA+ħ2-13&]\Et61wFnBߐ ad\ ScE0PQO4PܿA#ş FpOJ@0"^FW}.Z#Ќ5n;"N9wK8.'խ=qoe?bfz*wٓWxYZ%AFy$k>G`{nޅcF*"VEdg4+ #˴W,n[db[8tu֧5ycCU] 򑋠wk9NjV;zjS%5&7XM']x^ɭ~s˥l m8z2֒E׻rDW*3/=G{NXųr9#m\&R0pU5I gK}76b̟LG2㰻&ݺ.K. ?44}DTvvǟ( ŃtUs{ӈ0OLm1Mzm*W#݉o28/SLN\1&m+:MglFEt휰i ׮]/.t9ڻUʪx/<8lnxGCE`N:<}ok0bѮzD-llZ-rYvF7ambsPSS-.R⯌ZQ5j9tFQnؠ]@AVĞ-jM{vY}%Quom]ᒭe;3|wONsy/oRŗU4jB]SgR '6qR;ti:.Wy/+Yi,J+tQmuC8YNb~zn7ڶnV_o1 ٷ|r/%Hl$zy: #fLI&XQH,}}i,\3^Oޥ{̨n:nbi!@Эĝ]TI ibCݳ\1YWrS5Y3_5/g@&-m s-wW^S@ߝHa/'kT5Bg31du,WGDG Ub}d߉mށ6XTXlv e֛plZX#R=4G._2f5))\x8FLج,ڟ/Ly8՝VI>~iis)+ :[dZiؖzgs ێH)FK Ї [o_xԸlOsttg+ބfϨ vxڮ ,Y3دv+o WRߊ TTjt5][/ѤU/+Ny1 lxvZ'x҇e7i gRBEѻ*ZOhj@*lA_~ZS8URb_siL^_b-i Mxtoڔo<ՑX endstream endobj 62 0 obj << /Length1 737 /Length2 966 /Length3 0 /Length 1535 /Filter /FlateDecode >> stream xmR PSWHD0´*"IJ(T6! ;T0$&O*". es%JŢl"D@& u!әΛyswz2#Iqb0]Aز0>d&ښK@!☷@k!XdLK"Pvy#  T *T,*KP2U! Ġ2A|y  +E2T Q1p ;@c:I! ARb\|eA|usCr#T6+r̒ ,ј5%Ä*&JPqX7_,l=(FC!Q2YP)IHO a$L&ĨNFKrTE2|I!% P2:00X&B$%)IB T)?)} #8&K+ȟV&lI}W=wW&5^\4Wf-KQmˮΩVe[+3w$J)=d@j)܀7?˦:lǚzIzs%e6%nRuFܻǖWE~Qq}ď9eh8št{$ꎿ}'\i~Vx]p(;Cx148nH -5U4pm1t:i%Ė.+w5nnm>m:]d_&5;R@Wt_lj^il~ >i[|V .Fْ\I0M;үȆns_l`WhA`,mk[ෙ V,<^uZ>8\秪^_r;9J"'|S/v6AJv{A/rlqq2MXFϓK6>CwyMB+597f]fiÆZޫ{V4jA-8w+Ny׃ߍo`z=J)%CF+M|_/+LgOU>&MXD㦯$eE3;qM3ٚQq>\Y?|dd>18_b} endstream endobj 65 0 obj << /Producer (pdfTeX-1.40.22) /Creator (TeX) /CreationDate (D:20240204081128-08'00') /ModDate (D:20240204081128-08'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021) kpathsea version 6.3.3) >> endobj 15 0 obj << /Type /ObjStm /N 47 /First 355 /Length 2559 /Filter /FlateDecode >> stream xZYs8~ׯnMET9>8ǎkS~%FQ1~R)3d%UM4믻AZ0‰H͈D3K#{"1$q=q $*!÷eD DJX"Bp?D"yP$&qOz8QI$&Z:e֌剶͉0%1 =:2o`JmDWmK!E`JM F&1`!aܡ pD l9>F44 ! vFSǏMNQvlȧHՀr>KpD8Ed|`0``֋>p}ϰF?&tT8]xT]W[u]wZ@Mհ&kOCX*y`6CI Н=BO rVV_>fG-hR{LLiYuOÅ.د0< YY=k 5ǭ?h 1s>Cc W\):!]lcxw3a>ZkFp>U{(TJҘZW-Ǝzh6JSM6Yv ) Ce%UB=Ck: c(". q*7=X't؂ֻJ(-SoiȐ,ꖶF_b;u4k ~621S@@e4| p< Lk#E:8J tlpA)H)8Y8cb }  i5( 7VD4؉Ј$ $?`h! BiEˑvdHus'4%}tE<" $NIa=987 9 P""C "jX gM+tb۠Bpk|"m4v{ aI?^mT4kTm(!=rPfUIf+&~lWjݰ]f_7>Q34O_#4?Bl, UՑ΄rҸGhmuD0UUȪz 5XFy KE`cX C?Nti=M&ټawMyuNO^^}3YyaH7"_oz߃6r<ݸ}LEu1]-6[?Ãieԫ0U?zN|>g@xj%S{@oTp޾yfA kZEN0y8q )3U^~'fw $Zݭ?H?W]td\hH-3Չ8;9w=$ 5Y_ЭRÂohtK۝fcE|~>]l|.qp}y/i9Qy-tˬpb*:{2岫ܹD9^Ժ溻ݺ[ gQ7WykF9fZ\yQeQm7W>p55r 6׼~2h_#T-Jӻ5Lc;5)27ʻ${X)`mfpe9O`w+_1z ?lm;KJ<Ci7H"I)ԭ  u endstream endobj 66 0 obj << /Type /XRef /Index [0 67] /Size 67 /W [1 3 1] /Root 64 0 R /Info 65 0 R /ID [<4D0E2DF95F23D90C5167A39EBBC784D0> <4D0E2DF95F23D90C5167A39EBBC784D0>] /Length 188 /Filter /FlateDecode >> stream x̹mqr-e#E@BH$P "%wћoF(@ꉩO1C%&/B1+v.