Cache lowest bounds for combining pairs
This commit is contained in:
parent
3facf2f297
commit
affb99472c
3 changed files with 156 additions and 13 deletions
|
@ -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);
|
||||
|
|
|
@ -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
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…
Reference in a new issue