# pinyinchch

一个拼音转汉字的工具库。

## 介绍

这是一个用 Rust 重写的 [Pinyin2Hanzi](https://github.com/letiantian/Pinyin2Hanzi) 库，或者说是将 [Pinyin2Hanzi](https://github.com/letiantian/Pinyin2Hanzi) 库从 Python 移植到 Rust，支持两种模型：

- **HMM (隐马尔可夫模型)**：使用 Viterbi 算法进行拼音转汉字
- **DAG (有向无环图)**：使用动态规划算法进行拼音转汉字

> [!NOTE]
>
> 本项目只实现了 Pinyin2Hanzi 中模型推理的部分，即实现拼音到汉字的转换，并未实现使用 Rust 训练 HMM 和 DAG 模型。
>
> 本项目中将 [Pinyin2Hanzi](https://github.com/letiantian/Pinyin2Hanzi) 提供的 HMM 和 DAG 模型封装成不同的 crate，按需引入。也可以通过实现 `pingyinchch-type` 中定义的 `trait Hmm` 和 `trait Dag`，来导入自行训练的 HMM 和 DAG 模型

## 安装

- 如果使用 hmm 的 Viterbi 算法进行拼音到汉字的转换，那么需要启用 hmm 功能。

  ```shell
  cargo add pinyinchch -F hmm
  ```

  如果使用默认提供的 hmm 模型，则需要引入 `pinyinchch-model-hmm`

  ```shell
  cargo add pinyinchch-model-hmm
  ```

  如果想自己实现一个拼音到汉字的 hmm 模型，那么需要引入 `pinyinchch-type`, 并且实现 `Hmm` trait

  ```shell
  cargo add pinyinchch-type
  ```

- 如果使用 dag 的动态规划算法进行拼音到汉字的转换，那么需要启用 dag 功能。

  ```shell
  cargo add pinyinchch -F dag
  ```

  如果需要使用默认提供的 dag 模型，则需要引入 `pinyinchch-model-dag`

  ```shell
  cargo add pinyinchch-model-dag
  ```

  如果想自己实现一个拼音到汉字的 dag 模型，那么需要引入 `pinyinchch-type`, 并且实现 `dag` trait

  ```shell
  cargo add pinyinchch-type
  ```

## 使用示例

### 使用默认 HMM 模型将拼音转换成汉字

```rust
use pinyinchch::hmm::viterbi;
use pinyinchch::pinyin::pinyin_split_by_trie_tokenizer;
use pinyinchch_model_hmm::DefaultHmm;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建 HMM 实例
    let hmm = DefaultHmm::default();

    let pinyin = "nihao";

    // 对拼音字符串进行拆分
    let pinyin_split_str = pinyin_split_by_trie_tokenizer(pinyin);

    let pinyin_seq = pinyin_split_str.split(" ").collect::<Vec<_>>();

    // 使用 Viterbi 算法，返回 2 个候选结果，不使用对数概率
    let result = viterbi(&hmm, &pinyin_seq, 2, false, 3.14e-200);

    println!("HMM结果:");
    for item in &result {
        println!("分数: {}, 路径: {:?}", item.score(), item.path());
    }

    // 使用对数概率
    let result_log = viterbi(&hmm, &pinyin_seq, 2, true, 3.14e-200);

    println!("\nHMM结果 (对数概率):");
    for item in &result_log {
        println!("分数: {}, 路径: {:?}", item.score(), item.path());
    }

    Ok(())
}
```

输出

```bash
HMM结果:
分数: 0.00006800447668424451, 路径: ['你', '好']
分数: 0.000000006323093325525867, 路径: ['你', '毫']

HMM结果 (对数概率):
分数: -9.595937021363273, 路径: ['你', '好']
分数: -18.87905729828791, 路径: ['你', '嚎']
```

### 使用默认的 DAG 模型将拼音转换成汉字

```rust
use pinyinchch::dag::dispatch;
use pinyinchch_model_dag::DefaultDag;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建DAG参数实例
    let dag_params = DefaultDag::default();

    // 测试拼音转汉字
    let pinyin_list = vec!["ni", "hao"];

    // 使用DAG算法，返回2个候选结果，不使用对数概率
    let result = dispatch(&dag_params, &pinyin_list, 2, false);

    println!("DAG结果:");
    for item in &result {
        println!("分数: {}, 路径: {:?}", item.score(), item.path());
    }

    // 使用对数概率
    let result_log = dispatch(&dag_params, &pinyin_list, 2, true);

    println!("\nDAG结果 (对数概率):");
    for item in &result_log {
        println!("分数: {}, 路径: {:?}", item.score(), item.path());
    }

    Ok(())
}
```

输出

```bash
DAG结果:
分数: 0.21953546530016635, 路径: ["你好"]
分数: 0.015182124223139684, 路径: ["你", "好"]

DAG结果 (对数概率):
分数: -1.516241486396955, 路径: ["你好"]
分数: -4.187636581156532, 路径: ["你", "好"]
```

## 特性

- 支持 HMM 和 DAG 两种转换算法，提供对数概率和线性概率两种评分方式
- 支持自定义 HMM 和 DAG 实现
- 提供将拼音字符串拆分的方法

## 数据文件

本库使用预训练的模型数据，包括：

- `hmm_py2hz.json`: 拼音到汉字的映射
- `hmm_start.json`: 起始概率
- `hmm_emission.json`: 发射概率
- `hmm_transition.json`: 状态转移概率
- `dag_char.json`: 单字拼音数据
- `dag_phrase.json`: 词组拼音数据

## 测试

运行测试：

```bash
cargo test
```

## xtask 脚本

该脚本负责将原本以 json 格式保存的模型转换成以 rkyv 格式保存

在 workspace 中执行 `cargo xtask convert-to-rkyv` 即可将 data 目录下的模型转换到对应 crate 中

## 许可

许可任你喜欢选择下面任一种，或者两种都选

* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

### 贡献

除非您另有明确说明，否则任何您提交的代码许可应按上述 Apache 和 MIT 双重许可，并没有任何附加条款或条件。