#ifndef __SD_MODEL_DIFFUSION_MINIMAX_H3_HPP__ #define __SD_MODEL_DIFFUSION_MINIMAX_H3_HPP__ #include #include #include #include #include #include #include #include #include "core/ggml_tensor_utils.h" #include "core/ggml_graph_cut.h" #include "model/diffusion/dit.hpp" #include "model/diffusion/model.hpp" namespace MiniMaxH3 { constexpr int H3_GRAPH_SIZE = 131072; constexpr float FRAME_RESCALE = 5.f / 3.f; constexpr float VISUAL_COND_TIMESTEP = 0.999f; struct Config { int64_t hidden_size = 5376; int64_t num_layers = 50; int64_t token_refiner_num_layers = 2; int64_t num_attention_heads = 56; int64_t attention_head_dim = 128; int64_t ffn_hidden_size = 14336; int64_t video_latent_channels = 24; int64_t audio_latent_channels = 32; int64_t text_dim = 5120; int64_t timestep_input_dim = 256; int64_t time_embed_hidden_size = 5376; int64_t time_embed_dim = 2688; int64_t rope_inv_freq_len = 16; int64_t adaln_curve_grid = 0; int patch_t = 1; int patch_h = 2; int patch_w = 2; float norm_eps = 1e-5f; float qk_norm_eps = 1e-5f; float final_norm_eps = 1e-5f; bool uses_adaln_curves() const { return adaln_curve_grid > 0; } static int64_t count_blocks(const String2TensorStorage& tensors, const std::string& prefix) { std::set indices; for (const auto& [name, _] : tensors) { if (!starts_with(name, prefix)) { continue; } size_t begin = prefix.size(); size_t end = name.find('.', begin); if (end != std::string::npos) { indices.insert(std::atoi(name.substr(begin, end - begin).c_str())); } } return static_cast(indices.size()); } static Config detect_from_weights(const String2TensorStorage& tensors, const std::string& prefix) { Config config; auto find = [&](const std::string& suffix) -> const TensorStorage* { auto it = tensors.find(prefix + "." + suffix); return it == tensors.end() ? nullptr : &it->second; }; if (const auto* weight = find("video_patch_proj.weight")) { config.video_latent_channels = weight->ne[0] / 4; config.hidden_size = weight->ne[1]; } if (const auto* weight = find("audio_patch_proj.weight")) { config.audio_latent_channels = weight->ne[0]; } config.num_layers = count_blocks(tensors, prefix + ".blocks."); config.token_refiner_num_layers = count_blocks(tensors, prefix + ".token_refiner.blocks."); if (const auto* weight = find("blocks.0.attn.q_norm.weight")) { config.attention_head_dim = weight->ne[0]; } if (const auto* weight = find("blocks.0.attn.qkv_proj.weight")) { config.num_attention_heads = weight->ne[1] / (3 * config.attention_head_dim); } if (const auto* weight = find("blocks.0.mlp.fc1.weight")) { config.ffn_hidden_size = weight->ne[1] / 2; } if (const auto* weight = find("condition_proj.weight")) { config.text_dim = weight->ne[0]; } if (const auto* table = find("adaln_t_table")) { config.time_embed_dim = table->ne[0]; config.adaln_curve_grid = table->ne[1]; } else { if (const auto* weight = find("time_embedder.proj_in.weight")) { config.timestep_input_dim = weight->ne[0]; config.time_embed_hidden_size = weight->ne[1]; } if (const auto* weight = find("time_embedder.proj_out.weight")) { config.time_embed_dim = weight->ne[1]; } } if (const auto* inv_freq = find("rope.inv_freq")) { config.rope_inv_freq_len = inv_freq->ne[0]; } LOG_VERBOSE("minimax_h3: layers=%" PRId64 ", hidden=%" PRId64 ", heads=%" PRId64 ", head_dim=%" PRId64 ", ffn=%" PRId64 ", adaln_curve=%" PRId64, config.num_layers, config.hidden_size, config.num_attention_heads, config.attention_head_dim, config.ffn_hidden_size, config.adaln_curve_grid); return config; } }; static float time_shift_sigma(float sigma, float from_shift, float to_shift) { float base = sigma / (from_shift + sigma * (1.f - from_shift)); return to_shift * base / (1.f + (to_shift - 1.f) * base); } struct TimeEmbedder : public GGMLBlock { TimeEmbedder(int64_t input_dim, int64_t hidden_dim, int64_t output_dim) { blocks["proj_in"] = std::make_shared(input_dim, hidden_dim, true, true); blocks["proj_out"] = std::make_shared(hidden_dim, output_dim, true, true); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) { auto proj_in = std::dynamic_pointer_cast(blocks["proj_in"]); auto proj_out = std::dynamic_pointer_cast(blocks["proj_out"]); return proj_out->forward(ctx, ggml_silu(ctx->ggml_ctx, proj_in->forward(ctx, x))); } }; struct MLP : public UnaryBlock { MLP(int64_t hidden_size, int64_t ffn_hidden_size) { blocks["fc1"] = std::make_shared(hidden_size, ffn_hidden_size * 2, false, false, true, 1.f / 128.f); blocks["fc2"] = std::make_shared(ffn_hidden_size, hidden_size, false, false, true, 1.f / 128.f); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override { auto fc1 = std::dynamic_pointer_cast(blocks["fc1"]); auto fc2 = std::dynamic_pointer_cast(blocks["fc2"]); auto uv = ggml_ext_chunk(ctx->ggml_ctx, fc1->forward(ctx, x), 2, 0); return fc2->forward(ctx, ggml_mul(ctx->ggml_ctx, ggml_silu(ctx->ggml_ctx, uv[0]), uv[1])); } }; static ggml_tensor* attention_layout(ggml_context* ctx, ggml_tensor* x) { x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); return ggml_reshape_3d(ctx, x, x->ne[0], x->ne[1], x->ne[2] * x->ne[3]); } static ggml_tensor* apply_partial_rope(ggml_context* ctx, ggml_tensor* x, ggml_tensor* pe) { int64_t rot_dim = pe->ne[2] * 2; GGML_ASSERT(rot_dim <= x->ne[0]); auto rotated = Rope::apply_rope(ctx, ggml_ext_slice(ctx, x, 0, 0, rot_dim), pe, false); if (rot_dim == x->ne[0]) { return rotated; } auto tail = attention_layout(ctx, ggml_ext_slice(ctx, x, 0, rot_dim, x->ne[0])); return ggml_concat(ctx, rotated, tail, 0); } struct Attention : public GGMLBlock { int64_t heads; int64_t head_dim; Attention(int64_t hidden_size, int64_t heads, int64_t head_dim, float eps) : heads(heads), head_dim(head_dim) { int64_t inner = heads * head_dim; blocks["qkv_proj"] = std::make_shared(hidden_size, inner * 3, false); blocks["q_norm"] = std::make_shared(head_dim, eps); blocks["k_norm"] = std::make_shared(head_dim, eps); blocks["out_proj"] = std::make_shared(inner, hidden_size, false); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* pe = nullptr) { auto qkv_proj = std::dynamic_pointer_cast(blocks["qkv_proj"]); auto q_norm = std::dynamic_pointer_cast(blocks["q_norm"]); auto k_norm = std::dynamic_pointer_cast(blocks["k_norm"]); auto out_proj = std::dynamic_pointer_cast(blocks["out_proj"]); int64_t sequence = x->ne[1]; int64_t batch = x->ne[2] * x->ne[3]; auto qkv = ggml_ext_chunk(ctx->ggml_ctx, qkv_proj->forward(ctx, x), 3, 0); auto q = ggml_reshape_4d(ctx->ggml_ctx, qkv[0], head_dim, heads, sequence, batch); auto k = ggml_reshape_4d(ctx->ggml_ctx, qkv[1], head_dim, heads, sequence, batch); auto v = ggml_reshape_4d(ctx->ggml_ctx, qkv[2], head_dim, heads, sequence, batch); q = q_norm->forward(ctx, q); k = k_norm->forward(ctx, k); if (pe != nullptr) { q = apply_partial_rope(ctx->ggml_ctx, q, pe); k = apply_partial_rope(ctx->ggml_ctx, k, pe); } else { q = attention_layout(ctx->ggml_ctx, q); k = attention_layout(ctx->ggml_ctx, k); } auto out = ggml_ext_attention_ext(ctx, q, k, v, static_cast(heads), nullptr, true, ctx->flash_attn_enabled, 1.f / 128.f); return out_proj->forward(ctx, out); } }; struct TokenRefinerBlock : public GGMLBlock { TokenRefinerBlock(const Config& config) { blocks["norm1"] = std::make_shared(config.hidden_size, config.norm_eps); blocks["norm2"] = std::make_shared(config.hidden_size, config.norm_eps); blocks["attn"] = std::make_shared(config.hidden_size, config.num_attention_heads, config.attention_head_dim, config.qk_norm_eps); blocks["mlp"] = std::make_shared(config.hidden_size, config.ffn_hidden_size); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) { auto norm1 = std::dynamic_pointer_cast(blocks["norm1"]); auto norm2 = std::dynamic_pointer_cast(blocks["norm2"]); auto attn = std::dynamic_pointer_cast(blocks["attn"]); auto mlp = std::dynamic_pointer_cast(blocks["mlp"]); x = ggml_add(ctx->ggml_ctx, x, attn->forward(ctx, norm1->forward(ctx, x))); return ggml_add(ctx->ggml_ctx, x, mlp->forward(ctx, norm2->forward(ctx, x))); } }; struct TokenRefiner : public GGMLBlock { int64_t num_layers; explicit TokenRefiner(const Config& config) : num_layers(config.token_refiner_num_layers) { for (int64_t i = 0; i < num_layers; ++i) { blocks["blocks." + std::to_string(i)] = std::make_shared(config); } blocks["final_norm"] = std::make_shared(config.hidden_size, config.final_norm_eps); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) { for (int64_t i = 0; i < num_layers; ++i) { auto block = std::dynamic_pointer_cast(blocks["blocks." + std::to_string(i)]); x = block->forward(ctx, x); } return std::dynamic_pointer_cast(blocks["final_norm"])->forward(ctx, x); } }; struct AdaLayerNormModulation : public GGMLBlock { int64_t hidden_size; int expand; int modalities; bool apply_silu; AdaLayerNormModulation(int64_t time_dim, int64_t hidden_size, int expand, int modalities, bool apply_silu, bool force_f32) : hidden_size(hidden_size), expand(expand), modalities(modalities), apply_silu(apply_silu) { blocks["linear"] = std::make_shared(time_dim, hidden_size * expand * modalities, true, force_f32); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* t_emb) { if (apply_silu) { t_emb = ggml_silu(ctx->ggml_ctx, t_emb); } return std::dynamic_pointer_cast(blocks["linear"])->forward(ctx, t_emb); } }; struct TokenModulationSpan { int64_t start; int64_t end; int64_t modulation_row; }; enum class SequenceKind { TEXT, CONDITION_VIDEO, CONDITION_AUDIO, TARGET_AUDIO, TARGET_VIDEO, }; struct SequenceSegment { int64_t start; int64_t end; SequenceKind kind; int32_t source_index = -1; }; static std::vector modulation_row(ggml_context* ctx, ggml_tensor* projection, int64_t hidden_size, int expand, int modalities, int64_t row) { int64_t timestep_rows = projection->ne[1]; auto reshaped = ggml_reshape_2d(ctx, projection, hidden_size * expand, timestep_rows * modalities); auto selected = ggml_ext_slice(ctx, reshaped, 1, row, row + 1); return ggml_ext_chunk(ctx, selected, expand, 0); } static ggml_tensor* modulate_segments(ggml_context* ctx, ggml_tensor* x, ggml_tensor* projection, const std::vector& segments, int64_t hidden_size, int expand, int modalities, int shift_index, int scale_index) { ggml_tensor* out = nullptr; for (const auto& segment : segments) { auto mods = modulation_row(ctx, projection, hidden_size, expand, modalities, segment.modulation_row); auto part = ggml_ext_slice(ctx, x, 1, segment.start, segment.end); part = ggml_add(ctx, ggml_add(ctx, part, ggml_mul(ctx, part, mods[scale_index])), mods[shift_index]); out = out == nullptr ? part : ggml_concat(ctx, out, part, 1); } return out; } static ggml_tensor* gated_residual_segments(ggml_context* ctx, ggml_tensor* x, ggml_tensor* update, ggml_tensor* projection, const std::vector& segments, int64_t hidden_size, int gate_index) { ggml_tensor* out = nullptr; for (const auto& segment : segments) { auto mods = modulation_row(ctx, projection, hidden_size, 6, 3, segment.modulation_row); auto base = ggml_ext_slice(ctx, x, 1, segment.start, segment.end); auto add = ggml_ext_slice(ctx, update, 1, segment.start, segment.end); auto part = ggml_add(ctx, base, ggml_mul(ctx, add, mods[gate_index])); out = out == nullptr ? part : ggml_concat(ctx, out, part, 1); } return out; } struct TransformerBlock : public GGMLBlock { Config config; explicit TransformerBlock(const Config& config) : config(config) { blocks["norm1"] = std::make_shared(config.hidden_size, config.norm_eps); blocks["norm2"] = std::make_shared(config.hidden_size, config.norm_eps); blocks["attn"] = std::make_shared(config.hidden_size, config.num_attention_heads, config.attention_head_dim, config.qk_norm_eps); blocks["mlp"] = std::make_shared(config.hidden_size, config.ffn_hidden_size); blocks["adaln_proj"] = std::make_shared(config.time_embed_dim, config.hidden_size, 6, 3, !config.uses_adaln_curves(), config.uses_adaln_curves()); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* t_emb, const std::vector& segments, ggml_tensor* pe) { auto norm1 = std::dynamic_pointer_cast(blocks["norm1"]); auto norm2 = std::dynamic_pointer_cast(blocks["norm2"]); auto attn = std::dynamic_pointer_cast(blocks["attn"]); auto mlp = std::dynamic_pointer_cast(blocks["mlp"]); auto adaln = std::dynamic_pointer_cast(blocks["adaln_proj"]); auto mods = adaln->forward(ctx, t_emb); auto h = modulate_segments(ctx->ggml_ctx, norm1->forward(ctx, x), mods, segments, config.hidden_size, 6, 3, 0, 1); x = gated_residual_segments(ctx->ggml_ctx, x, attn->forward(ctx, h, pe), mods, segments, config.hidden_size, 2); h = modulate_segments(ctx->ggml_ctx, norm2->forward(ctx, x), mods, segments, config.hidden_size, 6, 3, 3, 4); return gated_residual_segments(ctx->ggml_ctx, x, mlp->forward(ctx, h), mods, segments, config.hidden_size, 5); } }; struct FinalLayer : public GGMLBlock { Config config; explicit FinalLayer(const Config& config) : config(config) { int64_t video_dim = config.video_latent_channels * config.patch_t * config.patch_h * config.patch_w; blocks["norm"] = std::make_shared(config.hidden_size, config.final_norm_eps); blocks["adaln_proj"] = std::make_shared(config.time_embed_dim, config.hidden_size, 2, 1, !config.uses_adaln_curves(), config.uses_adaln_curves()); blocks["video_out"] = std::make_shared(config.hidden_size, video_dim, true, true); blocks["audio_out"] = std::make_shared(config.hidden_size, config.audio_latent_channels, true, true); } std::pair forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* t_emb, const TokenModulationSpan& video, const TokenModulationSpan& audio) { auto norm = std::dynamic_pointer_cast(blocks["norm"]); auto adaln = std::dynamic_pointer_cast(blocks["adaln_proj"]); auto video_out = std::dynamic_pointer_cast(blocks["video_out"]); auto audio_out = std::dynamic_pointer_cast(blocks["audio_out"]); auto mods = adaln->forward(ctx, t_emb); auto apply = [&](const TokenModulationSpan& segment) { auto row = modulation_row(ctx->ggml_ctx, mods, config.hidden_size, 2, 1, segment.modulation_row); auto value = norm->forward(ctx, ggml_ext_slice(ctx->ggml_ctx, x, 1, segment.start, segment.end)); return ggml_add(ctx->ggml_ctx, ggml_add(ctx->ggml_ctx, value, ggml_mul(ctx->ggml_ctx, value, row[1])), row[0]); }; return {video_out->forward(ctx, apply(video)), audio_out->forward(ctx, apply(audio))}; } }; struct MiniMaxH3Transformer3DModel : public GGMLBlock { Config config; explicit MiniMaxH3Transformer3DModel(const Config& config) : config(config) { int64_t video_dim = config.video_latent_channels * config.patch_t * config.patch_h * config.patch_w; blocks["video_patch_proj"] = std::make_shared(video_dim, config.hidden_size, true, true); blocks["audio_patch_proj"] = std::make_shared(config.audio_latent_channels, config.hidden_size, true, true); blocks["condition_proj"] = std::make_shared(config.text_dim, config.hidden_size, true); if (!config.uses_adaln_curves()) { blocks["time_embedder"] = std::make_shared(config.timestep_input_dim, config.time_embed_hidden_size, config.time_embed_dim); } blocks["token_refiner"] = std::make_shared(config); for (int64_t i = 0; i < config.num_layers; ++i) { blocks["blocks." + std::to_string(i)] = std::make_shared(config); } blocks["final_layer"] = std::make_shared(config); } void init_params(ggml_context* ctx, const String2TensorStorage& tensors = {}, const std::string prefix = "") override { GGMLBlock::init_params(ctx, tensors, prefix); params["rope.inv_freq"] = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, config.rope_inv_freq_len); if (config.uses_adaln_curves()) { params["adaln_t_table"] = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, config.time_embed_dim, config.adaln_curve_grid); } } ggml_tensor* refine_context(GGMLRunnerContext* ctx, ggml_tensor* context) { if (context->ne[0] == config.hidden_size) { return context; } GGML_ASSERT(context->ne[0] == config.text_dim); auto condition_proj = std::dynamic_pointer_cast(blocks["condition_proj"]); auto token_refiner = std::dynamic_pointer_cast(blocks["token_refiner"]); return token_refiner->forward(ctx, condition_proj->forward(ctx, context)); } ggml_tensor* time_embedding(GGMLRunnerContext* ctx, ggml_tensor* timestep_features, ggml_tensor* curve_indices, ggml_tensor* curve_upper_indices, ggml_tensor* curve_fractions) { if (!config.uses_adaln_curves()) { return std::dynamic_pointer_cast(blocks["time_embedder"])->forward(ctx, timestep_features); } auto lower = ggml_get_rows(ctx->ggml_ctx, params["adaln_t_table"], curve_indices); auto upper = ggml_get_rows(ctx->ggml_ctx, params["adaln_t_table"], curve_upper_indices); return ggml_add(ctx->ggml_ctx, lower, ggml_mul(ctx->ggml_ctx, ggml_sub(ctx->ggml_ctx, upper, lower), curve_fractions)); } ggml_tensor* build_rope(GGMLRunnerContext* ctx, ggml_tensor* position_ids) { auto inv = ggml_reshape_2d(ctx->ggml_ctx, params["rope.inv_freq"], config.rope_inv_freq_len, 1); ggml_tensor* angles = nullptr; for (int axis = 0; axis < 3; ++axis) { auto pos = ggml_ext_slice(ctx->ggml_ctx, position_ids, 0, axis, axis + 1); auto expanded_inv = ggml_repeat_4d(ctx->ggml_ctx, inv, inv->ne[0], pos->ne[1], 1, 1); auto a = ggml_mul(ctx->ggml_ctx, expanded_inv, pos); angles = angles == nullptr ? a : ggml_concat(ctx->ggml_ctx, angles, a, 0); } auto c = ggml_reshape_4d(ctx->ggml_ctx, ggml_cos(ctx->ggml_ctx, angles), 1, angles->ne[0], angles->ne[1], 1); auto s = ggml_reshape_4d(ctx->ggml_ctx, ggml_sin(ctx->ggml_ctx, angles), 1, angles->ne[0], angles->ne[1], 1); auto ns = ggml_neg(ctx->ggml_ctx, s); auto pe = ggml_concat(ctx->ggml_ctx, c, ns, 0); pe = ggml_concat(ctx->ggml_ctx, pe, s, 0); pe = ggml_concat(ctx->ggml_ctx, pe, c, 0); return ggml_reshape_4d(ctx->ggml_ctx, pe, 2, 2, angles->ne[0], angles->ne[1]); } std::pair forward(GGMLRunnerContext* ctx, ggml_tensor* video, ggml_tensor* audio, ggml_tensor* context, const std::vector& condition_videos, const std::vector& condition_audios, ggml_tensor* position_ids, ggml_tensor* timestep_features, ggml_tensor* curve_indices, ggml_tensor* curve_upper_indices, ggml_tensor* curve_fractions, const std::vector& segments, const std::vector& sequence_segments, const TokenModulationSpan& video_segment, const TokenModulationSpan& audio_segment) { auto video_proj = std::dynamic_pointer_cast(blocks["video_patch_proj"]); auto audio_proj = std::dynamic_pointer_cast(blocks["audio_patch_proj"]); std::vector> condition_video_ranges; ggml_tensor* video_rows = nullptr; int64_t video_offset = 0; for (auto condition : condition_videos) { auto rows = DiT::patchify_3d(ctx->ggml_ctx, condition, config.patch_t, config.patch_h, config.patch_w, 1, true); condition_video_ranges.push_back({video_offset, video_offset + rows->ne[1]}); video_offset += rows->ne[1]; video_rows = video_rows == nullptr ? rows : ggml_concat(ctx->ggml_ctx, video_rows, rows, 1); } auto target_video_rows = DiT::patchify_3d(ctx->ggml_ctx, video, config.patch_t, config.patch_h, config.patch_w, 1, true); std::pair target_video_range = {video_offset, video_offset + target_video_rows->ne[1]}; video_rows = video_rows == nullptr ? target_video_rows : ggml_concat(ctx->ggml_ctx, video_rows, target_video_rows, 1); auto video_embeds = video_proj->forward(ctx, video_rows); auto pack_audio_rows = [&](ggml_tensor* value) { value = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, value, 2, 0, 1, 3)); return ggml_reshape_3d(ctx->ggml_ctx, value, value->ne[0], value->ne[1] * value->ne[2], value->ne[3]); }; std::vector> condition_audio_ranges; ggml_tensor* audio_rows = nullptr; int64_t audio_offset = 0; for (auto condition : condition_audios) { auto rows = pack_audio_rows(condition); condition_audio_ranges.push_back({audio_offset, audio_offset + rows->ne[1]}); audio_offset += rows->ne[1]; audio_rows = audio_rows == nullptr ? rows : ggml_concat(ctx->ggml_ctx, audio_rows, rows, 1); } audio = pack_audio_rows(audio); std::pair target_audio_range = {audio_offset, audio_offset + audio->ne[1]}; audio_rows = audio_rows == nullptr ? audio : ggml_concat(ctx->ggml_ctx, audio_rows, audio, 1); auto audio_embeds = audio_proj->forward(ctx, audio_rows); context = refine_context(ctx, context); ggml_tensor* h = nullptr; auto append = [&](ggml_tensor* value) { h = h == nullptr ? value : ggml_concat(ctx->ggml_ctx, h, value, 1); }; for (const auto& sequence : sequence_segments) { if (sequence.kind == SequenceKind::TEXT) { append(context); } else if (sequence.kind == SequenceKind::CONDITION_VIDEO) { GGML_ASSERT(sequence.source_index >= 0 && sequence.source_index < static_cast(condition_video_ranges.size())); auto range = condition_video_ranges[static_cast(sequence.source_index)]; append(ggml_ext_slice(ctx->ggml_ctx, video_embeds, 1, range.first, range.second)); } else if (sequence.kind == SequenceKind::CONDITION_AUDIO) { GGML_ASSERT(sequence.source_index >= 0 && sequence.source_index < static_cast(condition_audio_ranges.size())); auto range = condition_audio_ranges[static_cast(sequence.source_index)]; append(ggml_ext_slice(ctx->ggml_ctx, audio_embeds, 1, range.first, range.second)); } else if (sequence.kind == SequenceKind::TARGET_AUDIO) { append(ggml_ext_slice(ctx->ggml_ctx, audio_embeds, 1, target_audio_range.first, target_audio_range.second)); } else { append(ggml_ext_slice(ctx->ggml_ctx, video_embeds, 1, target_video_range.first, target_video_range.second)); } } GGML_ASSERT(h != nullptr); auto t_emb = time_embedding(ctx, timestep_features, curve_indices, curve_upper_indices, curve_fractions); auto pe = build_rope(ctx, position_ids); for (int64_t i = 0; i < config.num_layers; ++i) { auto block = std::dynamic_pointer_cast(blocks["blocks." + std::to_string(i)]); h = block->forward(ctx, h, t_emb, segments, pe); sd::ggml_graph_cut::mark_graph_cut(h, "minimax_h3.blocks." + std::to_string(i), "hidden_states"); } auto final_layer = std::dynamic_pointer_cast(blocks["final_layer"]); auto output = final_layer->forward(ctx, h, t_emb, video_segment, audio_segment); auto video_out = DiT::unpatchify_3d(ctx->ggml_ctx, output.first, video->ne[2] / config.patch_t, video->ne[1] / config.patch_h, video->ne[0] / config.patch_w, config.patch_t, config.patch_h, config.patch_w, true); auto audio_out = ggml_reshape_4d(ctx->ggml_ctx, output.second, config.audio_latent_channels, audio->ne[1] / 2, 2, audio->ne[2]); audio_out = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, audio_out, 1, 2, 0, 3)); video_out = ggml_ext_scale(ctx->ggml_ctx, video_out, -1.f); audio_out = ggml_ext_scale(ctx->ggml_ctx, audio_out, -1.f); return {video_out, audio_out}; } }; struct PackedSequenceLayout { std::vector positions; std::vector segments; std::vector sequence_segments; TokenModulationSpan video_segment{}; TokenModulationSpan audio_segment{}; std::vector timesteps; }; static float video_span(int64_t frame) { static const int spans[5] = {1, 4, 4, 4, 4}; return FRAME_RESCALE * spans[frame % 5]; } static std::vector spatial_axis(int64_t dim, float sqrt_area) { int64_t count = dim / 2; float ratio = static_cast(dim) / sqrt_area; std::vector result(static_cast(count)); for (int64_t i = 0; i < count; ++i) { result[static_cast(i)] = (static_cast(i) * (ratio / count) + (1.f - ratio) * 0.5f) * 32.f; } return result; } static int find_or_add_timestep(std::vector* values, float value) { auto it = std::find(values->begin(), values->end(), value); if (it != values->end()) { return static_cast(it - values->begin()); } values->push_back(value); return static_cast(values->size() - 1); } static PackedSequenceLayout build_layout(int64_t text_len, int64_t latent_t, int64_t latent_h, int64_t latent_w, int64_t audio_t, const std::vector>& condition_videos, const std::vector>& condition_audios, const sd::Tensor& keyframe_indices, const std::vector& reference_blocks, const sd::Tensor& text_tags, float video_t, float audio_timestep) { PackedSequenceLayout layout; float sqrt_area = std::sqrt(static_cast(latent_h * latent_w)); auto h_axis = spatial_axis(latent_h, sqrt_area); auto w_axis = spatial_axis(latent_w, sqrt_area); int64_t frame_rows = static_cast(h_axis.size() * w_axis.size()); int64_t row = 0; auto append_position = [&](float t, float h, float w) { layout.positions.push_back(t); layout.positions.push_back(h); layout.positions.push_back(w); }; for (int64_t i = 0; i < text_len; ++i) { append_position(static_cast(i), 0.f, 0.f); } layout.sequence_segments.push_back({0, text_len, SequenceKind::TEXT}); int video_time_row = find_or_add_timestep(&layout.timesteps, video_t); int audio_time_row = find_or_add_timestep(&layout.timesteps, audio_timestep); int condition_time_row = find_or_add_timestep(&layout.timesteps, std::max(video_t, VISUAL_COND_TIMESTEP)); int audio_condition_time_row = find_or_add_timestep(&layout.timesteps, std::max(audio_timestep, 1.f)); int64_t run_start = 0; int current_tag = text_tags.empty() ? 1 : text_tags[0]; for (int64_t i = 1; i <= text_len; ++i) { int tag = i < text_len && !text_tags.empty() ? text_tags[i] : -1; if (i == text_len || tag != current_tag) { layout.segments.push_back({run_start, i, video_time_row * 3 + current_tag}); run_start = i; current_tag = tag; } } row = text_len; auto condition_spatial_axes = [&](const sd::Tensor& condition) { float area = std::sqrt(static_cast(condition.shape()[0] * condition.shape()[1])); return std::make_pair(spatial_axis(condition.shape()[1], area), spatial_axis(condition.shape()[0], area)); }; auto append_video_positions = [&](const sd::Tensor& condition, float cursor) { auto axes = condition_spatial_axes(condition); for (int64_t t = 0; t < condition.shape()[2]; ++t) { for (float h : axes.first) { for (float w : axes.second) { append_position(cursor, h, w); } } cursor += video_span(t); } return cursor; }; auto append_audio_positions = [&](int64_t length, float cursor, float w_low, float w_high) { for (int channel = 0; channel < 2; ++channel) { float w = channel == 0 ? w_low : w_high; for (int64_t t = 0; t < length; ++t) { append_position(cursor + static_cast(t), 0.f, w); } } }; float cursor = static_cast(text_len); if (reference_blocks.empty()) { float video_duration = 0.f; for (int64_t t = 0; t < latent_t; ++t) { video_duration += video_span(t); } for (size_t index = 0; index < condition_videos.size(); ++index) { const auto& condition = condition_videos[index]; auto axes = condition_spatial_axes(condition); int64_t count = condition.shape()[2] * static_cast(axes.first.size() * axes.second.size()); bool is_first = keyframe_indices.empty() || keyframe_indices[static_cast(index)] == 0; float keyframe_t = is_first ? static_cast(text_len) : static_cast(text_len) + video_duration - FRAME_RESCALE; for (int64_t t = 0; t < condition.shape()[2]; ++t) { for (float h : axes.first) { for (float w : axes.second) { append_position(keyframe_t, h, w); } } } layout.sequence_segments.push_back({row, row + count, SequenceKind::CONDITION_VIDEO, static_cast(index)}); layout.segments.push_back({row, row + count, condition_time_row * 3}); row += count; } } else { for (const auto& block : reference_blocks) { const sd::Tensor* ref_video = nullptr; const sd::Tensor* ref_audio = nullptr; if (block.video_index >= 0) { GGML_ASSERT(block.video_index < static_cast(condition_videos.size())); ref_video = &condition_videos[static_cast(block.video_index)]; } if (block.audio_index >= 0) { GGML_ASSERT(block.audio_index < static_cast(condition_audios.size())); ref_audio = &condition_audios[static_cast(block.audio_index)]; } float block_end = cursor; if (block.kind == MiniMaxH3ReferenceKind::AUDIO || block.kind == MiniMaxH3ReferenceKind::VIDEO_AUDIO) { GGML_ASSERT(ref_audio != nullptr); float w_low = w_axis.front(); float w_high = w_axis.back(); if (ref_video != nullptr) { auto axes = condition_spatial_axes(*ref_video); w_low = axes.second.front(); w_high = axes.second.back(); } int64_t count = ref_audio->shape()[0] * 2; append_audio_positions(ref_audio->shape()[0], cursor, w_low, w_high); layout.sequence_segments.push_back({row, row + count, SequenceKind::CONDITION_AUDIO, block.audio_index}); layout.segments.push_back({row, row + count, audio_condition_time_row * 3 + 2}); row += count; block_end = std::max(block_end, cursor + static_cast(ref_audio->shape()[0])); } if (block.kind != MiniMaxH3ReferenceKind::AUDIO) { GGML_ASSERT(ref_video != nullptr); auto axes = condition_spatial_axes(*ref_video); int64_t count = ref_video->shape()[2] * static_cast(axes.first.size() * axes.second.size()); float video_end = append_video_positions(*ref_video, cursor); layout.sequence_segments.push_back({row, row + count, SequenceKind::CONDITION_VIDEO, block.video_index}); layout.segments.push_back({row, row + count, condition_time_row * 3}); row += count; block_end = block.kind == MiniMaxH3ReferenceKind::IMAGE ? std::max(block_end, cursor + 1.f) : std::max(block_end, video_end); } cursor = block_end; } } int64_t audio_start = row; append_audio_positions(audio_t, cursor, w_axis.front(), w_axis.back()); layout.audio_segment = {audio_start, row + audio_t * 2, audio_time_row}; layout.sequence_segments.push_back({audio_start, row + audio_t * 2, SequenceKind::TARGET_AUDIO}); layout.segments.push_back({audio_start, row + audio_t * 2, audio_time_row * 3 + 2}); row += audio_t * 2; int64_t video_start = row; for (int64_t t = 0; t < latent_t; ++t) { for (float h : h_axis) { for (float w : w_axis) { append_position(cursor, h, w); } } cursor += video_span(t); } int64_t video_rows = latent_t * frame_rows; layout.video_segment = {video_start, video_start + video_rows, video_time_row}; layout.sequence_segments.push_back({video_start, video_start + video_rows, SequenceKind::TARGET_VIDEO}); layout.segments.push_back({video_start, video_start + video_rows, video_time_row * 3}); return layout; } struct MiniMaxH3Runner : public DiffusionModelRunner { Config config; MiniMaxH3Transformer3DModel model; sd::Tensor video_input_cache; sd::Tensor audio_input_cache; sd::Tensor position_input_cache; sd::Tensor timestep_feature_input_cache; sd::Tensor curve_index_input_cache; sd::Tensor curve_upper_index_input_cache; sd::Tensor curve_fraction_input_cache; MiniMaxH3Runner(ggml_backend_t backend, const String2TensorStorage& tensors, const std::string& prefix = "model.diffusion_model", std::shared_ptr weight_manager = nullptr) : DiffusionModelRunner(backend, prefix, weight_manager), config(Config::detect_from_weights(tensors, prefix)), model(config) { model.init(params_ctx, tensors, prefix); } std::string get_desc() override { return "minimax_h3"; } void get_param_tensors(std::map& tensors, const std::string& prefix) override { model.get_param_tensors(tensors, prefix); } std::pair, sd::Tensor> split_av_latents(const sd::Tensor& packed, int audio_length) const { GGML_ASSERT(packed.dim() == 4 || packed.dim() == 5); int64_t spatial = packed.shape()[0] * packed.shape()[1] * packed.shape()[2]; int64_t video_values = spatial * config.video_latent_channels; sd::Tensor video({packed.shape()[0], packed.shape()[1], packed.shape()[2], config.video_latent_channels, 1}); std::copy_n(packed.data(), static_cast(video_values), video.data()); if (audio_length <= 0) { return {video, {}}; } int64_t audio_values = audio_length * 2 * config.audio_latent_channels; GGML_ASSERT(packed.numel() >= video_values + audio_values); sd::Tensor audio({audio_length, 2, config.audio_latent_channels, 1}); std::copy_n(packed.data() + video_values, static_cast(audio_values), audio.data()); return {video, audio}; } ggml_tensor* merge_av_latents(ggml_context* ctx, ggml_tensor* video, ggml_tensor* audio) const { int64_t divisor = video->ne[0] * video->ne[1] * video->ne[2]; int64_t values = ggml_nelements(audio); int64_t padding = (divisor - values % divisor) % divisor; audio = ggml_reshape_4d(ctx, ggml_cont(ctx, audio), values, 1, 1, 1); if (padding > 0) { audio = ggml_ext_pad(ctx, audio, static_cast(padding), 0, 0, 0); } audio = ggml_reshape_4d(ctx, audio, video->ne[0], video->ne[1], video->ne[2], (values + padding) / divisor); return ggml_concat(ctx, video, audio, 3); } ggml_cgraph* build_graph(const sd::Tensor& packed, const sd::Tensor& timestep, const sd::Tensor& context_tensor, const std::vector>& condition_videos, const std::vector>& condition_audios, const sd::Tensor& text_tags, const sd::Tensor& keyframe_indices, const std::vector& reference_blocks, int audio_length, float video_shift, float audio_shift) { auto split = split_av_latents(packed, audio_length); video_input_cache = std::move(split.first); audio_input_cache = std::move(split.second); GGML_ASSERT(!audio_input_cache.empty()); GGML_ASSERT(!context_tensor.empty()); auto video = make_input(video_input_cache); auto audio_carrier = make_input(audio_input_cache); auto context = make_input(context_tensor); std::vector condition_inputs; condition_inputs.reserve(condition_videos.size()); for (const auto& condition : condition_videos) { condition_inputs.push_back(make_input(condition)); } std::vector audio_condition_inputs; audio_condition_inputs.reserve(condition_audios.size()); for (const auto& condition : condition_audios) { audio_condition_inputs.push_back(make_input(condition)); } float sigma_v = std::clamp(timestep[0] / 1000.f, 1e-6f, 1.f); float sigma_a = time_shift_sigma(sigma_v, video_shift, audio_shift); float audio_scale = video_shift / audio_shift; float t_v = 1.f - sigma_v; float t_a = 1.f - sigma_a; // The sampler carries c_a = (sigma_v / sigma_a) * x_a so the packed // latent follows one sigma schedule. Restore x_a for the H3 network. auto audio = ggml_ext_scale(compute_ctx, audio_carrier, sigma_a / sigma_v); auto layout = build_layout(context_tensor.shape()[1], video_input_cache.shape()[2], video_input_cache.shape()[1], video_input_cache.shape()[0], audio_length, condition_videos, condition_audios, keyframe_indices, reference_blocks, text_tags, t_v, t_a); position_input_cache = sd::Tensor( {3, static_cast(layout.positions.size() / 3)}, layout.positions); auto positions = make_input(position_input_cache); ggml_tensor* timestep_features = nullptr; ggml_tensor* curve_indices = nullptr; ggml_tensor* curve_upper_indices = nullptr; ggml_tensor* curve_fractions = nullptr; if (config.uses_adaln_curves()) { std::vector indices(layout.timesteps.size()); std::vector upper_indices(layout.timesteps.size()); std::vector fractions(layout.timesteps.size()); for (size_t i = 0; i < layout.timesteps.size(); ++i) { float position = std::clamp(layout.timesteps[i], 0.f, 1.f) * (config.adaln_curve_grid - 1); int index = std::min(static_cast(std::floor(position)), static_cast(config.adaln_curve_grid - 2)); indices[i] = index; upper_indices[i] = index + 1; fractions[i] = position - index; } curve_index_input_cache = sd::Tensor( {static_cast(indices.size())}, indices); curve_upper_index_input_cache = sd::Tensor( {static_cast(upper_indices.size())}, upper_indices); curve_fraction_input_cache = sd::Tensor( {1, static_cast(fractions.size())}, fractions); curve_indices = make_input(curve_index_input_cache); curve_upper_indices = make_input(curve_upper_index_input_cache); curve_fractions = make_input(curve_fraction_input_cache); } else { timestep_feature_input_cache = sd::Tensor( {config.timestep_input_dim, static_cast(layout.timesteps.size())}, timestep_embedding(layout.timesteps, static_cast(config.timestep_input_dim), 10000, true, 1.f)); timestep_features = make_input(timestep_feature_input_cache); } auto runner_ctx = get_context(); auto output = model.forward(&runner_ctx, video, audio, context, condition_inputs, audio_condition_inputs, positions, timestep_features, curve_indices, curve_upper_indices, curve_fractions, layout.segments, layout.sequence_segments, layout.video_segment, layout.audio_segment); // Convert the model's audio velocity to d(c_a) / d(sigma_v). output.second = ggml_add(compute_ctx, ggml_ext_scale(compute_ctx, audio, 1.f - audio_scale), ggml_ext_scale(compute_ctx, output.second, 1.f + (audio_scale - 1.f) * sigma_a)); auto merged = merge_av_latents(compute_ctx, output.first, output.second); auto graph = new_graph_custom(H3_GRAPH_SIZE); ggml_build_forward_expand(graph, merged); return graph; } sd::Tensor compute(int n_threads, const DiffusionParams& params) override { GGML_ASSERT(params.x != nullptr && params.timesteps != nullptr && params.context != nullptr); const auto* extra = diffusion_extra_as(params); static const std::vector> empty_conditions; static const std::vector empty_reference_blocks; const auto& conditions = params.ref_latents == nullptr ? empty_conditions : *params.ref_latents; const auto& audio_conditions = extra->reference_audio_latents == nullptr ? empty_conditions : *extra->reference_audio_latents; const auto& reference_blocks = extra->reference_blocks == nullptr ? empty_reference_blocks : *extra->reference_blocks; const sd::Tensor empty_int; auto get_graph = [&]() { return build_graph(*params.x, *params.timesteps, *params.context, conditions, audio_conditions, extra->text_token_tags == nullptr ? empty_int : *extra->text_token_tags, extra->keyframe_indices == nullptr ? empty_int : *extra->keyframe_indices, reference_blocks, extra->audio_length, extra->video_sigma_shift, extra->audio_sigma_shift); }; return restore_trailing_singleton_dims(GGMLRunner::compute(get_graph, n_threads, false), params.x->dim()); } }; } // namespace MiniMaxH3 #endif // __SD_MODEL_DIFFUSION_MINIMAX_H3_HPP__