mirror of
https://github.com/unanmed/ginka-generator.git
synced 2026-08-14 18:12:28 +08:00
123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
import torch
|
||
import numpy as np
|
||
|
||
# 工具函数:密度常量、剩余密度计算、掩码生成、邻接检测
|
||
|
||
MAP_W = 13 # 地图宽度
|
||
MAP_H = 13 # 地图高度
|
||
DENSITY_DIM = 5 # [wall, door, monster, entrance, resource]
|
||
MAP_SIZE = MAP_W * MAP_H # 地图大小
|
||
|
||
WALL_DENSITY_IDX = 0
|
||
DOOR_DENSITY_IDX = 1
|
||
MONSTER_DENSITY_IDX = 2
|
||
ENTRANCE_DENSITY_IDX = 3
|
||
RESOURCE_DENSITY_IDX = 4
|
||
|
||
def print_memory(device, tag=""):
|
||
if torch.cuda.is_available():
|
||
print(f"{tag} | 当前显存: {torch.cuda.memory_allocated(device) / 1024**2:.2f} MB, 最大显存: {torch.cuda.max_memory_allocated(device) / 1024**2:.2f} MB")
|
||
else:
|
||
print("当前设备不支持 cuda.")
|
||
|
||
def compute_remaining(
|
||
current: torch.Tensor,
|
||
target_density: torch.Tensor,
|
||
stage: int
|
||
) -> torch.Tensor:
|
||
remain = torch.zeros(current.size(0), DENSITY_DIM, device=current.device)
|
||
|
||
visible_wall = (current == 1).sum(dim=1).float() / MAP_SIZE
|
||
visible_door = ((current == 2) | (current == 6)).sum(dim=1).float() / MAP_SIZE
|
||
visible_monster = (current == 4).sum(dim=1).float() / MAP_SIZE
|
||
visible_entrance = (current == 5).sum(dim=1).float() / MAP_SIZE
|
||
visible_resource = (current == 3).sum(dim=1).float() / MAP_SIZE
|
||
|
||
if stage == 1:
|
||
remain[:, WALL_DENSITY_IDX] = (
|
||
target_density[:, WALL_DENSITY_IDX] - visible_wall
|
||
).clamp(min=0.0, max=1.0)
|
||
elif stage == 2:
|
||
remain[:, DOOR_DENSITY_IDX] = (
|
||
target_density[:, DOOR_DENSITY_IDX] - visible_door
|
||
).clamp(min=0.0, max=1.0)
|
||
remain[:, MONSTER_DENSITY_IDX] = (
|
||
target_density[:, MONSTER_DENSITY_IDX] - visible_monster
|
||
).clamp(min=0.0, max=1.0)
|
||
remain[:, ENTRANCE_DENSITY_IDX] = (
|
||
target_density[:, ENTRANCE_DENSITY_IDX] - visible_entrance
|
||
).clamp(min=0.0, max=1.0)
|
||
elif stage == 3:
|
||
remain[:, RESOURCE_DENSITY_IDX] = (
|
||
target_density[:, RESOURCE_DENSITY_IDX] - visible_resource
|
||
).clamp(min=0.0, max=1.0)
|
||
|
||
return remain
|
||
|
||
def rect_mask(
|
||
ratio: float, h_range: tuple[int, int] = (2, 7),
|
||
w_range: tuple[int, int] = (2, 7)
|
||
) -> np.ndarray:
|
||
# 纯矩形分块掩码,反复放置随机矩形直到掩码格数达标
|
||
target = int(MAP_SIZE * ratio)
|
||
mask = np.zeros((MAP_H, MAP_W), dtype=bool)
|
||
while mask.sum() < target:
|
||
bh = np.random.randint(h_range[0], h_range[1])
|
||
bw = np.random.randint(w_range[0], w_range[1])
|
||
x = np.random.randint(0, MAP_H - bh + 1)
|
||
y = np.random.randint(0, MAP_W - bw + 1)
|
||
mask[x:x + bh, y:y + bw] = True
|
||
return mask
|
||
|
||
def compute_adjacency_mask(flat_state: torch.Tensor) -> torch.Tensor:
|
||
# 返回与输入同形状的 bool tensor,True 表示该位置与任意墙壁 4-邻接
|
||
# 支持 [MAP_SIZE] 和 [B, MAP_SIZE] 两种输入
|
||
was_1d = flat_state.dim() == 1
|
||
if was_1d:
|
||
flat_state = flat_state.unsqueeze(0)
|
||
state_2d = flat_state.reshape(flat_state.size(0), MAP_H, MAP_W)
|
||
wall = (state_2d == 1)
|
||
adj = torch.zeros_like(wall, dtype=torch.bool)
|
||
adj[:, :, 1:] |= wall[:, :, :-1]
|
||
adj[:, :, :-1] |= wall[:, :, 1:]
|
||
adj[:, 1:, :] |= wall[:, :-1, :]
|
||
adj[:, :-1, :] |= wall[:, 1:, :]
|
||
result = adj.reshape(flat_state.size(0), MAP_SIZE)
|
||
if was_1d:
|
||
result = result.squeeze(0)
|
||
return result
|
||
|
||
def summarize_codebook_hits(code_hits):
|
||
# code_hits 为 tuple of 3 tensors(各量器不同 K)
|
||
# 返回各阶段独立统计 + 汇总统计
|
||
names = ["q1(stage1)", "q2(stage2)", "q3(stage3)"]
|
||
result = {}
|
||
for hits, name in zip(code_hits, names):
|
||
total = hits.sum()
|
||
if total.item() <= 0:
|
||
result[name] = {"ppl": 0.0, "usage": 0.0, "active": 0, "K": int(hits.numel())}
|
||
continue
|
||
probs = hits / total
|
||
ppl = torch.exp(
|
||
-(probs * torch.log(probs.clamp_min(1e-10))).sum()
|
||
).item()
|
||
active = int((hits > 0).sum().item())
|
||
usage = active / hits.numel()
|
||
result[name] = {"ppl": ppl, "usage": usage, "active": active, "K": int(hits.numel())}
|
||
|
||
# 汇总统计
|
||
combined = torch.cat([h.flatten() for h in code_hits], dim=0)
|
||
total_hits = combined.sum()
|
||
if total_hits.item() > 0:
|
||
probs = combined / total_hits
|
||
result["combined"] = {
|
||
"ppl": float(torch.exp(
|
||
-(probs * torch.log(probs.clamp_min(1e-10))).sum()
|
||
).item()),
|
||
"active": int((combined > 0).sum().item()),
|
||
"K": int(combined.numel()),
|
||
}
|
||
else:
|
||
result["combined"] = {"ppl": 0.0, "active": 0, "K": int(combined.numel())}
|
||
return result
|