# Latent Sequence Models and Optional Adapters

## Interpretation boundary

Hidden states and mixture components are statistical constructs. They must not be labelled as emotions, cognitive states, diagnoses, intentions, or causal mechanisms without independent theory, design, and validation.

## Synthetic categorical sequences

    paths <- list(
      s1 = c("A", "A", "B", "B", "C"),
      s2 = c("A", "B", "B", "C", "C"),
      s3 = c("A", "A", "B", "C", "C"),
      s4 = c("C", "C", "B", "B", "A"),
      s5 = c("C", "B", "B", "A", "A"),
      s6 = c("C", "C", "B", "A", "A")
    )
    sequence_data <- do.call(rbind, lapply(seq_along(paths), function(i) {
      data.frame(sequence_id = names(paths)[i],
                 sequence_order = seq_along(paths[[i]]),
                 state = paths[[i]], stringsAsFactors = FALSE)
    }))

## Categorical HMM

    hmm <- fit_sequence_hmm(
      sequence_data,
      n_states = 2L,
      max_iter = 60L,
      seed = 10L
    )
    summarise_sequence_hmm(hmm)$fit
    #>   log_likelihood      aic      bic n_parameters n_observations iterations
    #> 1      -30.16079 74.32157 84.12995            7             30         28
    #>   converged
    #> 1      TRUE
    head(decode_sequence_states(hmm, method = "viterbi"))
    #>   sequence_id sequence_order observed_state component latent_state
    #> 1          s1              1              A         1     latent_2
    #> 2          s1              2              A         1     latent_2
    #> 3          s1              3              B         1     latent_1
    #> 4          s1              4              B         1     latent_1
    #> 5          s1              5              C         1     latent_2
    #> 6          s2              1              A         1     latent_2
    #>   posterior_probability decoding_method
    #> 1             0.9999999         viterbi
    #> 2             0.5680346         viterbi
    #> 3             0.9999997         viterbi
    #> 4             0.9999997         viterbi
    #> 5             0.5579014         viterbi
    #> 6             0.9999999         viterbi

    one_state <- fit_sequence_hmm(
      sequence_data,
      n_states = 1L,
      max_iter = 30L,
      seed = 10L
    )
    compare_sequence_hmms(one_state = one_state, two_state = hmm)
    #>       model            class log_likelihood      aic      bic n_parameters
    #> 1 one_state gp3_sequence_hmm      -32.95837 69.91674 72.71913            2
    #> 2 two_state gp3_sequence_hmm      -30.16079 74.32157 84.12995            7
    #>   n_observations converged delta_aic delta_bic
    #> 1             30      TRUE  0.000000   0.00000
    #> 2             30      TRUE  4.404834  11.41082

## Mixture HMM

    mixture <- fit_sequence_hmm_mixture(
      sequence_data,
      n_components = 2L,
      n_states = 2L,
      max_iter = 40L,
      inner_initial_iter = 5L,
      seed = 12L
    )
    summarise_sequence_hmm(mixture)$mixture
    #>   component weight n_states
    #> 1         1    0.5        2
    #> 2         2    0.5        2
    mixture$responsibilities
    #>   sequence_id  component_1  component_2 assigned_component
    #> 1          s1 1.018440e-12 1.000000e+00                  2
    #> 2          s2 1.092078e-13 1.000000e+00                  2
    #> 3          s3 1.365095e-13 1.000000e+00                  2
    #> 4          s4 1.000000e+00 9.438120e-13                  1
    #> 5          s5 1.000000e+00 1.092080e-13                  1
    #> 6          s6 1.000000e+00 1.365095e-13                  1

## Estimation limitations

The native estimators are compact, dependency-light, time-homogeneous categorical HMM workflows. EM estimation can converge to local optima, latent state labels are exchangeable, and AIC or BIC differences do not validate a substantive interpretation. Analysts should inspect convergence histories, fit multiple seeded specifications when the result matters, and use a specialist package such as `seqHMM` for multichannel, covariate-dependent, or more complex models.

## Optional ecosystem adapters

The adapters are guarded by `requireNamespace()` and do not make specialist packages mandatory dependencies.

    grp_input <- as_grpstring_data(sequence_data)
    grp_input$key
    #>   event_name character
    #> 1          A         A
    #> 2          B         B
    #> 3          C         C
    grp_input$strings
    #>      s1      s2      s3      s4      s5      s6
    #> "AABBC" "ABBCC" "AABCC" "CCBBA" "CBBAA" "CCBAA"

    if (requireNamespace("TraMineR", quietly = TRUE)) {
      traminer_sequences <- as_traminer_sequences(sequence_data)
      class(traminer_sequences)
    }
    #>  [>] state coding:
    #>        [alphabet]  [label]  [long label]
    #>      1  A           A        A
    #>      2  B           B        B
    #>      3  C           C        C
    #>  [>] 6 sequences in the data set
    #>  [>] min/max sequence length: 5/5
    #> [1] "stslist"    "data.frame"

    if (requireNamespace("TraMineR", quietly = TRUE) &&
        requireNamespace("seqHMM", quietly = TRUE)) {
      seqhmm_sequences <- as_seqhmm_sequences(sequence_data)
      class(seqhmm_sequences)
    }
    #>  [>] state coding:
    #>        [alphabet]  [label]  [long label]
    #>      1  A           A        A
    #>      2  B           B        B
    #>      3  C           C        C
    #>  [>] 6 sequences in the data set
    #>  [>] min/max sequence length: 5/5
    #> [1] "stslist"    "data.frame"

    if (requireNamespace("arules", quietly = TRUE) &&
        requireNamespace("arulesSequences", quietly = TRUE)) {
      cspade_input <- as_arules_sequences(sequence_data)
      arules::transactionInfo(cspade_input)
    }
    #>    sequenceID eventID
    #> 1           1       1
    #> 2           1       2
    #> 3           1       3
    #> 4           1       4
    #> 5           1       5
    #> 6           2       1
    #> 7           2       2
    #> 8           2       3
    #> 9           2       4
    #> 10          2       5
    #> 11          3       1
    #> 12          3       2
    #> 13          3       3
    #> 14          3       4
    #> 15          3       5
    #> 16          4       1
    #> 17          4       2
    #> 18          4       3
    #> 19          4       4
    #> 20          4       5
    #> 21          5       1
    #> 22          5       2
    #> 23          5       3
    #> 24          5       4
    #> 25          5       5
    #> 26          6       1
    #> 27          6       2
    #> 28          6       3
    #> 29          6       4
    #> 30          6       5

    network <- create_transition_network(sequence_data)
    if (requireNamespace("igraph", quietly = TRUE)) {
      graph <- as_igraph_transition_network(network)
      class(graph)
    }
    #> [1] "igraph"

    renamed <- sequence_data
    names(renamed)[names(renamed) == "sequence_order"] <- "position"
    names(renamed)[names(renamed) == "state"] <- "aoi_label"
    prepared <- prepare_gp3tools_sequences(renamed)
    prepared$status
    #> [1] "review"