Level: Advanced. Read “Evaluating Circumplex Structure” first.
This vignette explains what a boundary solution in a circular process
model (CPM) fit is, and what to do when one appears. Section 2, “When a
fit sits at a boundary”, refits the jz2017 model that
“Evaluating Circumplex Structure” fits and reads its boundary note. It
then glosses the five markers that cpm_fit() records and
states what the package’s validation measured about them. It ends with
four steps to take when one fires. The Wrap-up lists what the page
covered and names related pages.
This page starts from the fit of the jz2017 octants that
“Evaluating Circumplex Structure” makes. The same call refits it here,
with the same seed and the same two warnings.
data("jz2017")
set.seed(12345)
cpm <- cpm_fit(jz2017, scales = PANO(), angles = octants(), boots = 500)
#> Warning: CPM Hessian is ill-conditioned (condition number 1.83e+14): angles
#> may be clustered or parameters weakly determined.
#> Warning: 11 of 500 bootstrap resamples were excluded (0 with a degenerate or
#> non-positive-definite correlation matrix, 11 failing the convergence
#> acceptance criterion); the confidence intervals are based on the remaining
#> 489 replicates and are conditional on estimability.
summary(cpm)
#>
#> Circular Process Model (Browne, 1992)
#> Model: quasi-circumplex
#> Harmonics (m): 3
#> Sample size (N): 1166
#> Reference scale: PA
#> CI method: bootstrap
#> Confidence level: 0.95
#>
#> # Estimated angles and communality indices
#>
#> Scale Theory Angle lci uci Zeta lci uci
#> PA 90 90.000 90.000 90.000 0.767 0.684 0.860
#> BC 135 125.074 113.548 137.069 0.931 0.872 1.000
#> DE 180 170.353 156.943 185.174 0.780 0.733 0.826
#> FG 225 195.425 185.811 206.523 0.861 0.822 0.910
#> HI 270 250.721 244.660 258.758 0.956 0.935 0.977
#> JK 315 269.491 261.813 279.412 0.942 0.929 0.957
#> LM 360 294.230 286.626 303.019 0.806 0.761 0.850
#> NO 45 11.305 2.426 20.913 1.000 1.000 1.000
#>
#> # Correlation-function weights
#>
#> k Beta Beta_lci Beta_uci
#> 0 0.450 0.418 0.481
#> 1 0.440 0.411 0.466
#> 2 0.074 0.065 0.087
#> 3 0.036 0.015 0.057
#>
#> # Fit indices
#>
#> χ²(10) = 81.169, p = <1e-04
#> RMSEA = 0.078 [0.063, 0.094] (90% CI)
#> SRMR = 0.042
#> CFI = 0.984 TLI = 0.956
#> AIC = 117.169 BIC = 208.273
#>
#> # Residuals
#>
#> Largest absolute residual: 0.134 (PA – HI)
#>
#> # Diagnostics
#>
#> Note: a communality index reached its upper boundary (ζ > 0.995, a
#> Heywood-type solution).
#> Note: 11 of 500 bootstrap resamples were excluded (0 degenerate, 11
#> non-convergent); the intervals are based on 489 replicates and are
#> conditional on estimability.
#>
#> Note: boundary/weak-identification markers fired: Heywood communality;
#> small correlation-function weight; ill-conditioned Hessian.
#> What has been measured about these markers covers analytic intervals only,
#> and not every marker was measured; they are not validated as predictors of
#> the bootstrap intervals shown here (see the vignette section 'When a fit
#> sits at a boundary').The fit above sits at a boundary, and it says so. Its
# Diagnostics block prints a note that a communality index
reached its upper boundary. The results table shows which one: the NO
scale’s communality index is 1.000, with a confidence interval of
[1.000, 1.000]. A zero-width interval is not a precise estimate. It is
an absent one. Both endpoints equal the estimate. So at least the middle
95% of the retained bootstrap resamples also sat on the boundary. That
left the percentile interval no width to report. The same chunk also
emits a warning that the fit’s Hessian is ill-conditioned, a second
signal from the same regime. When octant scales share a strong general
factor, as interpersonal problem scales do, fitted solutions frequently
sit at or near a parameter boundary. This is the estimator meeting real
data at finite \(n\), not a data-entry
error.
cpm_fit() records five such markers, and
summary() names the ones that fired:
summary() prints that list when markers fire, but on the
analytic path it prints the list only within a sample-size window. On
the bootstrap path, as in the fit displayed above, the list prints as a
descriptive note at every sample size. Beside the note, a statement says
that what has been measured about the markers covers analytic intervals
only. It also says that not every marker was measured. What a fired
marker does and does not tell you, below, has the particulars. So
the markers are not validated as predictors of the bootstrap intervals
they accompany. When the intervals are analytic, the list instead
arrives inside a coverage caution, printed when \(N\) falls between 2000 and 50000. Below
2000, an unconditional caution prints without the list. Above 50000,
neither prints. To see that caution-embedded form, we simulate a larger
sample from the structure just estimated with
cpm_simulate(). Then we refit it on the correlation-matrix
path, where the intervals are analytic.
set.seed(2026)
sim <- cpm_simulate(cpm, n = 2500) # draws from the structure estimated above
demo <- cpm_fit(
cormat = cor(sim), scales = colnames(sim), angles = octants(), n = 2500
)
summary(demo)
#>
#> Circular Process Model (Browne, 1992)
#> Model: quasi-circumplex
#> Harmonics (m): 3
#> Sample size (N): 2500
#> Reference scale: PA
#> CI method: analytic
#> Confidence level: 0.95
#>
#> # Estimated angles and communality indices
#>
#> Scale Theory Angle lci uci Zeta lci uci
#> PA 90 90.000 90.000 90.000 0.785 0.748 0.823
#> BC 135 125.057 120.571 129.542 0.881 0.852 0.911
#> DE 180 167.802 161.988 173.616 0.799 0.777 0.820
#> FG 225 193.344 187.711 198.976 0.878 0.854 0.901
#> HI 270 250.958 245.112 256.805 0.958 0.944 0.971
#> JK 315 269.528 263.351 275.706 0.948 0.938 0.958
#> LM 360 293.619 286.622 300.617 0.803 0.778 0.829
#> NO 45 12.673 7.017 18.329 0.929 0.836 1.023
#>
#> # Correlation-function weights
#>
#> k Beta Beta_lci Beta_uci
#> 0 0.442 0.422 0.462
#> 1 0.453 0.436 0.470
#> 2 0.068 0.058 0.078
#> 3 0.037 0.030 0.044
#>
#> # Fit indices
#>
#> χ²(10) = 2.229, p = 0.994
#> RMSEA = 0 [0, 0] (90% CI)
#> SRMR = 0.005
#> CFI = 1 TLI = 1.002
#> AIC = 38.229 BIC = 143.062
#>
#> # Residuals
#>
#> Largest absolute residual: 0.012 (FG – NO)
#>
#> Note: this solution is near a parameter boundary or weakly identified
#> (small correlation-function weight); analytic (Wald) confidence intervals
#> mis-covered for such fits in validation even at N in the tens of thousands.
#> Interpret them with caution and prefer the bootstrap on the raw-data path
#> when available.One marker fires here, a small correlation-function
weight, and summary() names it. Notice what did
not happen. The population that this sample was drawn from
carries NO’s communality at the boundary, yet this draw produced no
Heywood case. A boundary in the population does not guarantee a boundary
in every sample from it. In the same way, a quiet fit does not guarantee
that there is no boundary behind the data.
What a fired marker does and does not tell you. The package’s validation simulations measured what these markers predict, and the measurement is narrower than the markers are. It covered analytic (Wald) intervals fitted from a correlation matrix. It measured interval coverage (how often an interval contained the truth), not bias in the point estimates.
The record does not support four things, and no reading of these markers should assume them. First, nothing was measured about bias in the point estimates. Second, the marker study fitted analytic intervals only. So the markers are not validated as predictors on the bootstrap path that this vignette uses by default. Third, the boundary harmonic removed marker showed no evidence of predicting mis-coverage at all. Fits carrying it covered as well as fits without it. It is kept because it names a real feature of the solution, not because it forecasts trouble. Fourth, the numbers behind Heywood communality, ill-conditioned Hessian and competing near-tied optima come almost entirely from one simulated configuration, chosen deliberately to provoke them. In that configuration, those markers fire often enough to measure. Even there, near-tied optima fired only rarely. Read them as what happens where those markers fire, not as how often they fire.
What to do when one fires.
"quasi-circumplex" model is the least constrained of the
variants that “Evaluating Circumplex Structure” compares. So a boundary
that appears only under a constrained variant was put there by the
constraint. A boundary already present in the default fit belongs to the
data and the model family, not to an assumption you added. If you fitted
from a correlation matrix, refitting from raw data also buys you
bootstrap intervals in place of analytic ones.RMSEA = 0 [0, 0], which is not that failure. It is a fit
statistic at its own floor, because the refit recovers almost exactly
the structure it was simulated from.)A boundary solution is the estimator meeting real data, and a fired marker is a finding to locate, refit, report and keep in view. No page follows this one. “Evaluating Circumplex Structure” compares the model variants that a refit chooses among. “Confidence Interval Accuracy” asks whether the intervals of an SSM analysis can be trusted at your sample size.