kronos-mql5/Include/Kronos/KronosDecoder.mqh

153 lines
7.4 KiB
MQL5
Raw Permalink Normal View History

2026-07-05 05:03:49 +00:00
//+------------------------------------------------------------------+
//| 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 <Kronos\KronosTokenizerMath.mqh> // KronosBSQ_IndicesToCode, KR_NFEAT(6), KR_CODEBOOK_DIM(20)
#include <Kronos\KronosTransformerCore.mqh> // 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
//+------------------------------------------------------------------+