| 166 | engine::io::require_all_positive(config.downsample_factors, "downsample_factors"); |
| 167 | engine::io::require_all_positive(config.downsample_kernel_sizes, "downsample_kernel_sizes"); |
| 168 | engine::io::require_all_positive(config.upsample_factors, "upsample_factors"); |
| 169 | engine::io::require_all_positive(config.upsample_kernel_sizes, "upsample_kernel_sizes"); |
| 170 | if (config.downsample_factors.size() != config.downsample_kernel_sizes.size() || |
| 171 | config.upsample_factors.size() != config.upsample_kernel_sizes.size()) { |
| 172 | throw std::runtime_error("HeartCodec config sampling factor/kernel counts must match"); |
| 173 | } |
| 174 | if (config.norm_type != "ada_norm_single") { |
| 175 | throw std::runtime_error("HeartCodec config norm_type must be ada_norm_single"); |
| 176 | } |
| 177 | if (!config.causal) { |
| 178 | throw std::runtime_error("HeartCodec config must be causal"); |
| 179 | } |
| 180 | return config; |
| 181 | } |
| 182 | |
| 183 | void validate_mula_weight_anchors(const HeartMuLaAssets & assets) { |
| 184 | const auto & config = assets.mula_config; |
| 185 | const auto & weights = *assets.mula_weights; |
| 186 | const auto & backbone = config.backbone; |
| 187 | const auto & decoder = config.decoder; |
| 188 | assets::require_tensor_shape(weights, "text_embeddings.weight", {config.text_vocab_size, backbone.embed_dim}); |
| 189 | assets::require_tensor_shape( |
| 190 | weights, |
| 191 | "audio_embeddings.weight", |
| 192 | {config.audio_vocab_size * config.audio_num_codebooks, backbone.embed_dim}); |
| 193 | assets::require_tensor_shape(weights, "unconditional_text_embedding.weight", {1, backbone.embed_dim}); |
| 194 | assets::require_tensor_shape(weights, "projection.weight", {decoder.embed_dim, backbone.embed_dim}); |
| 195 | assets::require_tensor_shape(weights, "codebook0_head.weight", {config.audio_vocab_size, backbone.embed_dim}); |
| 196 | assets::require_tensor_shape( |
| 197 | weights, |
| 198 | "audio_head", |
| 199 | {config.audio_num_codebooks - 1, decoder.embed_dim, config.audio_vocab_size}); |
| 200 | assets::require_tensor_shape(weights, "muq_linear.weight", {backbone.embed_dim, config.muq_dim}); |
| 201 | assets::require_tensor_shape(weights, "muq_linear.bias", {backbone.embed_dim}); |
| 202 | assets::require_tensor_shape(weights, "backbone.norm.scale", {backbone.embed_dim}); |
| 203 | assets::require_tensor_shape(weights, "decoder.norm.scale", {decoder.embed_dim}); |
| 204 | assets::require_tensor_shape(weights, "backbone.layers.0.attn.q_proj.weight", {backbone.embed_dim, backbone.embed_dim}); |
| 205 | assets::require_tensor_shape( |
| 206 | weights, |
| 207 | "backbone.layers.0.attn.k_proj.weight", |
| 208 | {backbone.num_kv_heads * backbone.head_dim, backbone.embed_dim}); |
| 209 | assets::require_tensor_shape( |
| 210 | weights, |
| 211 | "backbone.layers.0.attn.v_proj.weight", |
| 212 | {backbone.num_kv_heads * backbone.head_dim, backbone.embed_dim}); |
| 213 | assets::require_tensor_shape( |
| 214 | weights, |
| 215 | "backbone.layers.0.attn.output_proj.weight", |
| 216 | {backbone.embed_dim, backbone.embed_dim}); |
| 217 | assets::require_tensor_shape(weights, "backbone.layers.0.mlp.w1.weight", {backbone.intermediate_dim, backbone.embed_dim}); |
| 218 | assets::require_tensor_shape(weights, "backbone.layers.0.mlp.w2.weight", {backbone.embed_dim, backbone.intermediate_dim}); |
| 219 | assets::require_tensor_shape(weights, "backbone.layers.0.mlp.w3.weight", {backbone.intermediate_dim, backbone.embed_dim}); |
| 220 | assets::require_tensor_shape(weights, "backbone.layers.0.sa_norm.scale", {backbone.embed_dim}); |
| 221 | assets::require_tensor_shape(weights, "backbone.layers.0.mlp_norm.scale", {backbone.embed_dim}); |
| 222 | assets::require_tensor_shape(weights, "decoder.layers.0.attn.q_proj.weight", {decoder.embed_dim, decoder.embed_dim}); |
| 223 | assets::require_tensor_shape( |
| 224 | weights, |
| 225 | "decoder.layers.0.attn.k_proj.weight", |
no test coverage detected