Skip to content

Commit

Permalink
Add difference method
Browse files Browse the repository at this point in the history
  • Loading branch information
tansongchen committed Aug 6, 2024
1 parent 468c41a commit b39ee23
Show file tree
Hide file tree
Showing 16 changed files with 733 additions and 508 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libchai"
version = "0.1.13"
version = "0.1.14"
authors = ["Songchen Tan <i@tansongchen.com>"]
edition = "2018"

Expand Down
29 changes: 6 additions & 23 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use chai::config::Config;
use chai::encoder::occupation::Occupation;
use chai::encoder::simple_occupation::SimpleOccupation;
use criterion::{criterion_group, criterion_main, Criterion};

use chai::cli::{Cli, Command};
use chai::constraints::Constraints;
use chai::encoder::{Driver, Encoder};
use chai::encoder::Encoder;
use chai::objectives::Objective;
use chai::problem::Problem;
use chai::representation::{AssembleList, Assets};
Expand All @@ -26,41 +24,26 @@ fn simulate_cli_input(name: &str) -> (Config, AssembleList, Assets) {
cli.prepare_file()
}

fn do_benchmark(
representation: Representation,
fn process_cli_input(
config: Config,
elements: AssembleList,
assets: Assets,
driver: Box<dyn Driver>,
b: &mut Criterion,
) -> Result<(), Error> {
let representation = Representation::new(config)?;
let constraints = Constraints::new(&representation)?;
let encoder = Encoder::new(&representation, elements, &assets, driver)?;
let encoder = Encoder::new(&representation, elements, &assets)?;
let objective = Objective::new(&representation, encoder, assets)?;
let mut problem = Problem::new(representation, constraints, objective)?;
let candidate = problem.generate_candidate();
b.bench_function("Evaluation", |b| {
b.iter(|| {
problem.rank_candidate(&candidate);
problem.rank_candidate(&candidate, &None);
})
});
Ok(())
}

fn process_cli_input(
config: Config,
elements: AssembleList,
assets: Assets,
b: &mut Criterion,
) -> Result<(), Error> {
let representation = Representation::new(config)?;
let driver: Box<dyn Driver> = if representation.config.encoder.max_length <= 4 {
Box::new(SimpleOccupation::new(representation.get_space()))
} else {
Box::new(Occupation::new(representation.get_space()))
};
do_benchmark(representation, elements, assets, driver, b)
}

fn length_3(b: &mut Criterion) {
let (config, resource, assets) = simulate_cli_input("easy");
process_cli_input(config, resource, assets, b).unwrap();
Expand Down
72 changes: 39 additions & 33 deletions src/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,62 +133,68 @@ impl Constraints {
}
}

fn swap_narrowed_elements(&self, map: &KeyMap, element1: Element, element2: Element) -> KeyMap {
let mut next = map.clone();
pub fn constrained_random_swap(&self, keymap: &mut KeyMap) -> Vec<Element> {
let element1 = self.get_swappable_element();
let key1 = keymap[element1];
let mut element2 = self.get_swappable_element();
while keymap[element2] == key1 {
element2 = self.get_swappable_element();
}
let key2 = keymap[element2];
let destinations1 = self.narrowed.get(&element1).unwrap_or(&self.alphabet);
let destinations2 = self.narrowed.get(&element2).unwrap_or(&self.alphabet);
//分开判断可行性。这样如果无法交换,至少移动一下。
if destinations1.contains(&map[element2]) {
next[element1] = map[element2];
if destinations1.contains(&key2) {
keymap[element1] = key2;
}
if destinations2.contains(&map[element1]) {
next[element2] = map[element1];
if destinations2.contains(&key1) {
keymap[element2] = key1;
}
next
}

pub fn constrained_random_swap(&self, map: &KeyMap) -> KeyMap {
let element1 = self.get_swappable_element();
let element2 = self.get_swappable_element();
self.swap_narrowed_elements(map, element1, element2)
vec![element1, element2]
}

pub fn constrained_full_key_swap(&self, map: &KeyMap) -> KeyMap {
pub fn constrained_full_key_swap(&self, keymap: &mut KeyMap) -> Vec<Element> {
let mut rng = thread_rng();
let mut next = map.clone();
// 寻找一个可移动元素和一个它的可行移动位置,然后把这两个键上的所有元素交换
// 这样交换不成也至少能移动一次
let movable_element = self.get_movable_element();
let key1 = map[movable_element];
let destinations = self
let key1 = keymap[movable_element];
let mut destinations = self
.narrowed
.get(&movable_element)
.unwrap_or(&self.alphabet);
.unwrap_or(&self.alphabet)
.clone();
destinations.retain(|x| *x != key1);
let key2 = destinations.choose(&mut rng).unwrap(); // 在编译约束时已经确保了这里一定有可行的移动位置
for (element, key) in map.iter().enumerate() {
if (*key == key1 || *key == *key2) && !self.fixed.contains(&element) {
let destination = if *key == *key2 { key1 } else { *key2 };
//将元素移动到目标
//考虑到组合中的元素必然在同样的键上,有同样的约束条件,也必然跟随移动,这里不再判断组合
let destinations2 = self.narrowed.get(&element).unwrap_or(&self.alphabet);
if destinations2.contains(&destination) {
next[element] = destination;
}
let mut moved_elements = vec![];
for (element, key) in keymap.iter_mut().enumerate() {
if *key != key1 && *key != *key2 || self.fixed.contains(&element) {
continue;
}
let destination = if *key == *key2 { key1 } else { *key2 };
// 将元素移动到目标
let destinations2 = self.narrowed.get(&element).unwrap_or(&self.alphabet);
if destinations2.contains(&destination) {
*key = destination;
}
moved_elements.push(element);
}
next
moved_elements
}

pub fn constrained_random_move(&self, map: &KeyMap) -> KeyMap {
pub fn constrained_random_move(&self, keymap: &mut KeyMap) -> Vec<Element> {
let mut rng = thread_rng();
let mut next = map.clone();
let movable_element = self.get_movable_element();
let current = keymap[movable_element];
let destinations = self
.narrowed
.get(&movable_element)
.unwrap_or(&self.alphabet);
let key = destinations.choose(&mut rng).unwrap(); // 在编译约束时已经确保了这里一定有可行的移动位置
next[movable_element] = *key;
next
let mut key = destinations.choose(&mut rng).unwrap(); // 在编译约束时已经确保了这里一定有可行的移动位置
while *key == current {
key = destinations.choose(&mut rng).unwrap();
}
keymap[movable_element] = *key;
vec![movable_element]
}
}
40 changes: 34 additions & 6 deletions src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
use crate::error::Error;
use crate::representation::{
Assemble, AssembleList, Assets, AutoSelect, Code, CodeInfo, CodeSubInfo, Codes, Entry,
Assemble, AssembleList, Assets, AutoSelect, Code, CodeInfo, CodeSubInfo, Codes, Element, Entry,
Frequency, Key, KeyMap, Representation, Sequence, MAX_WORD_LENGTH,
};
use c3::C3;
use occupation::Occupation;
use rustc_hash::{FxHashMap, FxHashSet};
use simple_occupation::SimpleOccupation;
use std::cmp::Reverse;

pub mod c3;
Expand Down Expand Up @@ -72,7 +75,15 @@ pub fn adapt(
}

pub trait Driver {
fn run(&mut self, keymap: &KeyMap, config: &EncoderConfig, buffer: &mut Codes);
fn init(&mut self, _config: &EncoderConfig, _representation: &Representation) {}

fn run(
&mut self,
keymap: &KeyMap,
config: &EncoderConfig,
buffer: &mut Codes,
moved_elements: &[Element],
);
}

pub struct EncoderConfig {
Expand All @@ -83,6 +94,7 @@ pub struct EncoderConfig {
pub first_key: Key,
pub short_code: Option<[Vec<CompiledScheme>; MAX_WORD_LENGTH]>,
pub encodables: Vec<Encodable>,
pub elements_length: usize,
}

impl EncoderConfig {
Expand All @@ -108,6 +120,7 @@ pub struct Encoder {
pub transition_matrix: Vec<Vec<(usize, u64)>>,
pub config: EncoderConfig,
driver: Box<dyn Driver>,
default_driver: Occupation,
}

impl Encoder {
Expand All @@ -118,7 +131,6 @@ impl Encoder {
representation: &Representation,
resource: AssembleList,
assets: &Assets,
driver: Box<dyn Driver>,
) -> Result<Self, Error> {
let encoder = &representation.config.encoder;
let max_length = encoder.max_length;
Expand Down Expand Up @@ -195,23 +207,39 @@ impl Encoder {
radix: representation.radix,
select_keys: representation.select_keys.clone(),
first_key: representation.select_keys[0],
elements_length: representation.element_repr.len(),
short_code,
};
let mut driver: Box<dyn Driver> = if representation.config.info.name == "c3" {
Box::new(C3::new(representation.get_space()))
} else if representation.config.encoder.max_length <= 4 {
Box::new(SimpleOccupation::new(representation.get_space()))
} else {
Box::new(Occupation::new(representation.get_space()))
};
driver.init(&config, representation);
let encoder = Self {
transition_matrix,
buffer,
config,
driver,
default_driver: Occupation::new(representation.get_space()),
};
Ok(encoder)
}

pub fn prepare(&mut self, keymap: &KeyMap) {
self.driver.run(keymap, &self.config, &mut self.buffer);
pub fn init(&mut self, keymap: &KeyMap) {
self.default_driver
.run(keymap, &self.config, &mut self.buffer, &[]);
}

pub fn prepare(&mut self, keymap: &KeyMap, moved_elements: &[Element]) {
self.driver
.run(keymap, &self.config, &mut self.buffer, moved_elements);
}

pub fn encode(&mut self, keymap: &KeyMap, representation: &Representation) -> Vec<Entry> {
self.prepare(keymap);
self.init(keymap);
let mut entries: Vec<(usize, Entry)> = Vec::new();
let recover = |code: Code| representation.repr_code(code).iter().collect();
let buffer = &self.buffer;
Expand Down
58 changes: 41 additions & 17 deletions src/encoder/c3.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,68 @@
use super::{Driver, EncoderConfig};
use crate::representation::{Codes, KeyMap};
use std::iter::zip;
use crate::representation::{Codes, Element, KeyMap, Representation};

/// 编码是否已被占据
/// 用一个数组和一个哈希集合来表示,数组用来表示四码以内的编码,哈希集合用来表示四码以上的编码
pub struct C3 {
pub full_space: Vec<u8>,
pub full_space: Vec<bool>,
pub involved_message: Vec<Vec<usize>>,
}

impl C3 {
pub fn new(length: usize) -> Self {
Self {
full_space: vec![0; length],
full_space: vec![false; length],
involved_message: vec![],
}
}

pub fn reset(&mut self) {
self.full_space.iter_mut().for_each(|x| {
*x = 0;
*x = false;
});
}
}

impl Driver for C3 {
fn run(&mut self, keymap: &KeyMap, config: &EncoderConfig, buffer: &mut Codes) {
fn init(&mut self, config: &EncoderConfig, _: &Representation) {
for _ in 0..=config.elements_length {
self.involved_message.push(vec![]);
}
for (index, encodable) in config.encodables.iter().enumerate() {
for element in &encodable.sequence {
self.involved_message[*element].push(index);
}
}
}

fn run(
&mut self,
keymap: &KeyMap,
config: &EncoderConfig,
buffer: &mut Codes,
moved_elements: &[Element],
) {
self.reset();
// 1. 全码
for (encodable, pointer) in zip(&config.encodables, buffer.iter_mut()) {
let sequence = &encodable.sequence;
assert!(sequence.len() >= 3);
let code = keymap[sequence[0]] as u64 * config.radix * config.radix
+ keymap[sequence[1]] as u64 * config.radix
+ keymap[sequence[2]] as u64;
pointer.full.actual = code;

// 部分编码,只计算变化的部分
for element in moved_elements {
for index in &self.involved_message[*element] {
let pointer = &mut buffer[*index];
let encodable = &config.encodables[*index];
let sequence = &encodable.sequence;
let full = &mut pointer.full;
let code = keymap[sequence[0]] as u64 * config.radix * config.radix
+ keymap[sequence[1]] as u64 * config.radix
+ keymap[sequence[2]] as u64;
full.check_actual(code);
}
}

for pointer in buffer.iter_mut() {
let rank = self.full_space[pointer.full.actual as usize];
pointer.full.duplicate = rank > 0;
self.full_space[pointer.full.actual as usize] = rank.saturating_add(1);
let full = &mut pointer.full;
let duplicate = self.full_space[full.actual as usize];
full.check_duplicate(duplicate);
self.full_space[full.actual as usize] = true;
}
}
}
Loading

0 comments on commit b39ee23

Please sign in to comment.