//+------------------------------------------------------------------+ //| KronosDecoder.mqh | //| MMQ — Muhammad Minhas Qamar | //| www.mql5.com | //+------------------------------------------------------------------+ #property copyright "MMQ — Muhammad Minhas Qamar" #property link "https://www.mql5.com" #property version "1.00" #ifndef KRONOS_DECODER_MQH #define KRONOS_DECODER_MQH #include // KronosBSQ_IndicesToCode, KR_NFEAT(6), KR_CODEBOOK_DIM(20) #include // LinearT, AddRowBias, TransformerBlock //+------------------------------------------------------------------+ //| Tokenizer decoder. | //+------------------------------------------------------------------+ class CKronosDecoder { private: int m_blocks; // n_dec_layers - 1 int m_heads; ulong m_dm; // d_model ulong m_ff; // ff_dim string m_dir; //--- single linears matrix m_pqW; vector m_pqB; // post_quant_embed : [d_model, 20] matrix m_headW; vector m_headB; // head : [6, d_model] //--- per-block weights (decoder_0 .. decoder_{m_blocks-1}) vector m_n1[]; // norm1.weight matrix m_Wq[]; vector m_bq[]; // self_attn.q_proj.weight/bias matrix m_Wk[]; vector m_bk[]; // self_attn.k_proj.weight/bias matrix m_Wv[]; vector m_bv[]; // self_attn.v_proj.weight/bias matrix m_Wo[]; vector m_bo[]; // self_attn.out_proj.weight/bias vector m_n2[]; // norm2.weight matrix m_W1[]; // ffn.w1.weight : [ff, d_model] matrix m_W3[]; // ffn.w3.weight : [ff, d_model] matrix m_W2[]; // ffn.w2.weight : [d_model, ff] string F(const string name) { return m_dir + name + ".bin"; } public: //+---------------------------------------------------------------+ //| Load all decoder weights. Tensor names follow the tokenizer | //| manifest: post_quant_embed_weight/bias, head_weight/bias, and | //| decoder_N_* for each block (same layout as encoder_N_*). | //+---------------------------------------------------------------+ bool Init(const string weight_dir, int n_dec_layers, ulong d_model, int n_heads, ulong ff_dim) { m_dir = weight_dir; m_dm = d_model; m_heads = n_heads; m_ff = ff_dim; m_blocks = n_dec_layers - 1; // the (n-1) quirk, same as the encoder if(d_model == 0 || n_heads == 0 || n_dec_layers == 0 || ff_dim == 0) { Print("CKronosDecoder::Init: invalid config (zero dimension)"); return false; } bool ok = true; //--- post_quant_embed (20 -> d_model) and head (d_model -> 6); LinearT //--- weights stored transposed (W^T) so LinearT is a plain MatMul ok &= KronosLoadMatrixT(F("post_quant_embed_weight"), m_dm, KR_CODEBOOK_DIM, m_pqW); ok &= KronosLoadVector(F("post_quant_embed_bias"), m_dm, m_pqB); ok &= KronosLoadMatrixT(F("head_weight"), KR_NFEAT, m_dm, m_headW); ok &= KronosLoadVector(F("head_bias"), KR_NFEAT, m_headB); ArrayResize(m_n1, m_blocks); ArrayResize(m_n2, m_blocks); ArrayResize(m_Wq, m_blocks); ArrayResize(m_bq, m_blocks); ArrayResize(m_Wk, m_blocks); ArrayResize(m_bk, m_blocks); ArrayResize(m_Wv, m_blocks); ArrayResize(m_bv, m_blocks); ArrayResize(m_Wo, m_blocks); ArrayResize(m_bo, m_blocks); ArrayResize(m_W1, m_blocks); ArrayResize(m_W3, m_blocks); ArrayResize(m_W2, m_blocks); for(int b = 0; b < m_blocks; b++) { string p = StringFormat("decoder_%d_", b); // ModuleList index ok &= KronosLoadVector(F(p + "norm1_weight"), m_dm, m_n1[b]); ok &= KronosLoadMatrixT(F(p + "self_attn_q_proj_weight"), m_dm, m_dm, m_Wq[b]); ok &= KronosLoadVector(F(p + "self_attn_q_proj_bias"), m_dm, m_bq[b]); ok &= KronosLoadMatrixT(F(p + "self_attn_k_proj_weight"), m_dm, m_dm, m_Wk[b]); ok &= KronosLoadVector(F(p + "self_attn_k_proj_bias"), m_dm, m_bk[b]); ok &= KronosLoadMatrixT(F(p + "self_attn_v_proj_weight"), m_dm, m_dm, m_Wv[b]); ok &= KronosLoadVector(F(p + "self_attn_v_proj_bias"), m_dm, m_bv[b]); ok &= KronosLoadMatrixT(F(p + "self_attn_out_proj_weight"), m_dm, m_dm, m_Wo[b]); ok &= KronosLoadVector(F(p + "self_attn_out_proj_bias"), m_dm, m_bo[b]); ok &= KronosLoadVector(F(p + "norm2_weight"), m_dm, m_n2[b]); ok &= KronosLoadMatrixT(F(p + "ffn_w1_weight"), m_ff, m_dm, m_W1[b]); ok &= KronosLoadMatrixT(F(p + "ffn_w3_weight"), m_ff, m_dm, m_W3[b]); ok &= KronosLoadMatrixT(F(p + "ffn_w2_weight"), m_dm, m_ff, m_W2[b]); if(!ok) { PrintFormat("CKronosDecoder::Init failed at block %d", b); return false; } } return ok; } //+---------------------------------------------------------------+ //| Decode hierarchical token ids into a normalized OHLCVA window.| //| s1_ids,s2_ids : input token ids, length L | //| recon_norm : output (L, 6), still z-scored (denormalize | //| separately with the window's mean/std) | //+---------------------------------------------------------------+ bool Decode(const int &s1_ids[], const int &s2_ids[], matrix &recon_norm) { int L = ArraySize(s1_ids); if(L == 0 || ArraySize(s2_ids) != L) { PrintFormat("CKronosDecoder::Decode: bad id lengths (%d / %d)", L, ArraySize(s2_ids)); return false; } //--- indices_to_bits (half): each row is the 20-dim bipolar code (LSB-first) matrix code = matrix::Zeros((ulong)L, KR_CODEBOOK_DIM); double row[]; for(int t = 0; t < L; t++) { KronosBSQ_IndicesToCode(s1_ids[t], s2_ids[t], row); // verified BSQ math for(int k = 0; k < KR_CODEBOOK_DIM; k++) code[t][k] = row[k]; } //--- post_quant_embed: 20 -> d_model matrix z = LinearT(code, m_pqW); AddRowBias(z, m_pqB); //--- (n_dec_layers - 1) causal pre-norm blocks for(int b = 0; b < m_blocks; b++) z = TransformerBlock(z, m_n1[b], m_Wq[b], m_bq[b], m_Wk[b], m_bk[b], m_Wv[b], m_bv[b], m_Wo[b], m_bo[b], m_heads, m_n2[b], m_W1[b], m_W3[b], m_W2[b]); //--- head: d_model -> 6 (normalized OHLCVA) recon_norm = LinearT(z, m_headW); AddRowBias(recon_norm, m_headB); return true; } }; #endif // KRONOS_DECODER_MQH //+------------------------------------------------------------------+