Browse Source

Cache lowest bounds for combining pairs

master
Jirka Sejkora 4 years ago
parent
commit
affb99472c
  1. 6
      src/combine-layouts.rs
  2. 69
      src/combine.rs
  3. 94
      src/db.rs

6
src/combine-layouts.rs

@ -23,7 +23,7 @@ fn main() {
let transposed_city = transpose_city(&city);
eprintln!("Finished building a transposed city");
const TOP_LAYOUT_COUNT: usize = 300;
const TOP_LAYOUT_COUNT: usize = 1000;
let mut cache = combine::CompatibilityCache::new();
@ -36,7 +36,7 @@ fn main() {
.map(|layout| (*layout).clone())
.collect();
let chosen_layouts: Vec<_> = sorted.into_iter().take(TOP_LAYOUT_COUNT).collect();
if combine::try_combine(&city, &chosen_layouts, &chosen_layouts, &mut db, &mut cache, false) {
if combine::create_new_best_combination(&city, &chosen_layouts, &chosen_layouts, &mut db, &mut cache, false) {
last_improve_step = LastStep::Vertical;
}
eprintln!("Finished vertical cuts, improvement: {}", last_improve_step == LastStep::Vertical);
@ -51,7 +51,7 @@ fn main() {
let transposed_chosen_layouts: Vec<_> = chosen_layouts.iter().map(|x| transpose_saved_layout(x)).collect();
eprintln!("Starting to combine {} top houses DB; horizontal cuts", TOP_LAYOUT_COUNT);
if combine::try_combine(&transposed_city, &transposed_chosen_layouts, &transposed_chosen_layouts, &mut db, &mut cache, true) {
if combine::create_new_best_combination(&transposed_city, &transposed_chosen_layouts, &transposed_chosen_layouts, &mut db, &mut cache, true) {
last_improve_step = LastStep::Horizontal;
}
eprintln!("Finished horizontal cuts, improvement: {}", last_improve_step == LastStep::Horizontal);

69
src/combine.rs

@ -1,5 +1,5 @@
use crate::city;
use crate::db::{LayoutDB, SavedLayout};
use crate::db::{LayoutDB, SavedLayout, MergeLowerBound};
use crate::city::{City, House, SIZE};
use itertools::Itertools;
use itertools::iproduct;
@ -37,13 +37,38 @@ impl CompatibilityCache {
}
}
pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts: &Vec<SavedLayout>, db: &mut LayoutDB, cache: &mut CompatibilityCache, transposed: bool) -> bool {
pub fn create_new_best_combination(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts: &Vec<SavedLayout>, db: &mut LayoutDB, cache: &mut CompatibilityCache, transposed: bool) -> bool {
let mut best_price = left_layouts.iter().chain(right_layouts.iter())
.map(|layout| city::get_price(&city, layout.houses()))
.min();
let mut improved = false;
let mut lefts = Vec::new();
let mut rights = Vec::new();
for left_layout in left_layouts {
// We make sure that there is at least one other layout we want to compare this one with
// to avoid unnecessary house updates
let mut needed = false;
for right_layout in right_layouts {
if let Some(bound) = db.get_merge_lower_bound(left_layout, right_layout, transposed) {
if bound < best_price.expect("No best price set while lower bounds exist") {
eprintln!("Low bound; left needed ({} - {})", left_layout.id(), right_layout.id());
needed = true;
break;
}
} else {
eprintln!("No bound; left needed ({} - {})", left_layout.id(), right_layout.id());
needed = true;
break;
}
}
if !needed {
continue;
}
// Sorted in reverse so we can remove from the end
let sorted_houses: Vec<_> = left_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect();
@ -55,6 +80,23 @@ pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts:
}
for right_layout in right_layouts {
// We make sure that there is at least one other layout we want to compare this one with
// to avoid unnecessary house updates
let mut needed = false;
for left_layout in left_layouts {
if let Some(bound) = db.get_merge_lower_bound(left_layout, right_layout, transposed) {
if bound < best_price.expect("No best price set while lower bounds exist") {
needed = true;
}
} else {
needed = true;
}
}
if !needed {
continue;
}
let mut sorted_houses: Vec<_> = right_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect();
let mut line = RightLine::new();
@ -68,10 +110,6 @@ pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts:
});
}
let mut best_price = left_layouts.iter().chain(right_layouts.iter())
.map(|layout| city::get_price(&city, layout.houses()))
.min();
let axis = if transposed { "y" } else { "x" };
// x is the last left coordinate, x+1 is right
@ -103,6 +141,14 @@ pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts:
let pairs = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate())
.filter(|((left_i, _), (right_i, _))| left_i != right_i)
.filter(|((_, left), (_, right))| {
if let Some(bound) = db.get_merge_lower_bound(left.layout, right.layout, transposed) {
if bound >= best_price.expect("No best price set while lower bounds exist") {
return false;
}
}
return true;
})
.map(|((left_i, left), (right_i, right))| (left, right, left.line.price + right.line.price, left_i, right_i))
.sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2));
@ -156,6 +202,17 @@ pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts:
eprintln!("{} incompatibles checked before {} compatible ({} cache hits)", incompatibles, compatibles, cache_hits);
}
if let Some(lowest_price) = best_price {
let mut new_bounds = Vec::new();
for left_layout in left_layouts {
for right_layout in right_layouts {
new_bounds.push(MergeLowerBound::new(left_layout.id(), right_layout.id(), transposed, lowest_price));
}
}
db.add_merge_lower_bounds(new_bounds);
}
improved
}

94
src/db.rs

@ -4,13 +4,43 @@ use std::collections::HashMap;
pub struct LayoutDB {
connection: Connection,
layouts: Vec<SavedLayout>
layouts: Vec<SavedLayout>,
merge_lower_bounds: HashMap<(usize, usize, bool), MergeLowerBound>,
}
pub struct MergeLowerBound {
left_layout_id: usize,
right_layout_id: usize,
y_axis: bool,
price: u32,
}
impl MergeLowerBound {
pub fn new(left_layout_id: usize, right_layout_id: usize, y_axis: bool, price: u32) -> Self {
MergeLowerBound { left_layout_id, right_layout_id, y_axis, price }
}
pub fn left_layout_id(&self) -> usize {
self.left_layout_id
}
pub fn right_layout_id(&self) -> usize {
self.right_layout_id
}
pub fn y_axis(&self) -> bool {
self.y_axis
}
pub fn price(&self) -> u32 {
self.price
}
}
#[derive(Clone)]
pub struct SavedLayout {
id: usize,
houses: Vec<House>
houses: Vec<House>,
}
impl SavedLayout {
@ -45,6 +75,25 @@ impl LayoutDB {
}
}
let mut merges = HashMap::new();
{
let mut stmt = connection.prepare("SELECT left_layout_id, right_layout_id, axis, price FROM merge_lower_bounds")?;
let mut rows = stmt.query(NO_PARAMS)?;
while let Some(row) = rows.next()? {
let left_id: u32 = row.get(0)?;
let right_id: u32 = row.get(1)?;
let axis: u32 = row.get(2)?;
let price: u32 = row.get(3)?;
merges.insert((left_id as usize, right_id as usize, axis == 1), MergeLowerBound {
left_layout_id: left_id as usize,
right_layout_id: right_id as usize,
y_axis: axis == 1,
price,
});
}
}
let layouts = layouts.into_iter().map(|(id, xy_pairs)|
SavedLayout
{
@ -53,7 +102,7 @@ impl LayoutDB {
}
).collect();
Ok(LayoutDB { connection, layouts })
Ok(LayoutDB { connection, layouts, merge_lower_bounds: merges })
}
pub fn layouts(&self) -> &Vec<SavedLayout> {
@ -70,7 +119,44 @@ impl LayoutDB {
params![layout_id, house.x as u32, house.y as u32])?;
}
transaction.commit()?;
self.layouts.push(SavedLayout {id: layout_id as usize, houses: houses.clone()});
self.layouts.push(SavedLayout { id: layout_id as usize, houses: houses.clone() });
Ok(())
}
pub fn get_merge_lower_bound(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, y_axis: bool) -> Option<u32> {
if let Some(bound) = self.merge_lower_bounds.get(&(left_layout.id, right_layout.id, y_axis)) {
return Some(bound.price);
}
None
}
pub fn add_merge_lower_bound(&mut self, lower_bound: MergeLowerBound) -> Result<()> {
let transaction = self.connection.transaction()?;
transaction.execute("INSERT INTO merge_lower_bounds (left_layout_id, right_layout_id, axis, price) VALUES (?1, ?2, ?3, ?4) ON CONFLICT(left_layout_id, right_layout_id) DO UPDATE SET price = ?4",
params![lower_bound.left_layout_id as u32,
lower_bound.right_layout_id as u32,
if lower_bound.y_axis {1u32} else {0u32},
lower_bound.price
],
)?;
transaction.commit()?;
self.merge_lower_bounds.insert((lower_bound.left_layout_id, lower_bound.right_layout_id, lower_bound.y_axis), lower_bound);
Ok(())
}
pub fn add_merge_lower_bounds(&mut self, lower_bounds: Vec<MergeLowerBound>) -> Result<()> {
let transaction = self.connection.transaction()?;
for lower_bound in lower_bounds {
transaction.execute("INSERT INTO merge_lower_bounds (left_layout_id, right_layout_id, axis, price) VALUES (?1, ?2, ?3, ?4) ON CONFLICT(left_layout_id, right_layout_id) DO UPDATE SET price = ?4",
params![lower_bound.left_layout_id as u32,
lower_bound.right_layout_id as u32,
if lower_bound.y_axis {1u32} else {0u32},
lower_bound.price
],
)?;
self.merge_lower_bounds.insert((lower_bound.left_layout_id, lower_bound.right_layout_id, lower_bound.y_axis), lower_bound);
}
transaction.commit()?;
Ok(())
}
}

Loading…
Cancel
Save