Reference
Abstract type
SymbolicLongMemorySequences — Module
SymbolicLongMemorySequencesSelf-Similar Symbols Sequence Synthesis.
SymbolicLongMemorySequences.jl generates Long-Range Dependent (LRD) sequences of categorical (non-numerical) symbols for use as ground-truth test data in LRD estimation studies.
Generators
| ID | Type | Mechanism |
|---|---|---|
| PB1 | SpectralFGN | Spectral fGn synthesis + quantization |
| PB2 | LGCM | Latent Gaussian categorical model |
| PB3 | WaveletMarkov | Spectral/Haar driver + Markov regimes |
| PB4 | IntermittentMapSymbols | Intermittent-map driver + quantization |
| MB1a | LAMP | Linear-Additive Markov Process |
| MB1b | DyadicLAMP | Dyadic-bucket LAMP approximation |
| MB1c | CalibratedAdditiveMarkov | Centered additive memory function |
| MB2 | OnOffMarkov | Heavy-tailed regime-switching Markov |
| MB3 | FSS | Fractal Symbol Sequence via FRP/FSNP |
| MB4a | HawkesSymbol | Normalized Hawkes-style symbol process |
| MB4b | SelfExcitingMass | Unnormalized self-exciting symbol mass |
| MB4c | LogitSelfExcitingMass | Log-contrast self-exciting symbol mass |
| MB5 | DuplicationMutation | Copy-mutate symbolic growth |
Common interface
generate(g, n; rng = Random.default_rng()) -> Vector
generate_with_latent(g, n; rng) -> (Vector, Matrix)
make_generator(id, alphabet; kwargs...) -> LRDGenerator
method_ids() -> Tuple
method_info(id) -> MethodInfo
method_parameters(id) -> Tuple
save_sequence(filepath, seq, g; created) -> filepathExamples
julia> g = make_generator(:PB1, [:a, :b]; H = 0.75)
SpectralFGN{Vector{Symbol}, Vector{Float64}}(H=0.75, k=2)
julia> length(generate(g, 16; rng = MersenneTwister(1)))
16SymbolicLongMemorySequences.LRDGenerator — Type
LRDGeneratorAbstract supertype for all LRD symbol-sequence generators in SymbolicLongMemorySequences.jl.
Concrete subtypes must implement:
generate(g::MyGenerator, n::Int; rng::AbstractRNG = Random.default_rng()) -> VectorExamples
julia> SpectralFGN(0.8, [:a, :b]) isa LRDGenerator
trueCommon interface
SymbolicLongMemorySequences.generate — Function
generate(g, n; rng = Random.default_rng()) -> VectorGenerate a sequence of n symbols using LRD generator g.
Returns a Vector whose element type matches the alphabet of g.
Arguments
g::LRDGenerator: configured generator instance.n::Int: number of symbols to emit.
Keyword Arguments
rng::AbstractRNG: random number generator (default:Random.default_rng()).
Examples
julia> g = SpectralFGN(0.8, ['a', 'b', 'c'])
julia> seq = generate(g, 1000)
julia> length(seq) == 1000
trueSymbolicLongMemorySequences.generate_with_latent — Function
generate_with_latent(g::SpectralFGN, n; rng) -> sequence, latentGenerate n symbols and return the one-row latent fGn matrix used before quantization.
Examples
julia> g = SpectralFGN(0.75, [:a, :b]);
julia> seq, latent = generate_with_latent(g, 16; rng = MersenneTwister(1));
julia> length(seq), size(latent)
(16, (1, 16))generate_with_latent(g::LGCM, n; rng) -> sequence, latentGenerate n symbols and return the k × n latent fGn matrix used by the calibrated argmax transform.
Examples
julia> g = LGCM(0.75, [:a, :b]; calibration_iters = 2);
julia> seq, latent = generate_with_latent(g, 16; rng = MersenneTwister(1));
julia> length(seq), size(latent)
(16, (2, 16))generate_with_latent(g::WaveletMarkov, n; rng) -> sequence, latentGenerate n symbols and return the one-row latent regime-driver matrix used before rank-binning into Markov regimes.
Examples
julia> P = [0.9 0.1; 0.2 0.8];
julia> g = WaveletMarkov(0.75, [:a, :b], [P, P]);
julia> seq, latent = generate_with_latent(g, 16; rng = MersenneTwister(1));
julia> length(seq), size(latent)
(16, (1, 16))generate_with_latent(g::IntermittentMapSymbols, n; rng) -> sequence, latentGenerate n symbols and return the one-row intermittent-map driver matrix used before quantization.
Examples
julia> g = IntermittentMapSymbols(1.6, [:a, :b]; burnin = 10);
julia> seq, latent = generate_with_latent(g, 16; rng = MersenneTwister(1));
julia> length(seq), size(latent)
(16, (1, 16))generate_with_latent(g, n; rng) -> sequence, latentGenerate a property-based symbolic sequence and return the numerical latent series used before symbolization.
The returned latent value is a width × n matrix, where width is latent_width of the symbolizer. This helper is intended for validation and research workflows where the numerical long-memory driver should be diagnosed alongside the final symbolic sequence. It is additive to the common generate contract; ordinary callers can continue to use generate(g, n; rng).
Examples
julia> g = PropertyBasedGenerator(SpectralFGNSource(0.75),
... QuantileSymbolizer([:a, :b]));
julia> seq, latent = generate_with_latent(g, 16; rng = MersenneTwister(1));
julia> length(seq), size(latent)
(16, (1, 16))SymbolicLongMemorySequences.MethodInfo — Type
MethodInfoMetadata returned by method_info for one SymbolicLongMemorySequences synthesis method.
Fields:
id: stable method identifier, such as:PB1or:MB5.family::property_basedor:model_based.type_name: exported generator type name.defaults: standard-case keyword defaults used bymake_generator.parameters: keyword metadata for the factory inputs accepted by this method.standard_cases: named construction presets accepted bymake_generator.description: short human-readable summary.
Examples
julia> info = method_info(:PB1)
MethodInfo(id=PB1, type=SpectralFGN)
julia> info.defaults.H
0.8
julia> method_parameters(:PB1)[1].name
:HSymbolicLongMemorySequences.ParameterInfo — Type
ParameterInfoMetadata for one keyword accepted by make_generator.
Fields:
name: keyword name.kind: currently:keyword; reserved for future input categories.default: factory default value.domain: concise domain or accepted-value summary.description: short human-readable explanation.
Examples
julia> p = method_parameters(:PB1)[1]
ParameterInfo(name=H, default=0.8)
julia> p.domain
"0.5 < H < 1"SymbolicLongMemorySequences.method_ids — Function
method_ids(; family = :all) -> Tuple{Vararg{Symbol}}Return the stable method identifiers accepted by make_generator.
Use family = :property_based or family = :model_based to filter the list.
Examples
julia> method_ids()[1:3]
(:PB1, :PB2, :PB3)
julia> :MB5 in method_ids(family = :model_based)
trueSymbolicLongMemorySequences.method_info — Function
method_info(id) -> MethodInfo
method_info() -> Tuple{Vararg{MethodInfo}}Return metadata and standard defaults for one method accepted by make_generator. id may be a method identifier such as :PB1 or an exported type name such as :SpectralFGN. With no argument, return metadata for all methods in method_ids order.
Examples
julia> method_info(:MB5).type_name
:DuplicationMutation
julia> method_info(:SpectralFGN).id
:PB1
julia> length(method_info()) == length(method_ids())
trueSymbolicLongMemorySequences.method_parameters — Function
method_parameters(id) -> Tuple{Vararg{ParameterInfo}}Return keyword metadata for the factory inputs accepted by one method.
This is a discovery helper for user interfaces, examples, and benchmark grids. All methods still require the positional alphabet input to make_generator, and sequence length n is supplied later to generate.
Examples
julia> first(method_parameters(:PB1)).name
:H
julia> any(p -> p.name === :mutation_probability, method_parameters(:MB5))
trueSymbolicLongMemorySequences.make_generator — Function
make_generator(id, alphabet; kwargs...) -> LRDGeneratorConstruct a standard SymbolicLongMemorySequences generator by method identifier.
This is a convenience API for common cases. It does not replace the explicit scientific constructors: use method_info(id).defaults to inspect default parameters, then pass keyword overrides as needed. id may be a method id (:PB1, :MB1c) or a type name (:SpectralFGN, :DuplicationMutation).
Common Keywords
marginal = :uniform: target marginal where the method has one.case: standard preset for methods that need local/regime structure.
Examples
julia> g = make_generator(:PB1, [:a, :b]; H = 0.75)
SpectralFGN{Vector{Symbol}, Vector{Float64}}(H=0.75, k=2)
julia> generate(g, 4; rng = MersenneTwister(1)) isa Vector{Symbol}
true
julia> make_generator(:MB5, ['A', 'C']; alpha = 1.4, max_block_length = 128)
DuplicationMutation{Vector{Char}, Vector{Float64}}(α=1.4, k=2, μ=0.02, seed=64, max_block=128)SymbolicLongMemorySequences.save_sequence — Function
save_sequence(filepath, seq, gen; created = string(today())) -> filepathWrite a generated symbol sequence to an INC file (IncCSV.jl format) with full provenance metadata.
The INC file contains:
- A metadata block recording the SymbolicLongMemorySequences.jl package version, the generator type and all its parameters, and the creation date.
- A two-column CSV body:
index(1-based integer) andsymbol(string).
Arguments
filepath::AbstractString: output path (.incextension recommended).seq::AbstractVector: symbol sequence as returned bygenerate.gen::LRDGenerator: the generator instance used to produceseq.
Keyword Arguments
created::String: creation date (default: today's date in ISO 8601 format).
Returns
filepath, to allow chaining.
Examples
julia> g = SpectralFGN(0.8, [:a, :b, :c])
julia> seq = generate(g, 1000)
julia> save_sequence("output.inc", seq, g)
"output.inc"SymbolicLongMemorySequences.LocalStructureSpec — Type
LocalStructureSpecAbstract supertype for explicit local-structure specifications.
MarkovSpec is the current concrete first-order specification. Future higher-order specifications, such as sparse trigram controls, should subtype LocalStructureSpec and define local_structure_order.
Examples
julia> MarkovSpec([:a, :b], [0.9 0.1; 0.2 0.8]) isa LocalStructureSpec
trueSymbolicLongMemorySequences.MarkovSpec — Type
MarkovSpec(alphabet, transition_matrix)Validated first-order Markov specification over an ordered symbol alphabet.
transition_matrix[i, j] is the conditional probability of emitting alphabet[j] after alphabet[i].
Examples
julia> spec = MarkovSpec([:a, :b], [0.9 0.1; 0.2 0.8])
MarkovSpec{Vector{Symbol}}(k=2)
julia> spec.transition_matrix[1, :]
2-element Vector{Float64}:
0.9
0.1SymbolicLongMemorySequences.local_structure_order — Function
local_structure_order(spec) -> IntReturn the Markov order of a local-structure specification.
MarkovSpec has order 1. This function is the extension point for future higher-order local-structure specifications; SymbolicLongMemorySequences.jl does not currently expose a trigram-control specification.
Examples
julia> local_structure_order(MarkovSpec([:a, :b], [0.9 0.1; 0.2 0.8]))
1SymbolicLongMemorySequences.ControlCapabilities — Type
ControlCapabilitiesProgrammatic description of a generator's user-facing control strengths.
Fields use stable symbolic levels:
alphabet::exactmarginal::finite_sample,:empirical,:implied,:innovation_target, or:asymptoticbigram::per_regimeor:inducedtrigram::inducedlrd::approximate,:latent_approximate,:finite_history, or:nominal
Examples
julia> caps = control_capabilities(SpectralFGN(0.8, [:a, :b]));
julia> caps.alphabet, caps.marginal
(:exact, :finite_sample)SymbolicLongMemorySequences.control_capabilities — Function
control_capabilities(g) -> ControlCapabilitiesReturn the declared control strengths of generator g.
Examples
julia> control_capabilities(SpectralFGN(0.8, [:a, :b])).marginal
:finite_sample
julia> control_capabilities(FSS(1.5, [:a, :b])).bigram
:inducedProperty-based generators
SymbolicLongMemorySequences.LatentSource — Type
LatentSourceAbstract supertype for numerical latent sources used by PropertyBasedGenerator.
A latent source produces one or more real-valued series that carry the large-scale dependence structure. A Symbolizer then maps those numerical series to symbols.
Examples
julia> SpectralFGNSource(0.8) isa LatentSource
trueSymbolicLongMemorySequences.Symbolizer — Type
SymbolizerAbstract supertype for transforms that map numerical latent series to symbols.
Symbolizers declare the number of latent input series they need through latent_width. For example, QuantileSymbolizer needs one latent series, while ArgmaxSymbolizer needs one latent series per alphabet symbol.
Examples
julia> QuantileSymbolizer([:a, :b]) isa Symbolizer
trueSymbolicLongMemorySequences.PropertyBasedGenerator — Type
PropertyBasedGenerator(source, symbolizer)Composable property-based generator.
Property-based synthesis has two layers: a numerical LatentSource that carries the large-scale dependence, and a Symbolizer that maps the latent series to a finite alphabet. Not every source can feed every symbolizer; construction checks the required latent_width.
Named generators such as SpectralFGN, LGCM, WaveletMarkov, and IntermittentMapSymbols remain the stable standard cases. PropertyBasedGenerator exposes the lower-level composition path for controlled experiments.
Examples
julia> src = SpectralFGNSource(0.8);
julia> sym = QuantileSymbolizer([:a, :b], [0.25, 0.75]);
julia> g = PropertyBasedGenerator(src, sym)
PropertyBasedGenerator(source=SpectralFGNSource, symbolizer=QuantileSymbolizer)
julia> length(generate(g, 16; rng = MersenneTwister(1)))
16SymbolicLongMemorySequences.SpectralFGNSource — Type
SpectralFGNSource(H)Numerical latent source using the approximate spectral fGn construction also used by SpectralFGN.
The source can generate any positive number of independent latent streams. Each stream uses Hurst parameter H. This makes it compatible with one-stream symbolizers such as QuantileSymbolizer and multi-stream symbolizers such as ArgmaxSymbolizer.
Examples
julia> src = SpectralFGNSource(0.75)
SpectralFGNSource(H=0.75)
julia> size(generate_latent(src, 8, 2; rng = MersenneTwister(1)))
(2, 8)SymbolicLongMemorySequences.HaarLRDSource — Type
HaarLRDSource(H; cascade_depth = 0)Numerical latent source using the simple Haar-like cascade retained for PB3 comparison studies.
cascade_depth = 0 means choose floor(log2(n)) at generation time. This is a pragmatic finite-sample latent driver, not a calibrated wavelet synthesis package.
Examples
julia> src = HaarLRDSource(0.8; cascade_depth = 3)
HaarLRDSource(H=0.8, cascade_depth=3)
julia> size(generate_latent(src, 8, 1; rng = MersenneTwister(1)))
(1, 8)SymbolicLongMemorySequences.IntermittentMapSource — Type
IntermittentMapSource(z; burnin = 1000)One-stream numerical latent source using the Pomeau-Manneville-style intermittent map also used by IntermittentMapSymbols.
Because this source represents one deterministic latent map trajectory, it is compatible with one-stream symbolizers such as QuantileSymbolizer and MarkovRegimeSymbolizer, but not with ArgmaxSymbolizer.
Examples
julia> src = IntermittentMapSource(1.6; burnin = 10)
IntermittentMapSource(z=1.6, burnin=10)
julia> size(generate_latent(src, 8, 1; rng = MersenneTwister(1)))
(1, 8)SymbolicLongMemorySequences.QuantileSymbolizer — Type
QuantileSymbolizer(alphabet [, marginal])One-stream symbolizer using rank/quantile binning.
The sorted latent values are assigned to alphabet with finite-sample counts as close as possible to marginal.
Examples
julia> sym = QuantileSymbolizer([:a, :b], [0.25, 0.75])
QuantileSymbolizer{Vector{Symbol}, Vector{Float64}}(k=2)
julia> latent_width(sym)
1SymbolicLongMemorySequences.ArgmaxSymbolizer — Type
ArgmaxSymbolizer(alphabet [, marginal]; calibration_iters = 25,
calibration_rate = 0.7)Multi-stream symbolizer using calibrated argmax over one latent series per alphabet symbol.
This is the symbolization transform used by LGCM. It requires latent_width(symbolizer) == length(alphabet).
Examples
julia> sym = ArgmaxSymbolizer([:a, :b, :c])
ArgmaxSymbolizer{Vector{Symbol}, Vector{Float64}}(k=3)
julia> latent_width(sym)
3SymbolicLongMemorySequences.MarkovRegimeSymbolizer — Type
MarkovRegimeSymbolizer(alphabet, transition_matrices;
regime_weights = uniform)One-stream symbolizer that rank-bins a latent driver into regimes, then emits symbols from regime-specific Markov transition matrices.
This is the symbolization transform used by WaveletMarkov. The latent source supplies regime persistence; the transition matrices supply local bigram structure.
Examples
julia> P1 = [0.9 0.1; 0.2 0.8];
julia> P2 = [0.3 0.7; 0.6 0.4];
julia> sym = MarkovRegimeSymbolizer([:a, :b], [P1, P2])
MarkovRegimeSymbolizer{Vector{Symbol}, Vector{Float64}}(k=2, R=2)
julia> latent_width(sym)
1SymbolicLongMemorySequences.latent_width — Function
latent_width(symbolizer) -> IntReturn the number of latent numerical series required by symbolizer.
Examples
julia> latent_width(QuantileSymbolizer([:a, :b]))
1
julia> latent_width(ArgmaxSymbolizer([:a, :b]))
2SymbolicLongMemorySequences.generate_latent — Function
generate_latent(source, n, width; rng) -> Matrix{Float64}Generate a width × n matrix of numerical latent series from source.
Examples
julia> latent = generate_latent(SpectralFGNSource(0.75), 8, 2;
... rng = MersenneTwister(1));
julia> size(latent)
(2, 8)SymbolicLongMemorySequences.symbolize — Function
symbolize(symbolizer, latent; rng) -> VectorMap a width × n latent matrix to symbols using symbolizer.
Examples
julia> latent = reshape([0.1, 0.8, 0.2, 0.9], 1, 4);
julia> symbolize(QuantileSymbolizer([:a, :b]), latent; rng = MersenneTwister(1))
4-element Vector{Symbol}:
:a
:b
:a
:bSymbolicLongMemorySequences.SpectralFGN — Type
SpectralFGN(H, alphabet [, marginal])Property-based LRD symbol-sequence generator (PB1).
Synthesises fractional Gaussian noise (fGn) with Hurst parameter H via an approximate spectral (FFT) method, then maps the real-valued output to symbols using sample-quantile thresholding targeting a prescribed marginal distribution.
Arguments
H::Real: Hurst parameter,H ∈ (0.5, 1.0). Higher values give stronger LRD.alphabet: ordered collection of symbols.marginal::AbstractVector{<:Real}: target marginal probabilities (default: uniform).
Complexity
O(n log n) time, O(n) memory.
Notes
Short-range structure (bigrams, etc.) is determined entirely by the quantization scheme and cannot be prescribed independently. For joint control of LRD and local structure see LAMP.
The spectral method is approximate: it reproduces the asymptotic spectral slope correctly but may deviate from the exact fGn autocovariance near lag 0. Requires n ≥ 4 (at least one interior frequency pair).
References
Paxson, V. (1997). Fast, approximate synthesis of fractional Gaussian noise for generating self-similar network traffic. Computer Communications Review 27, 5–18.
Dieker, T. (2004). Simulation of fractional Brownian motion. PhD thesis, University of Twente.
Examples
julia> g = SpectralFGN(0.8, [:a, :b, :c])
SpectralFGN{Vector{Symbol}, Vector{Float64}}(H=0.8, k=3)
julia> seq = generate(g, 4096; rng = MersenneTwister(1))
julia> length(seq) == 4096 && eltype(seq) == Symbol
trueSymbolicLongMemorySequences.LGCM — Type
LGCM(H, alphabet [, marginal]; calibration_iters = 25, calibration_rate = 0.7)Property-based LRD symbol-sequence generator (PB2): Latent Gaussian Categorical Model.
For each symbol, LGCM generates a latent fGn stream with Hurst parameter H. At each time step the emitted symbol is the argmax of the latent streams after adding per-symbol offsets. The offsets are calibrated on the generated latent sample so the output marginal is close to the requested marginal.
Arguments
H::Real: Hurst parameter,H ∈ (0.5, 1.0).alphabet: ordered collection of unique symbols.marginal::AbstractVector{<:Real}: target marginal probabilities.
Keyword Arguments
calibration_iters::Int = 25: number of mean-offset calibration passes.calibration_rate::Real = 0.7: step size for the log-ratio calibration update.
Complexity
O(calibration_iters · n · k) time and O(n · k) memory.
Notes
The marginal calibration is empirical: it adjusts offsets for the realised latent sample rather than solving the exact multivariate Gaussian choice probabilities. It gives practical marginal control while preserving the latent argmax mechanism. For exact finite-sample marginal counts, use SpectralFGN.
Examples
julia> g = LGCM(0.8, [:a, :b], [0.4, 0.6]; calibration_iters = 3)
LGCM{Vector{Symbol}, Vector{Float64}}(H=0.8, k=2)
julia> length(generate(g, 128; rng = MersenneTwister(1)))
128SymbolicLongMemorySequences.WaveletMarkov — Type
WaveletMarkov(H, alphabet, transition_matrices;
regime_weights = uniform, cascade_depth = auto,
driver = :spectral)Property-based LRD symbol-sequence generator (PB3): multiscale cascade driving a Markov state machine.
WaveletMarkov generates a latent long-memory driver, rank-bins the driver into regimes, and lets each regime select one Markov transition matrix over alphabet. The default driver = :spectral uses the same approximate spectral fGn synthesis as SpectralFGN before rank-binning. The legacy driver = :haar path keeps the original simple Haar-style Gaussian cascade for comparison and validation studies.
Arguments
H::Real: Hurst parameter for the latent multiscale driver,H ∈ (0.5, 1.0).alphabet: ordered collection of unique symbols.transition_matrices: vector of row-stochastick × kmatrices, one per regime.
Keyword Arguments
regime_weights: target fraction of time spent in each regime. Defaults to uniform over regimes.cascade_depth::Int = 0: number of dyadic cascade levels.0means choosefloor(log2(n))at generation time. Used only withdriver = :haar.driver::Symbol = :spectral: latent regime driver, either:spectralor:haar.
Complexity
O(n log n + n k) time with O(n + R k²) memory.
Notes
This is a pragmatic PB3 implementation: the spectral driver gives the current default latent LRD pathway, while the Haar-like cascade is retained as a comparison path rather than a calibrated wavelet synthesis package. The important interface property is present: local bigram structure is controlled by explicit Markov matrices while a latent long-memory process controls regime persistence.
Symbol-level ACF and spectrum diagnostics only see this regime persistence when the regimes have different observable stationary symbol distributions. If every regime has the same stationary marginal, the latent multiscale process may be mostly hidden from one-hot symbol diagnostics.
Examples
julia> P1 = [0.9 0.1; 0.2 0.8];
julia> P2 = [0.3 0.7; 0.6 0.4];
julia> g = WaveletMarkov(0.8, [:a, :b], [P1, P2])
WaveletMarkov{Vector{Symbol}, Vector{Float64}}(H=0.8, k=2, R=2, driver=spectral)
julia> length(generate(g, 64; rng = MersenneTwister(1)))
64SymbolicLongMemorySequences.IntermittentMapSymbols — Type
IntermittentMapSymbols(z, alphabet [, marginal]; burnin = 1000)Property-based symbolic generator (PB4) using a latent intermittent map.
The latent driver follows the Pomeau-Manneville-style update
x[t+1] = (x[t] + x[t]^z) mod 1from a random initial state. Intermittency near zero can create long laminar episodes and broad finite-scale dependence. The generated real-valued driver is rank-binned into alphabet, so finite-sample symbol counts are as close as possible to marginal.
This is a latent-dynamics generator, not an exact symbolic LRD construction. The parameter z controls the strength of intermittency, but SymbolicLongMemorySequences.jl does not claim a closed-form finite-sample Hurst parameter for this model.
Arguments
z::Real: intermittency exponent,z > 1.alphabet: ordered collection of symbols.marginal::AbstractVector{<:Real}: target marginal probabilities (default: uniform).
Keyword Arguments
burnin::Int = 1000: number of latent-map iterations discarded before collecting the driver.
Complexity
O(n log n) time from rank binning, O(n) memory.
References
Provata, A., & Beck, C. (2012). Coupled intermittent maps modelling the statistics of genomic sequences: a network approach. arXiv:1205.2249.
Examples
julia> g = IntermittentMapSymbols(1.6, [:A, :B], [0.4, 0.6])
IntermittentMapSymbols{Vector{Symbol}, Vector{Float64}}(z=1.6, k=2, burnin=1000)
julia> seq = generate(g, 1024; rng = MersenneTwister(1))
julia> length(seq) == 1024 && eltype(seq) == Symbol
trueModel-based generators
SymbolicLongMemorySequences.LAMP — Type
LAMP(beta, alphabet [, marginal]; d = 1000, epsilon = 0.01,
transition_matrix = identity)Model-based LRD symbol-sequence generator (MB1a): exact finite-history Linear-Additive Markov Process.
At each step the probability of the next symbol is a convex combination of transition-matrix rows selected by the most recent d history symbols, mixed with an optional innovation term:
q(s) = (1 - epsilon) * Σⱼ wⱼ * P[Xₜ₋ⱼ, s] + epsilon * p(s)with power-law weights wⱼ ∝ j^{-(1+β)}, so the autocovariance decays as a power law with exponent β up to the finite history depth, giving nominal Hurst parameter H = (2−β)/2.
Arguments
beta::Real: ACF decay exponent,β ∈ (0, 1).alphabet: ordered collection of symbols.marginal::AbstractVector{<:Real}: stationary marginal (default: uniform).
Keyword Arguments
d::Int = 1000: history depth. The effective LRD range is bounded byd; for finite sequencesdmay exceedn. Only observed history contributes; missing pre-history mass is assigned to the target marginal.epsilon::Real = 0.01: marginal innovation probability. Larger values improve finite-sample marginal control but weaken history dependence.transition_matrix: row-stochastic transition matrix overalphabet. The default is identity, so history symbols tend to copy themselves. Uselamp_repeat_transitionfor a simple identity/dyad mixture.
Complexity
O(n·min(d,n)) time, O(d + n) memory.
References
Kumar, R., Raghu, M., Sarlos, T., & Tomkins, A. (2017). Linear additive Markov processes. WWW '17, 411–419.
Singh, M., Greenberg, C., & Klakow, D. (2016). The custom decay language model for long range dependencies. TSD, 343–351.
Examples
julia> g = LAMP(0.5, [:a, :b, :c]; d = 500, epsilon = 0.02)
LAMP{Vector{Symbol}, Vector{Float64}}(β=0.5, k=3, d=500, ε=0.02)
julia> seq = generate(g, 5000; rng = MersenneTwister(42))
julia> length(seq) == 5000 && eltype(seq) == Symbol
trueSymbolicLongMemorySequences.DyadicLAMP — Type
DyadicLAMP(beta, alphabet [, marginal]; d = 1_000_000, epsilon = 0.01,
transition_matrix = identity)Scalable dyadic-bucket approximation to LAMP (MB1b).
DyadicLAMP uses the same power-law lag weights and transition-matrix control as exact LAMP, but compresses observed history into age buckets 1, 2:3, 4:7, and so on. Each bucket contributes its total power-law weight times the empirical symbol mix in that bucket. Missing pre-history mass is assigned to the target marginal.
This is a finite-sequence approximation for large d and long sequences; it is not an exact replacement for LAMP.
Complexity
O(n · k · log(n) · log(min(d, n))) time and O(n · k) memory, where k is the alphabet size.
Examples
julia> p = [0.4, 0.6]
julia> P = lamp_repeat_transition(p; repeat_probability = 0.8)
julia> g = DyadicLAMP(0.5, [:a, :b], p; d = 10_000, transition_matrix = P)
DyadicLAMP{Vector{Symbol}, Vector{Float64}}(β=0.5, k=2, d=10000, ε=0.01)
julia> length(generate(g, 1000; rng = MersenneTwister(1))) == 1000
trueSymbolicLongMemorySequences.CalibratedAdditiveMarkov — Type
CalibratedAdditiveMarkov(beta, alphabet [, marginal]; d = 1000,
strength = 0.8)Model-based symbolic generator (MB1c) using a centered additive Markov memory function.
At each step, the next-symbol probabilities are
q(s) = p(s) + strength * Σⱼ wⱼ * (1[X[t-j] = s] - p(s)),where p is the target marginal and wⱼ ∝ j^(-beta) over 1:d. The centered terms sum to zero across symbols, so q remains a probability vector for strength ∈ [0, 1]. Larger strength gives more weight to observed history; strength = 0 gives iid draws from marginal.
This generator is related to additive Markov-chain memory-function models. It is finite-history and does not claim exact asymptotic LRD beyond the configured memory depth d.
Arguments
beta::Real: nominal memory-function decay exponent,beta ∈ (0, 1).alphabet: ordered collection of symbols.marginal::AbstractVector{<:Real}: target marginal probabilities (default: uniform).
Keyword Arguments
d::Int = 1000: history depth and finite memory cutoff.strength::Real = 0.8: history coupling strength in[0, 1].
Complexity
O(n·min(d,n)) time, O(n) memory.
References
Melnyk, S. S., Usatenko, O. V., & Yampol'skii, V. A. (2006). Memory functions of the additive Markov chains: applications to complex dynamic systems. Physica A 361, 405-415.
Mayzelis, Z. A., Apostolov, S. S., Melnyk, S. S., Usatenko, O. V., & Yampol'skii, V. A. (2006). Additive N-step Markov chains as prototype model of symbolic stochastic dynamical systems with long-range correlations.
Examples
julia> g = CalibratedAdditiveMarkov(0.4, [:x, :y], [0.3, 0.7]; d = 200)
CalibratedAdditiveMarkov{Vector{Symbol}, Vector{Float64}}(β=0.4, k=2, d=200, strength=0.8)
julia> length(generate(g, 1000; rng = MersenneTwister(1))) == 1000
trueSymbolicLongMemorySequences.OnOffMarkov — Type
OnOffMarkov(alpha, alphabet, transition_matrices, switching_matrix; L_min = 1.0)Model-based LRD symbol-sequence generator (MB2): Heavy-tailed On/Off doubly-stochastic Markov chain.
The generator alternates between regimes. Each regime has its own Markov transition matrix over alphabet; regime sojourn lengths are Pareto-distributed with tail index alpha. A row-stochastic switching_matrix controls which regime follows the current one after a sojourn ends.
Arguments
alpha::Real: Pareto tail index,alpha ∈ (1, 2), with nominalH = (3 - alpha) / 2.alphabet: ordered collection of unique symbols.transition_matrices: vector of row-stochastick × kmatrices, one per regime.switching_matrix: row-stochasticR × Rregime transition matrix.
Keyword Arguments
L_min::Real = 1.0: Pareto scale parameter for regime sojourns.
Complexity
O(n · k) time with the current sequential sampler and O(n + R · k²) memory.
Notes
This method is the natural first implementation for user-specified bigram structure: each regime has an explicit Markov transition matrix. Aggregate marginals and bigrams depend on regime occupancy, switching dynamics, and the per-regime stationary distributions.
For finite symbol-level ACF and spectrum diagnostics, use regimes with different observable stationary symbol distributions and choose L_min large enough for heavy-tailed sojourns to appear at the simulated sequence length. Regimes with identical stationary marginals can carry latent long memory while looking nearly short-memory to one-hot diagnostics.
Examples
julia> P1 = [0.9 0.1; 0.2 0.8];
julia> P2 = [0.3 0.7; 0.6 0.4];
julia> Q = [0.2 0.8; 0.8 0.2];
julia> g = OnOffMarkov(1.4, [:a, :b], [P1, P2], Q; L_min = 2.0)
OnOffMarkov{Vector{Symbol}}(α=1.4, H≈0.8, k=2, R=2)
julia> length(generate(g, 64; rng = MersenneTwister(1)))
64SymbolicLongMemorySequences.FSS — Type
FSS(alpha, alphabet; rates = ones(k), x_min = 1.0)Model-based LRD symbol-sequence generator (MB3): Fractal Symbol Sequence.
Each symbol is governed by an independent Pareto-distributed renewal process: inter-arrival times τ ~ Pareto(α, x_min). The output merges all symbol streams in event-time order — the symbol with the earliest pending event is emitted at each step.
LRD arises through heavy-tailed inter-arrival times. For tail index α ∈ (1, 2), the return-time variance is infinite, giving nominal Hurst parameter H = (3−α)/2.
Arguments
alpha::Real: Pareto tail index,α ∈ (1, 2).alphabet: ordered collection of symbols.
Keyword Arguments
rates::AbstractVector{<:Real}: per-symbol base arrival rates. Symboliappears with long-run frequency proportional torates[i]. Default: uniform.x_min::Real = 1.0: Pareto scale parameter (minimum inter-arrival time).
Complexity
O(n·k) time, O(k + n) memory (k = alphabet size).
Notes
Because each symbol stream is independent, joint symbol statistics (bigrams, etc.) cannot be prescribed independently of the marginal.
Missing-scales pitfall (Roughan, Yates & Veitch 1999): if the mean inter-arrival time x_min · α/(α−1) / rateᵢ is large relative to n, the observable LRD scale range is reduced. Keep rates such that each symbol appears O(√n) or more times.
References
Lowen, S. B., & Teich, M. C. (1995). Estimation and simulation of fractal stochastic point processes. Fractals 3(1), 183–210.
Roughan, M., Yates, J., & Veitch, D. (1999). The mystery of the missing scales: pitfalls in the use of fractal renewal processes. Applications of Heavy Tailed Distributions in Economics, Engineering and Statistics.
Examples
julia> g = FSS(1.4, [:a, :b, :c])
FSS{Vector{Symbol}, Vector{Float64}}(α=1.4, H≈0.8, k=3)
julia> seq = generate(g, 5000; rng = MersenneTwister(7))
julia> length(seq) == 5000 && eltype(seq) == Symbol
trueSymbolicLongMemorySequences.HawkesSymbol — Type
HawkesSymbol(beta, alphabet; baseline = ones(k), excitation = I,
d = 1000, c = 1.0)Model-based symbolic generator (MB4a): normalized discrete-time Hawkes-style symbols.
At each step the generator forms a non-negative intensity for every symbol,
\[lambda_j(t) = baseline_j + \sum_{l=1}^{\min(d,t-1)} w_l excitation_{X_{t-l},j},\]
where w_l is proportional to (l + c)^(-beta). The next symbol is sampled with probability proportional to these intensities.
This is a finite-history, discrete-time symbolic analogue of the Hawkes-process word-occurrence model of Ogura, Hanada, Amano, and Kondo (2022). It is useful for creating bursty word-like symbolic sequences: with an identity excitation matrix, recent appearances of a symbol raise its chance of appearing again.
Arguments
beta::Real: power-law memory-kernel exponent,beta in (0, 1).alphabet: ordered collection of unique symbols.
Keyword Arguments
baseline::AbstractVector{<:Real}: positive baseline symbol intensities.excitation::AbstractMatrix{<:Real}: non-negativek x kmatrix where rowicontributes excitation after observingalphabet[i].d::Integer = 1000: finite history depth.c::Real = 1.0: positive kernel offset.
Complexity
O(n * k * min(d, n)) time, O(n + d) memory.
Notes
target_marginal(g) reports baseline / sum(baseline), but the realized marginal also depends on excitation, beta, d, and finite-sample effects. This method should therefore be treated as having an implied marginal, not exact marginal control.
References
Ogura, H., Hanada, Y., Amano, H., & Kondo, M. (2022). Modeling long-range dynamic correlations of words in written texts with Hawkes processes. Entropy, 24(7),
- https://doi.org/10.3390/e24070858
Examples
julia> g = HawkesSymbol(0.6, [:a, :b]; baseline = [1.0, 1.0],
... excitation = [2.0 0.0; 0.0 2.0], d = 100)
HawkesSymbol{Vector{Symbol}}(beta=0.6, k=2, d=100)
julia> seq = generate(g, 1000; rng = MersenneTwister(7))
julia> length(seq) == 1000 && eltype(seq) == Symbol
trueSymbolicLongMemorySequences.SelfExcitingMass — Type
SelfExcitingMass(beta, alphabet, marginal = uniform; default_mass = 1.0,
excitation_strength = 1.0, smoothing = 0.05,
excitation = I, d = 1000, c = 1.0)Model-based symbolic generator (MB4b): unnormalized self-exciting symbol mass.
At time t, the generator assigns a non-negative mass to each symbol,
\[m_j(t) = b p_j + \eta\left(s p_j + \sum_{l=1}^{\min(d,t-1)} (l + c)^{-\beta} E_{X_{t-l},j}\right),\]
where p is the target/default marginal, b = default_mass, eta = excitation_strength, s = smoothing, and E is a non-negative excitation matrix. The next symbol is sampled from m(t) / sum(m(t)).
Unlike HawkesSymbol, the power-law kernel is not normalized. The total history mass can therefore grow over the represented scale range, allowing the self-exciting component to dominate the default marginal after startup while the default and smoothing masses keep all symbols reachable. This is a discrete-time categorical adaptation of Hawkes-style self-excitation rather than a fitted continuous-time Hawkes process.
Arguments
beta::Real: power-law memory-kernel exponent,beta in (0, 1).alphabet: ordered collection of unique symbols.marginal: target/default symbol probabilities. Defaults to uniform.
Keyword Arguments
default_mass::Real = 1.0: baseline mass in the target marginal direction.excitation_strength::Real = 1.0: multiplier for the self-exciting mass.smoothing::Real = 0.05: target-marginal floor inside the excitation component.excitation::AbstractMatrix{<:Real}: non-negativek x kmatrix where rowicontributes excitation after observingalphabet[i].d::Integer = 1000: finite history depth.c::Real = 1.0: positive kernel offset.
Complexity
O(n * k * min(d, n)) time, O(n + d) memory.
Notes
target_marginal(g) reports marginal, but strong excitation can distort the finite-sample realized marginal. Use validation diagnostics when marginal control matters.
References
The construction is motivated by Hawkes self-excitation and the use of Hawkes processes for long-range word recurrence, but modifies the categorical sampler by using an unnormalized discrete-time power-law memory mass.
Examples
julia> g = SelfExcitingMass(0.4, [:a, :b], [0.5, 0.5]; d = 100,
... default_mass = 0.2, excitation_strength = 1.0)
SelfExcitingMass{Vector{Symbol}, Vector{Float64}}(beta=0.4, k=2, d=100)
julia> seq = generate(g, 1000; rng = MersenneTwister(7))
julia> length(seq) == 1000 && eltype(seq) == Symbol
trueSymbolicLongMemorySequences.LogitSelfExcitingMass — Type
LogitSelfExcitingMass(beta, alphabet, marginal = uniform;
default_mass = 0.2,
excitation_strength = 1.0,
smoothing = 0.05,
contrast_strength = 1.0,
log_floor = 1e-12,
excitation = I,
d = nothing,
c = 0.0)Model-based symbolic generator (MB4c): log-contrast self-exciting symbol mass.
This variant keeps the MB4b idea of an unnormalized power-law excitation mass, but converts that mass into a centered log contrast before categorical sampling. At time t, define
M_j(t) = (b + eta * s) * p_j
+ eta * sum_{l=1}^{L_t} (l + c)^(-beta) * E[X_{t-l}, j]where L_t = t - 1 when d === nothing, otherwise L_t = min(d, t - 1). MB4c then computes a centered log contrast,
theta_j(t) = a * (log(M_j(t) + epsilon)
- sum_i p_i * log(M_i(t) + epsilon))and samples with weights proportional to
p_j * exp(theta_j(t)).The centering removes common-mode growth in the total excitation mass before the final probability normalization. Thus d is best read as a computational truncation depth; d = nothing represents the full available discrete history. The offset c is a short-lag regularizer, not a long-memory exponent; the asymptotic decay is controlled by beta.
Arguments
beta::Real: power-law memory-kernel exponent,beta in (0, 1).alphabet: ordered collection of unique symbols.marginal: target/default symbol probabilities. Defaults to uniform.
Keyword Arguments
default_mass::Real = 0.2: baseline mass in the target marginal direction.excitation_strength::Real = 1.0: multiplier for self-excitation.smoothing::Real = 0.05: target-marginal floor inside the excitation mass.contrast_strength::Real = 1.0: multiplier applied to centered log contrasts.log_floor::Real = 1e-12: positive floor used inside the logarithm.excitation::AbstractMatrix{<:Real}: non-negative symbol excitation matrix.d::Union{Nothing,Integer} = nothing: computational history truncation.c::Real = 0.0: non-negative short-lag kernel offset.
Complexity
O(n * k * min(d, n)) time for finite d, O(n^2 k) time for d = nothing, and O(n) memory.
Examples
julia> g = LogitSelfExcitingMass(0.4, [:a, :b], [0.5, 0.5]; d = 100)
LogitSelfExcitingMass{Vector{Symbol}, Vector{Float64}}(beta=0.4, k=2, d=100)
julia> seq = generate(g, 1000; rng = MersenneTwister(7))
julia> length(seq) == 1000 && eltype(seq) == Symbol
trueSymbolicLongMemorySequences.DuplicationMutation — Type
DuplicationMutation(alpha, alphabet [, marginal]; mutation_probability = 0.01,
seed_length = 64, max_block_length = 4096)Model-based symbolic growth generator (MB5) using copy-and-mutate block duplication.
Generation starts with seed_length iid symbols from marginal. The sequence then grows one symbol at a time by choosing a power-law copy distance, copying the symbol at that lag, and mutating the copied symbol independently with probability mutation_probability. Copy distances are drawn from a truncated power law P(D = ell) ∝ ell^(-alpha) over the available history, capped by max_block_length.
This is a finite-sequence symbolic analogue of expansion-modification and duplication-mutation ideas. It is naturally DNA-like, but it does not provide direct bigram control or an exact Hurst-parameter guarantee. The power-law copy distance is the part that gives a pathway to broad lag dependence. Earlier block-copy variants with uniformly chosen source blocks mostly created local duplication patches rather than a power-law autocorrelation curve.
Arguments
alpha::Real: copy-distance exponent,alpha > 1; use values near1for slower empirical decay and broader dependence.alphabet: ordered collection of symbols.marginal::AbstractVector{<:Real}: mutation replacement and seed marginal probabilities (default: uniform).
Keyword Arguments
mutation_probability::Real = 0.01: per-symbol mutation probability.seed_length::Int = 64: iid prefix length before copy-mutate growth.max_block_length::Int = 4096: legacy keyword naming the maximum copy distance/backward memory window.
Complexity
O(n log d + d) time and O(n + d) memory, where d = max_block_length.
References
Li, W. (1991). Expansion-modification systems: a model for spatial 1/f spectra. Physical Review A 43, 5240-5260.
Li, W., Marr, T. G., & Kaneko, K. (1994). Understanding long-range correlations in DNA sequences. Physica D 75, 392-416.
Examples
julia> g = DuplicationMutation(1.5, ['A', 'C', 'G', 'T']; mutation_probability = 0.02)
DuplicationMutation{Vector{Char}, Vector{Float64}}(α=1.5, k=4, μ=0.02, seed=64, max_block=4096)
julia> length(generate(g, 500; rng = MersenneTwister(1))) == 500
trueUtilities
SymbolicLongMemorySequences.target_marginal — Function
target_marginal(g) -> Vector{Float64}Return the marginal probabilities a generator claims to target.
Examples
julia> target_marginal(SpectralFGN(0.8, [:a, :b], [0.25, 0.75]))
2-element Vector{Float64}:
0.25
0.75SymbolicLongMemorySequences.empirical_marginal — Function
empirical_marginal(seq, alphabet) -> Vector{Float64}Estimate the marginal distribution of seq over alphabet.
Examples
julia> empirical_marginal([:a, :b, :b, :a], [:a, :b])
2-element Vector{Float64}:
0.5
0.5SymbolicLongMemorySequences.empirical_bigram — Function
empirical_bigram(seq, alphabet) -> Matrix{Float64}Estimate row-normalised bigram transition probabilities over alphabet. Rows with no observations are left as zeros.
Examples
julia> empirical_bigram([:a, :b, :b, :a], [:a, :b])
2×2 Matrix{Float64}:
0.0 1.0
0.5 0.5SymbolicLongMemorySequences.empirical_trigram — Function
empirical_trigram(seq, alphabet) -> Array{Float64,3}Estimate trigram probabilities P(X[t+2] | X[t], X[t+1]) over alphabet. Slices with no observations are left as zeros.
Examples
julia> T = empirical_trigram([:a, :b, :a, :b], [:a, :b]);
julia> T[1, 2, :]
2-element Vector{Float64}:
1.0
0.0SymbolicLongMemorySequences.bin_counts — Function
bin_counts(marginal, n) -> Vector{Int}Return integer bin counts for n observations with proportions as close as possible to marginal.
The counts are obtained by flooring n .* marginal and distributing the remaining observations to the largest fractional remainders. Ties are broken by alphabet order, which makes the result deterministic.
Examples
julia> bin_counts([0.2, 0.3, 0.5], 10)
3-element Vector{Int64}:
2
3
5SymbolicLongMemorySequences.total_variation — Function
total_variation(p, q) -> Float64Return the total variation distance between two probability arrays.
Examples
julia> total_variation([0.25, 0.75], [0.5, 0.5])
0.25SymbolicLongMemorySequences.rowwise_total_variation — Function
rowwise_total_variation(observed, target) -> Vector{Float64}Return total variation distance for each row of two transition matrices.
Examples
julia> rowwise_total_variation([0.5 0.5; 0.0 1.0], [1.0 0.0; 0.0 1.0])
2-element Vector{Float64}:
0.5
0.0SymbolicLongMemorySequences.validate_transition_matrix — Function
validate_transition_matrix(P, name = "transition_matrix") -> Matrix{Float64}Convert P to a dense Matrix{Float64} and check that it is square, finite, non-negative, non-empty, and row-stochastic.
Examples
julia> validate_transition_matrix([0.8 0.2; 0.1 0.9])
2×2 Matrix{Float64}:
0.8 0.2
0.1 0.9SymbolicLongMemorySequences.stationary_distribution — Function
stationary_distribution(P; maxiter = 10_000, tol = 1e-12) -> Vector{Float64}Return a stationary distribution for a row-stochastic transition matrix using power iteration.
Examples
julia> round.(stationary_distribution([0.8 0.2; 0.1 0.9]); digits = 3)
2-element Vector{Float64}:
0.333
0.667SymbolicLongMemorySequences.lamp_repeat_transition — Function
lamp_repeat_transition(marginal; repeat_probability = 0.8) -> Matrix{Float64}Construct a row-stochastic identity/dyad transition matrix for LAMP.
The matrix is
P[i, j] = repeat_probability * 𝟏[i = j] +
(1 - repeat_probability) * marginal[j]so larger repeat_probability makes the process more likely to repeat the state selected from history, while the dyad term pulls rows back toward the target marginal.
Examples
julia> P = lamp_repeat_transition([0.25, 0.75]; repeat_probability = 0.8)
2×2 Matrix{Float64}:
0.85 0.15
0.05 0.95SymbolicLongMemorySequences.quantize_to_symbols — Function
quantize_to_symbols(x, alphabet, marginal) -> VectorMap a real-valued sequence x to symbols from alphabet using rank binning.
The sorted values of x are partitioned into integer bin counts from bin_counts, then mapped back to the original order. This avoids threshold edge cases and makes each finite-sample marginal as close as possible to the requested marginal.
Arguments
x::AbstractVector{<:Real}: real-valued input sequence.alphabet: ordered collection ofkunique symbols.marginal::AbstractVector{<:Real}: target symbol probabilities.
Examples
julia> x = randn(1000)
julia> s = quantize_to_symbols(x, [:L, :M, :H], [0.25, 0.5, 0.25])
julia> count(==(:M), s) / 1000 ≈ 0.5
trueSymbolicLongMemorySequences._fgn_spectral — Function
_fgn_spectral(n, H, rng) -> Vector{Float64}Generate length-n fractional Gaussian noise with Hurst parameter H using Paxson's (1997) approximate spectral method.
Builds the target power spectrum S(f) ∝ |f|^(1−2H) on the DFT grid, fills with scaled complex Gaussian noise with Hermitian symmetry, then inverse-FFTs. Output is normalised to zero mean and unit standard deviation. Requires n ≥ 4.
Examples
julia> x = SymbolicLongMemorySequences._fgn_spectral(8, 0.75, MersenneTwister(1));
julia> length(x), round(mean(x); digits = 12)
(8, 0.0)SymbolicLongMemorySequences._pareto_sample — Function
_pareto_sample(rng, alpha, x_min) -> Float64Draw from a Pareto distribution with shape alpha and scale x_min using Distributions.jl.
Examples
julia> SymbolicLongMemorySequences._pareto_sample(MersenneTwister(1), 1.4, 1.0) >= 1.0
true