Static Local Linearization for Differentiable Discrete Programming
Project description
🎯 核心问题
离散点(非微分体系)的局限性
在数学和计算领域,连续函数拥有丰富的工具和运算方法,而离散点则受到很大限制:
计算方法对比
| 数学领域 | 连续函数(线) | 离散点 |
|---|---|---|
| 微积分 | 求导、积分、微分方程 | ❌ 无法直接应用 |
| 优化 | 梯度下降、牛顿法、共轭梯度 | ❌ 梯度为零或不存在 |
| 分析 | 泰勒展开、傅里叶变换 | ❌ 需要特殊处理 |
| 概率 | 概率密度函数(PDF) | ✅ 概率质量函数(PMF) |
| 代数 | 矩阵运算、特征分析 | ✅ 有限域运算 |
离散点的具体限制
| 操作类型 | 离散点的问题 | 连续函数的优势 |
|---|---|---|
| 求导 | 离散点没有切线,导数不存在 | 任意点可求导 |
| 链式法则 | 梯度无法传递通过离散边界 | 梯度流畅传递 |
| 积分 | 无法定义积分区间 | 可计算定积分、不定积分 |
| 优化 | 离散跳跃导致优化困难 | 平滑曲面易于优化 |
| 逼近 | 只能用有限差分 | 泰勒展开、插值等多种方法 |
一句话总结:连续函数拥有丰富的数学工具(导数、积分、优化算法等),而离散点几乎无法使用这些工具。
💡 SLL 的解决方案
将离散点转换为可微分的连续函数
SLL(Static Local Linearization)的核心思想是:
离散点 x_i → 分段线性函数 y = x_i + k*(x - x_i)
在每个离散点 x_i 附近创建一个无限小的线性区域,使得:
- 函数值在
x_i处保持不变 - 梯度可以通过线性函数传递
- 微分体系中的所有运算都可以正常应用
这就是 SLL 的核心价值:让原本只能应用于连续函数的强大数学工具,现在也能应用于离散数据!
⚡ 快速开始
import torch
import sll_core
# 定义包含离散操作的函数
def my_discrete_function(x):
return torch.round(x)
# 使用 SLL 包装,使其可微分
wrapped_func = sll_core.SLL(my_discrete_function)
# 现在可以正常计算梯度!
x = torch.tensor([1.2, 2.5, 3.7], requires_grad=True)
y = wrapped_func(x)
y.backward(torch.ones_like(y))
print(f"输入: {x}")
print(f"输出: {y}")
print(f"梯度: {x.grad}")
🚀 安装
pip install sll-core
要求: Python ≥ 3.8,PyTorch ≥ 1.9.0
📖 使用方式
方式一:类包装
import sll_core
def quantize(x):
return torch.round(x * 255) / 255
# 创建可微分包装
wrapped_quantize = sll_core.SLL(quantize, eps=1e-3)
x = torch.tensor([0.123, 0.456, 0.789], requires_grad=True)
y = wrapped_quantize(x)
y.backward(torch.ones_like(y))
方式二:装饰器
import sll_core
@sll_core.sll(eps=1e-3)
def threshold(x):
return (x > 0.5).float()
x = torch.tensor([0.3, 0.6, 0.9], requires_grad=True)
y = threshold(x)
y.backward(torch.ones_like(y))
方式三:直接转换离散输出
import sll_core
x = torch.tensor([0.0, 1.0, 2.0], requires_grad=True)
discrete_output = (x > 0.5).float()
# 将离散输出转为可微分形式
diff_output = sll_core.make_differentiable(discrete_output, eps=1e-3)
loss = diff_output.sum()
loss.backward()
🔧 API 参考
SLL 类
class SLL(func=None, eps=1e-3)
参数:
func: 要包装的函数(可选)eps: 线性化区域的宽度,默认 1e-3
sll 装饰器
@sll(eps=1e-3)
def my_function(x):
return torch.round(x)
make_differentiable 函数
make_differentiable(output, eps=1e-3)
参数:
output: 离散输出张量eps: 线性化参数
piecewise_linear_approximation 函数
piecewise_linear_approximation(x, eps=1e-3)
参数:
x: 输入张量eps: 分段线性区域宽度
🔬 应用场景
场景 1:梯度流经离散操作
import sll_core
@sll_core.sll(eps=1e-3)
def discrete_op(x):
return torch.round(x)
x = torch.tensor([1.5, 2.3, 3.7], requires_grad=True)
y = discrete_op(x)
z = y ** 2 # 在离散输出上执行连续运算
loss = z.sum()
loss.backward() # 梯度成功传递!
场景 2:离散优化问题
import sll_core
weights = torch.tensor([2.0, 3.0, 4.0])
values = torch.tensor([3.0, 4.0, 5.0])
capacity = torch.tensor(8.0)
@sll_core.sll(eps=0.1)
def knapsack_objective(probabilities):
selected = (probabilities > 0.5).float() # 离散决策
total_weight = (selected * weights).sum()
total_value = (selected * values).sum()
penalty = torch.max(torch.tensor(0.0), total_weight - capacity) * 100
return -(total_value - penalty)
probabilities = torch.tensor([0.5, 0.5, 0.5], requires_grad=True)
optimizer = torch.optim.Adam([probabilities], lr=0.1)
for _ in range(100):
optimizer.zero_grad()
loss = knapsack_objective(probabilities)
loss.backward() # 可微分!
optimizer.step()
场景 3:量化感知训练
import torch.nn as nn
import sll_core
class QuantizedNet(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(10, 5)
self.quantize = sll_core.SLL(lambda x: torch.round(x * 16) / 16)
def forward(self, x):
x = self.fc(x)
x = self.quantize(x) # 离散量化操作
return x
model = QuantizedNet()
x = torch.randn(3, 10, requires_grad=True)
y = model(x)
y.sum().backward() # 梯度成功流经量化操作!
⚙️ 数学原理
核心公式
在离散点 x_i 处,SLL 创建如下分段线性函数:
y = x_i + (x - x_i) * (1/eps) for x ∈ [x_i - eps/2, x_i + eps/2]
梯度计算
使用有限差分近似:
df/dx ≈ [f(x+ε) - f(x-ε)] / (2ε)
关键特性
| 特性 | 说明 |
|---|---|
| 保持原值 | 在离散点处函数值保持不变 |
| 可微分性 | 线性区域内可以正常求导 |
| 链式传递 | 梯度可以通过链式法则传递 |
| 数值稳定 | 通过 clamp 限制梯度范围 |
🏛️ 项目结构
sll-core/
├── sll_core/
│ ├── __init__.py # 模块导出
│ └── core.py # 核心实现
├── README.md # 中文文档
├── README_EN.md # 英文文档
├── LICENSE # 许可证
└── pyproject.toml # 打包配置
📈 对比分析
离散点 vs 连续函数的计算能力
| 数学运算 | 离散点(原始) | 连续函数 | SLL 转换后 |
|---|---|---|---|
| 求导 | ❌ 不可行 | ✅ 可行 | ✅ 可行 |
| 链式法则 | ❌ 中断 | ✅ 正常传递 | ✅ 正常传递 |
| 积分 | ❌ 不可行 | ✅ 可行 | ✅ 可行 |
| 优化算法 | ❌ 困难 | ✅ 高效 | ✅ 高效 |
| 泰勒展开 | ❌ 不可行 | ✅ 可行 | ✅ 可行 |
| 傅里叶变换 | ⚠️ 受限 | ✅ 可行 | ✅ 可行 |
SLL 的价值
┌─────────────────────────────────────────────────────────────┐
│ 数学工具库 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 求导 │ 积分 │ 优化 │ 泰勒展开 │ 傅里叶变换 │ │
│ └─────────────────────────────────────────────────────┘ │
└───────────────────────────┬─────────────────────────────────┘
│
│ 连续函数可以直接使用
▼
┌─────────────────────────────────────────────────────────────┐
│ 连续函数 (线) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ f(x) = x² + 2x + 1 ← 可以使用所有数学工具 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ SLL 转换
▼
┌─────────────────────────────────────────────────────────────┐
│ 离散点 → SLL → 可微分函数 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ f(x) = round(x) → SLL(f) → 可使用数学工具 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
梯度对比示例
import torch
import sll_core
x = torch.tensor([1.2, 2.5, 3.7], requires_grad=True)
# 原始 round 操作(离散点,不可微)
y1 = torch.round(x)
try:
y1.backward(torch.ones_like(y1))
print(f"原始梯度: {x.grad}") # 不会执行到这里
except RuntimeError as e:
print(f"原始操作错误: {e}")
# SLL 包装后(转换为可微分函数)
@sll_core.sll(eps=1e-3)
def round_sll(x):
return torch.round(x)
x.grad = None
y2 = round_sll(x)
y2.backward(torch.ones_like(y2))
print(f"SLL 梯度: {x.grad}")
📄 许可证
MIT License - 详见 LICENSE
🤝 贡献指南
欢迎提交 Issue 和 Pull Request!
📚 引用
如果您在研究中使用 SLL,请引用:
@software{sll-core,
title = {SLL-Core: Static Local Linearization for Differentiable Discrete Programming},
author = {Jacksong},
year = {2026},
url = {https://github.com/jacksong-sourse/sll-core},
}
⭐ 如果这个项目对您有帮助,请给个 Star!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sll_core-2.0.1.tar.gz.
File metadata
- Download URL: sll_core-2.0.1.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d33a361606ebdd59271542ec4340007507ee0865c83cd8017a47cf1882d7ffa
|
|
| MD5 |
1f99b743dc07f379159b17a01aa0a845
|
|
| BLAKE2b-256 |
37a1168a88eeb99e3f7ab2ce72011d3a0389f2300793f9ca1e9efdbc165c871c
|
Provenance
The following attestation bundles were made for sll_core-2.0.1.tar.gz:
Publisher:
publish.yml on jacksong-sourse/sll-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sll_core-2.0.1.tar.gz -
Subject digest:
9d33a361606ebdd59271542ec4340007507ee0865c83cd8017a47cf1882d7ffa - Sigstore transparency entry: 1505897677
- Sigstore integration time:
-
Permalink:
jacksong-sourse/sll-core@479c6a5ed4e1546d73496519bfcc91d42542a4d1 -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/jacksong-sourse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@479c6a5ed4e1546d73496519bfcc91d42542a4d1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sll_core-2.0.1-py3-none-any.whl.
File metadata
- Download URL: sll_core-2.0.1-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a318967fe4c88647adead535cc9fe9cdd321e0950393a4a930910d1f25cb9d2
|
|
| MD5 |
098ec077da02a32de5a4294201a6e341
|
|
| BLAKE2b-256 |
99dd87d279313169879e1a2ae34e326fbd1f0b0ed5b1af9bf07f458e5e8b128d
|
Provenance
The following attestation bundles were made for sll_core-2.0.1-py3-none-any.whl:
Publisher:
publish.yml on jacksong-sourse/sll-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sll_core-2.0.1-py3-none-any.whl -
Subject digest:
9a318967fe4c88647adead535cc9fe9cdd321e0950393a4a930910d1f25cb9d2 - Sigstore transparency entry: 1505897794
- Sigstore integration time:
-
Permalink:
jacksong-sourse/sll-core@479c6a5ed4e1546d73496519bfcc91d42542a4d1 -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/jacksong-sourse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@479c6a5ed4e1546d73496519bfcc91d42542a4d1 -
Trigger Event:
push
-
Statement type: