The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
Simulated annealing mimics the physical process of annealing metals.
Kirkpatrick et
al. (1983) introduced the analogy; simulatedAnnealing()
follows that demonstration closely, adapted for psychometric models.
At each step:
criterion value is compared to the
current model’s.As with antColony(), every candidate item must already
appear on its factor’s line in initialModel – factors and
each factor’s candidate item pool are derived directly from that
syntax.
set.seed(58310)
result <- suppressWarnings(simulatedAnnealing(
initialModel = " visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9 ",
originalData = lavaan::HolzingerSwineford1939,
maxIterations = 3,
criterion = "cfi",
negateCriterion = TRUE,
itemsPerFactor = c(2, 2, 2),
items = paste0("x", 1:9)
))
#> Initializing short form creation.
#> The initial short form is:
#> visual =~ x2 + x1
#> textual =~ x6 + x4
#> speed =~ x8 + x7
#>
#> Using the short form randomNeighbor function.
#> Finished initializing short form options.
#> Current Progress:
#> Old Fit: 0.97 New Fit: 0.985 Current Step = 2 of a maximum 3. Current Step = 3 of a maximum 3.
result
#> Algorithm: Simulated Annealing
#> Total Run Time: 0.058 secs using 1 chains.
#>
#> Function call:
#> simulatedAnnealing(initialModel = " visual =~ x1 + x2 + x3\n textual =~ x4 + x5
#> + x6\n speed =~ x7 + x8 + x9 ", originalData = lavaan::HolzingerSwineford1939,
#> maxIterations = 3, criterion = "cfi", negateCriterion = TRUE, itemsPerFactor
#> = c(2, 2, 2), items = paste0("x", 1:9), temperature = "linear", Kirkpatrick
#> = TRUE, randomNeighbor = TRUE, lavaan.model.specs = list(model.type = "cfa",
#> auto.var = TRUE, estimator = "default", ordered = NULL, int.ov.free = TRUE,
#> int.lv.free = FALSE, std.lv = TRUE, auto.fix.first = FALSE, auto.fix.single
#> = TRUE, auto.cov.lv.x = TRUE, auto.th = TRUE, auto.delta = TRUE, auto.cov.y =
#> TRUE), maxChanges = 5, restartCriteria = "consecutive", maximumConsecutive =
#> 25, bifactor = NULL, setChains = 1, shortForm = T)
#>
#> Final Model Syntax:
#> visual =~ x3 + x1
#> textual =~ x5 + x4
#> speed =~ x8 + x9
#>
#>
#>
#> Criterion: "cfi" (maximized)
#> Final Model Value: 0.985itemsPerFactor sets the target number of items to keep
per factor (in the order factors appear in initialModel);
items is the flat pool of candidate item names to draw from
(defaulting to all column names in originalData if
omitted).
summary(result)
#> Algorithm: Simulated Annealing
#> Total Run Time: 0.058 secs
#>
#> lavaan 0.7-2 ended normally after 28 iterations
#>
#> Estimator ML
#> Optimization method NLMINB
#> Number of model parameters 21
#>
#> Number of observations 301
#>
#> Model Test User Model:
#>
#> Test statistic 13.109
#> Degrees of freedom 6
#> P-value (Chi-square) 0.041
#>
#>
#> Final Model Syntax:
#> visual =~ x3 + x1
#> textual =~ x5 + x4
#> speed =~ x8 + x9
#>
#>
#>
#> Criterion: "cfi" (maximized)
#> Final Model Value: 0.985plot() shows the criterion value across chain steps.
Since the algorithm can wander to worse solutions before recovering,
this trace is not necessarily monotonic – the best model found
is what’s returned in result@best_model, not necessarily
the last one visited.
An early “burn-in” period (common practice for Monte Carlo-style methods) can be excluded from the plot:
criterion accepts either a character
fit-measure name recognized by lavaan::fitmeasures() (as
above), or an arbitrary function that takes a fitted lavaan
object and returns a single numeric value:
simulatedAnnealing(
initialModel = "...",
originalData = myData,
maxIterations = 20,
criterion = function(fit) AIC(fit),
negateCriterion = FALSE, # smaller AIC is better
itemsPerFactor = c(6, 6, 6)
)negateCriterion controls the search direction:
TRUE looks for the largest value of
criterion (e.g. CFI, where larger is better),
FALSE looks for the smallest (e.g. RMSEA or AIC,
where smaller is better).
criterion/negateCriterion is supplied on its
natural scale either way – you never need to pre-negate your own
criterion function.
Omitting itemsPerFactor switches from item-swap
short-form search to a full-model search, where each step frees or fixes
one parameter rather than swapping items:
fittedModel <- lavaan::cfa(
model = " visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9",
data = lavaan::HolzingerSwineford1939
)
simulatedAnnealing(
initialModel = fittedModel,
originalData = lavaan::HolzingerSwineford1939,
maxIterations = 20,
criterion = "cfi",
negateCriterion = TRUE
)Pass the name of the general factor as bifactor to have
all of the retained items across the other factors also load on it – as
with antColony(), this only applies when creating a short
form (itemsPerFactor supplied):
bifactorModel <- "
visual =~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9"
simulatedAnnealing(
initialModel = bifactorModel,
originalData = lavaan::HolzingerSwineford1939,
maxIterations = 20,
criterion = "cfi", negateCriterion = TRUE,
itemsPerFactor = c(6, 3, 3),
items = paste0("x", 1:9),
bifactor = "visual"
)setChains runs multiple independent searches in parallel
(each starting from the same initialModel but exploring
different random neighbors), which is a useful check against any one
chain getting stuck:
simulatedAnnealing(
initialModel = "...",
originalData = myData,
maxIterations = 100,
criterion = "cfi", negateCriterion = TRUE,
itemsPerFactor = c(6, 6, 6),
setChains = 4
)With setChains > 1, plot() overlays each
chain’s trace (up to 8 chains), and the best model/fit reported is the
best across all chains.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.