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);
|
let transposed_city = transpose_city(&city);
|
||||||
eprintln!("Finished building a transposed 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();
|
let mut cache = combine::CompatibilityCache::new();
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ fn main() {
|
||||||
.map(|layout| (*layout).clone())
|
.map(|layout| (*layout).clone())
|
||||||
.collect();
|
.collect();
|
||||||
let chosen_layouts: Vec<_> = sorted.into_iter().take(TOP_LAYOUT_COUNT).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;
|
last_improve_step = LastStep::Vertical;
|
||||||
}
|
}
|
||||||
eprintln!("Finished vertical cuts, improvement: {}", 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();
|
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);
|
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;
|
last_improve_step = LastStep::Horizontal;
|
||||||
}
|
}
|
||||||
eprintln!("Finished horizontal cuts, improvement: {}", last_improve_step == LastStep::Horizontal);
|
eprintln!("Finished horizontal cuts, improvement: {}", last_improve_step == LastStep::Horizontal);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::city;
|
use crate::city;
|
||||||
use crate::db::{LayoutDB, SavedLayout};
|
use crate::db::{LayoutDB, SavedLayout, MergeLowerBound};
|
||||||
use crate::city::{City, House, SIZE};
|
use crate::city::{City, House, SIZE};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use itertools::iproduct;
|
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 improved = false;
|
||||||
|
|
||||||
let mut lefts = Vec::new();
|
let mut lefts = Vec::new();
|
||||||
let mut rights = Vec::new();
|
let mut rights = Vec::new();
|
||||||
|
|
||||||
for left_layout in left_layouts {
|
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
|
// 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();
|
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 {
|
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 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();
|
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" };
|
let axis = if transposed { "y" } else { "x" };
|
||||||
|
|
||||||
// x is the last left coordinate, x+1 is right
|
// 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())
|
let pairs = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate())
|
||||||
.filter(|((left_i, _), (right_i, _))| left_i != right_i)
|
.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))
|
.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));
|
.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);
|
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
|
improved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
94
src/db.rs
94
src/db.rs
|
@ -4,13 +4,43 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct LayoutDB {
|
pub struct LayoutDB {
|
||||||
connection: Connection,
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct SavedLayout {
|
pub struct SavedLayout {
|
||||||
id: usize,
|
id: usize,
|
||||||
houses: Vec<House>
|
houses: Vec<House>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SavedLayout {
|
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)|
|
let layouts = layouts.into_iter().map(|(id, xy_pairs)|
|
||||||
SavedLayout
|
SavedLayout
|
||||||
{
|
{
|
||||||
|
@ -53,7 +102,7 @@ impl LayoutDB {
|
||||||
}
|
}
|
||||||
).collect();
|
).collect();
|
||||||
|
|
||||||
Ok(LayoutDB { connection, layouts })
|
Ok(LayoutDB { connection, layouts, merge_lower_bounds: merges })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layouts(&self) -> &Vec<SavedLayout> {
|
pub fn layouts(&self) -> &Vec<SavedLayout> {
|
||||||
|
@ -70,7 +119,44 @@ impl LayoutDB {
|
||||||
params![layout_id, house.x as u32, house.y as u32])?;
|
params![layout_id, house.x as u32, house.y as u32])?;
|
||||||
}
|
}
|
||||||
transaction.commit()?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue