Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,8 @@ LM 不隐式构建依赖栈,也不安装 Core 的独立 Python runtime。
```bash
python examples/bench.py --device nvidia --model=<model-path> --enable-paged-attn [--attn=default | --attn=flash-attn]
```

### Clover on Hygon

Native Clover block diffusion and the optional frontier/expert-reuse decoder are
documented in [the Hygon guide](docs/clover-hygon.md).
2 changes: 2 additions & 0 deletions csrc/engine/infer_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ InferEngine::Input::to_model_input(
: to_device(target_hidden_states),
sample_all_positions};
input.logical_kv_upper_bound = logical_kv_upper_bound;
input.diffusion_preview = diffusion_preview;
input.diffusion_expert_budget = diffusion_expert_budget;

auto &attn_metadata = forward_context.attn_metadata;
attn_metadata = {
Expand Down
2 changes: 2 additions & 0 deletions csrc/engine/rank_worker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class RankWorker {
std::optional<std::vector<std::tuple<float, float, int>>> sampling_rows;

std::optional<nsa::Snapshot> nsa_owner_snapshot;
bool diffusion_preview{false};
bool diffusion_expert_budget{false};

infinilm::InfinilmModel::Input to_model_input(
infinilm::runtime::Device device,
Expand Down
6 changes: 6 additions & 0 deletions csrc/global_state/forward_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct StaticPrefillHostDescription {
};

struct AttentionMetadata {
bool diffusion_preview{false};
/// Past Lengths of cached sequence for each request, of shape `[num_requests]`.
std::optional<infinilm::runtime::Tensor> past_sequence_lengths;
/// ToTal Lengths for each request sequence, of shape `[num_requests]`.
Expand Down Expand Up @@ -119,6 +120,11 @@ struct ForwardContext {
AttentionMetadata attn_metadata;
MambaMetadata mamba_metadata;
MultiModalMetadata mm_metadata;
bool diffusion_preview{false};
size_t diffusion_block_size{32};
bool diffusion_expert_budget{false};
infinilm::runtime::Tensor diffusion_valid;
infinilm::runtime::Tensor diffusion_live;
std::vector<infinilm::runtime::Tensor> kv_cache_vec;
std::vector<infinilm::runtime::Tensor> conv_state_vec;
std::vector<infinilm::runtime::Tensor> ssm_state_vec;
Expand Down
3 changes: 2 additions & 1 deletion csrc/layers/attention/backends/static_attn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ infinilm::runtime::Tensor StaticAttentionImpl::forward(const AttentionLayer &lay
const bool finite_window = window_size[0] >= 0;
bool varlen_candidate = finite_window;
varlen_candidate = varlen_candidate || (prefill && seq_len > 1 && kv_quant_scheme_ == infinilm::quantization::KVQuantAlgo::NONE && (q_rope->dtype() == infinilm::runtime::DataType::kFloat16 || q_rope->dtype() == infinilm::runtime::DataType::kBFloat16));
varlen_candidate = varlen_candidate && !attn_metadata.diffusion_preview;
if (finite_window && seq_len > 1 && !prefill) {
throw std::invalid_argument("Static finite window requires validated prefill row/past/total metadata");
}
Expand Down Expand Up @@ -269,7 +270,7 @@ infinilm::runtime::Tensor StaticAttentionImpl::forward(const AttentionLayer &lay
throw std::invalid_argument("causal_softmax has unsupported shape or dtype");
}
std::optional<infinilm::runtime::Tensor> causal_mask;
if (seq_len != 1) {
if (seq_len != 1 && !attn_metadata.diffusion_preview) {
causal_mask = infinilm::runtime::Tensor::empty(
runtime, {seq_len, total_seq_len}, attn_weight_softmax->dtype());
}
Expand Down
83 changes: 83 additions & 0 deletions csrc/models/clover_moe/clover_decoder_layer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include "clover_decoder_layer.hpp"

#include "runtime/runtime.hpp"
#include <exception>
#include <stdexcept>
#include <string>
#include <vector>

namespace infinilm::models::clover_moe {

CloverDecoderLayer::CloverDecoderLayer(infinilm::runtime::Runtime &runtime,
std::shared_ptr<infinilm::config::ModelConfig> model_config,
size_t layer_idx,
const engine::distributed::RankInfo &rank_info,
backends::AttentionBackend attention_backend,
const layers::moe::EPConfig &ep_config)
: layer_idx_(layer_idx) {
runtime.assertOwnerThread();
if (runtime.isGraphRecording()) {
throw std::logic_error("Clover decoder construction requires eager Runtime");
}
if (!model_config || rank_info.device != runtime.device() || rank_info.tp_size <= 0
|| rank_info.tp_rank < 0 || rank_info.tp_rank >= rank_info.tp_size
|| (rank_info.tp_size > 1 && !rank_info.comm)) {
throw std::invalid_argument("Invalid explicit Qwen35 decoder construction resources");
}
const auto layer_types = model_config->get<std::vector<std::string>>("layer_types");
layer_type_ = layer_types.at(layer_idx);
if (layer_type_ != "linear_attention" && layer_type_ != "full_attention") {
throw std::invalid_argument("Clover decoder has unsupported layer_type");
}
try {
const auto &dtype = model_config->get_dtype();
const size_t hidden_size = model_config->get<size_t>("hidden_size");
const double rms_norm_eps = model_config->get<double>("rms_norm_eps");

input_layernorm_ = this->bind_child("input_layernorm", std::make_shared<std::remove_reference_t<decltype(*input_layernorm_)>>(runtime, hidden_size, rms_norm_eps, dtype));
post_attention_layernorm_ = this->bind_child("post_attention_layernorm", std::make_shared<std::remove_reference_t<decltype(*post_attention_layernorm_)>>(runtime, hidden_size, rms_norm_eps, dtype));
mlp_ = this->bind_child("mlp", std::make_shared<std::remove_reference_t<decltype(*mlp_)>>(runtime, model_config, layer_idx, rank_info, attention_backend, ep_config));

if ("linear_attention" == layer_type_) {
linear_attn_ = this->bind_child("linear_attn", std::make_shared<std::remove_reference_t<decltype(*linear_attn_)>>(runtime, model_config, layer_idx, rank_info, attention_backend, ep_config));
} else if ("full_attention" == layer_type_) {
self_attn_ = this->bind_child("self_attn", std::make_shared<std::remove_reference_t<decltype(*self_attn_)>>(runtime, model_config, layer_idx, rank_info, attention_backend));
} else {
throw std::runtime_error(
"CloverDecoderLayer: unsupported layer_type '" + layer_type_
+ "' for layer " + std::to_string(layer_idx));
}
} catch (...) {
try {
runtime.syncStream();
} catch (...) { std::terminate(); }
throw;
}
}

std::tuple<infinilm::runtime::Tensor, infinilm::runtime::Tensor> CloverDecoderLayer::forward(
const infinilm::runtime::Tensor &positions,
infinilm::runtime::Tensor &hidden_states,
infinilm::runtime::Tensor &residual,
infinilm::global_state::ForwardContext &forward_context) {
auto &runtime = forward_context.require_runtime();
runtime.assertOwnerThread();
if (!hidden_states || hidden_states->device() != runtime.device()
|| (residual && residual->device() != runtime.device())
|| input_layernorm_->weight()->device() != runtime.device()
|| post_attention_layernorm_->weight()->device() != runtime.device()) {
throw std::invalid_argument("CloverDecoderLayer: forward Runtime/input/norm device mismatch");
}
input_layernorm_->forward_inplace(runtime, hidden_states, residual);
if ("linear_attention" == layer_type_) {
hidden_states = linear_attn_->forward(hidden_states, forward_context);
} else {
hidden_states = self_attn_->forward(positions, hidden_states, forward_context);
}

post_attention_layernorm_->forward_inplace(runtime, hidden_states, residual);
hidden_states = mlp_->forward(runtime, hidden_states, forward_context);
return std::make_tuple(hidden_states, residual);
}

} // namespace infinilm::models::clover_moe
43 changes: 43 additions & 0 deletions csrc/models/clover_moe/clover_decoder_layer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once
#include "../../layers/normalization/rmsnorm.hpp"
#include "../../weights/weights.hpp"

#include "../qwen3_5/qwen3_5_attention.hpp"
#include "clover_gdn.hpp"
#include "clover_moe.hpp"

#include <string>
#include <tuple>

namespace infinilm::models::clover_moe {

class CloverDecoderLayer : public infinilm::weights::ModelWeights {
public:
CloverDecoderLayer(infinilm::runtime::Runtime &runtime,
std::shared_ptr<infinilm::config::ModelConfig> model_config,
size_t layer_idx,
const engine::distributed::RankInfo &rank_info,
backends::AttentionBackend attention_backend,
const layers::moe::EPConfig &ep_config);

std::tuple<infinilm::runtime::Tensor, infinilm::runtime::Tensor> forward(
const infinilm::runtime::Tensor &positions,
infinilm::runtime::Tensor &hidden_states,
infinilm::runtime::Tensor &residual,
infinilm::global_state::ForwardContext &forward_context);

size_t layer_idx() const { return layer_idx_; }

protected:
std::shared_ptr<infinilm::layers::RMSNorm> input_layernorm_;
std::shared_ptr<infinilm::layers::RMSNorm> post_attention_layernorm_;
std::shared_ptr<qwen3_5::Qwen35Attention> self_attn_;
std::shared_ptr<CloverGatedDeltaNet> linear_attn_;
std::shared_ptr<CloverSparseMoe> mlp_;

private:
size_t layer_idx_;
std::string layer_type_;
};

} // namespace infinilm::models::clover_moe
105 changes: 105 additions & 0 deletions csrc/models/clover_moe/clover_for_causal_lm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "../../layers/causal_lm_templates/text_causal_lm.hpp"
#include "../models_registry.hpp"
#include "../qwen3_5/qwen3_5_for_causal_lm.hpp"
#include "../qwen3_next/qwen3_next_allocate_kv_cache_tensors.hpp"
#include "clover_decoder_layer.hpp"
#include <base/clover_live_mask.h>

namespace infinilm::models::clover_moe {

using CloverModel = layers::causal_lm_templates::TextModel<CloverDecoderLayer>;

class CloverForCausalLM : public layers::causal_lm_templates::TextCausalLM<CloverModel> {
public:
using Base = layers::causal_lm_templates::TextCausalLM<CloverModel>;
using Base::Base;

Output forward(const Input &input, global_state::ForwardContext &context) const override {
auto &runtime = context.require_runtime();
if (runtime.isGraphRecording()) {
throw std::invalid_argument("Clover requires eager execution.");
}
context.diffusion_preview = input.diffusion_preview;
context.diffusion_expert_budget = input.diffusion_expert_budget;
context.attn_metadata.diffusion_preview = input.diffusion_preview;
context.diffusion_block_size = model_config_->get<size_t>("block_size");
if (!input.diffusion_preview) {
denoise_step_ = 0;
}
if (input.diffusion_preview) {
const auto tokens = input.input_ids.value()->numel();
if (!input.sample_all_positions || tokens % context.diffusion_block_size) {
throw std::invalid_argument("Clover preview requires complete blocks and all-position logits.");
}
context.diffusion_valid = runtime::Tensor::empty(runtime, {tokens}, runtime::DataType::kInt8);
execution::submit<infini::ops::Fill>(runtime, infini::ops::Fill::DefaultConfig(runtime.device().type()),
context.diffusion_valid, 1.0, context.diffusion_valid);
if (input.diffusion_expert_budget) {
bool refresh = denoise_step_ % 5 == 0 || !previous_budget_;
const auto &ids = input.input_ids.value();
if (!previous_ids_ || previous_ids_->shape() != ids->shape()) {
previous_ids_ = runtime::Tensor::empty(runtime, ids->shape(), ids->dtype());
refresh = true;
}
context.diffusion_live = runtime::Tensor::empty(runtime, {tokens}, runtime::DataType::kInt8);
execution::submit<infini::ops::CloverLiveMask>(runtime, execution::RecordingPolicy::HostOnly,
infini::ops::CloverLiveMask::DefaultConfig(runtime.device().type()), ids, previous_ids_,
static_cast<int64_t>(model_config_->get<size_t>("mask_token_id")), refresh, context.diffusion_live);
}
previous_budget_ = input.diffusion_expert_budget;
++denoise_step_;
}
return Base::forward(input, context);
}

void reset_cache(const cache::CacheConfig *cache_config, global_state::ForwardContext &context,
backends::AttentionBackend backend, const engine::distributed::RankInfo &rank) override {
if (backend != backends::AttentionBackend::STATIC_ATTN) {
throw std::invalid_argument("Clover currently requires the static attention backend.");
}
auto &runtime = context.require_runtime();
denoise_step_ = 0;
previous_budget_ = false;
previous_ids_.reset();
auto allocated = qwen3_next::qwen3_next_allocate_cache_tensors(runtime, rank, cache_config, model_config_, backend);
for (auto &state : allocated.ssm_state_tensors) {
if (!state) {
continue;
}
state = runtime::Tensor::empty(runtime, state->shape(), runtime::DataType::kFloat32);
execution::submit<infini::ops::Fill>(runtime, infini::ops::Fill::DefaultConfig(runtime.device().type()), state, 0.0, state);
}
context.kv_cache_vec = std::move(allocated.kv_cache_tensors);
context.conv_state_vec = std::move(allocated.conv_state_tensors);
context.ssm_state_vec = std::move(allocated.ssm_state_tensors);
cache_config_ = cache_config ? cache_config->unique_copy() : nullptr;
}

private:
mutable size_t denoise_step_{0};
mutable bool previous_budget_{false};
mutable runtime::Tensor previous_ids_;
};

std::shared_ptr<config::ModelConfig> create_clover_config(std::shared_ptr<config::ModelConfig> config) {
if (config->get_dtype() != runtime::DataType::kBFloat16) {
throw std::invalid_argument("Clover requires BF16 model weights.");
}
auto &json = config->get_config_json();
if (json.at("model_type") != "clover_moe" || json.at("block_size") != 32 || json.at("linear_key_head_dim") != 128 || json.at("linear_value_head_dim") != 128 || json.at("linear_conv_kernel_dim") != 4 || json.at("num_experts") != 256 || json.at("num_experts_per_tok") != 8) {
throw std::invalid_argument("Unsupported Clover architecture.");
}
json["norm_topk_prob"] = true;
json["position_id_axes"] = 1;
json["rope_parameters"]["mrope_section"] = {16, 8, 8};
json["rope_parameters"]["mrope_interleaved"] = false;
return qwen3_5::prepare_qwen3_5_model_config(config);
}

} // namespace infinilm::models::clover_moe

namespace {
INFINILM_REGISTER_EXPLICIT_CAUSAL_LM_MODEL(clover_moe,
infinilm::models::clover_moe::CloverForCausalLM,
infinilm::models::clover_moe::create_clover_config);
}
Loading