diff --git a/src/optimize-subcity.rs b/src/optimize-subcity.rs index c45509b..926f061 100644 --- a/src/optimize-subcity.rs +++ b/src/optimize-subcity.rs @@ -25,10 +25,32 @@ fn main() { .next().expect("No best layout found"); eprintln!("Found best layout, ID {}, price {}", best_layout.id(), get_price(&city, best_layout.houses())); - let x_range = 5533..=12000; - let y_range = 4750..=12500; - //let x_range = 5533..=8000; - //let y_range = 4750..=8000; + //let x_range = 5533..=12000; + //let y_range = 4750..=12500; + //let x_range = 9500..=14100; + //let y_range = 10300..=14400; + //let x_range = 14900..=16200; + //let y_range = 7720..=10400; + //let x_range = 9982..=16383; + //let y_range = 0..=2720; + //let x_range = 7720..=12000; + //let y_range = 5608..=11000; + //let x_range = 3542..=8423; + //let y_range = 4832..=8423; + //let x_range = 1200..=9200; + //let y_range = 9000..=16383; + //let x_range = 9200..=16383; + //let y_range = 9200..=16383; + //let x_range = 9201..=16383; + //let y_range = 0..=7500; + //let x_range = 11100..=13333; + //let y_range = 14700..=16383; + //let x_range = 0..=7500; + //let y_range = 0..=7500; + let x_range = 0..=7500; + let y_range = 9500..=16383; + //let x_range = 9500..=16383; + //let y_range = 0..=7500; eprintln!("X {}-{}, Y {}-{}", x_range.start(), x_range.end(), y_range.start(), y_range.end()); let static_houses: Vec = best_layout.houses().iter() @@ -56,11 +78,21 @@ fn main() { //let mut subcity_db = MemoryLayoutDB::new(); let filename = format!("X{}_{}Y{}_{}ID{}.sqlite", x_range.start(), x_range.end(), y_range.start(), y_range.end(), best_layout.id()); - let mut subcity_db = SqliteLayoutDB::from_file(&filename).unwrap(); + let mut subcity_db = SqliteLayoutDB::from_file(&filename, true).unwrap(); + + // The following part merges best subcity layouts back into the global layout and + // inserting them into the database + + // Setting this value allows attempts at improving suboptimal subcities after merging them back. + // A subcity may result in a better score than original even if the subcity is worse than the + // original if global improvements improve the cost. + //const SUBOPTIMAL_SUBCITY_COST_MARGIN: u32 = 5000; + //const CHECKED_CITY_LIMIT: usize = 50; //for layout in subcity_db.layouts().iter() - // .filter(|x| get_price(subcity.city(), x.houses()) < removed_price) - // .sorted_by(|x, y| get_price(subcity.city(), x.houses()).cmp(&get_price(subcity.city(), y.houses()))) { + // .filter(|x| get_price(subcity.city(), x.houses()) < removed_price + SUBOPTIMAL_SUBCITY_COST_MARGIN) + // .sorted_by(|x, y| get_price(subcity.city(), x.houses()).cmp(&get_price(subcity.city(), y.houses()))) + // .take(CHECKED_CITY_LIMIT) { // let price = get_price(subcity.city(), layout.houses()); // let mut full_houses = subcity.to_full_houses(layout.houses()); // assert!(city::is_valid(&city, &full_houses).is_some()); @@ -71,7 +103,7 @@ fn main() { // } // let seed: u64 = thread_rng().gen(); // let mut rng = StdRng::seed_from_u64(seed); - // optimization::iterate_improvements(&mut house_layout, &mut rng, true); + // optimization::iterate_improvements(&mut house_layout, &mut rng, true, false); // eprintln!("Improvements finished"); // assert!(house_layout.is_valid()); // let improved_price = city::get_price(&city, house_layout.houses()); @@ -82,9 +114,17 @@ fn main() { // assert!(city::is_valid(&city, &full_houses).is_some()); // println!("Layout {}, price {}, full price {}", layout.id(), price, city::get_price(&city, &full_houses)); - // // Be careful with duplicates here - // //sqlite_db.add_layout(&full_houses, true); - // //println!("Inserted into the global DB"); + // if improved_price < city::get_price(&city, best_layout.houses()) { + // eprintln!("Found a new best layout for the full city"); + // println!("Found a new best layout for the full city"); + // println!("Layout {}, price {}, full price {}", layout.id(), price, city::get_price(&city, &full_houses)); + // } + // //eprintln!("NOT inserting into the DB"); + // // Be careful with duplicates here + // sqlite_db.add_layout(&full_houses, true); + // eprintln!("Inserted into the global DB"); + // println!("Inserted into the global DB"); + // //} //} //return; @@ -96,12 +136,16 @@ fn main() { // Generate `WEIGHTED_RANDOM_LAYOUTS` layouts weighted by scores from existing layouts // } - const FULL_RANDOM_LAYOUTS: usize = 100; - const DB_CHOICE_PROBABILITY: f64 = 0.90; - const WEIGHTED_RANDOM_LAYOUTS: usize = 200; + const FULL_RANDOM_LAYOUTS: usize = 200; + const DB_CHOICE_PROBABILITY: f64 = 0.95; + const WEIGHTED_RANDOM_LAYOUTS: usize = 100; const CUT_COMBINE_TOP_LAYOUTS: usize = 500; const IGNORED_WEIGHT_RATIO: f64 = 0.5; + const THREAD_COUNT: usize = 12; + + rayon::ThreadPoolBuilder::new().num_threads(THREAD_COUNT).build_global(); + if subcity_db.layouts().len() == 0 { let mut full_random_layouts = Vec::new(); @@ -119,7 +163,7 @@ fn main() { })); for houses in &full_random_layouts { - subcity_db.add_layout(houses, true); + subcity_db.add_layout(houses, true).expect("Failed to add new layout"); } eprintln!("Finished initial full random population"); @@ -127,9 +171,13 @@ fn main() { eprintln!("Skipping initial full random population because DB is non-empty [{} layouts]", subcity_db.layouts().len()); } - let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); - let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); - eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len()); + fn print_status(db: &TDB, subcity: &City) { + let best_price: u32 = db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.get_price(*h) as u32).sum()).min().unwrap(); + let worst_price: u32 = db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.get_price(*h) as u32).sum()).max().unwrap(); + eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, db.layouts().len()); + } + + print_status(&subcity_db, subcity.city()); let mut cache = combine::CompatibilityCache::new(); @@ -138,10 +186,8 @@ fn main() { // This is a bottleneck when it comes to multithreading, it only runs on a single thread combine::iterate_combines(&mut subcity_db, CUT_COMBINE_TOP_LAYOUTS, subcity.city(), &mut cache, false); - let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); - let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); eprintln!("Finished cut combines"); - eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len()); + print_status(&subcity_db, subcity.city()); let mut weighted_random_layouts = Vec::new(); // Only using the underlying memory-based DB is required here as the SqliteLayoutDB @@ -152,25 +198,25 @@ fn main() { let mut rng = StdRng::seed_from_u64(seed); let mut layout = HouseLayout::new(subcity.city()); + let best_price: u32 = memory_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); + let worst_price: u32 = memory_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); let price_range = worst_price - best_price; let max_price = best_price as f64 + (price_range as f64 * (1. - IGNORED_WEIGHT_RATIO)); //eprintln!("Starting random weighted population {}, using DB, score range {}-{}, DB use probability {}...", i, best_price, worst_price, DB_CHOICE_PROBABILITY); population::populate_using_db(&mut layout, &mut rng, memory_db, best_price as f64, max_price as f64, DB_CHOICE_PROBABILITY); //eprintln!("Finished random init {}, price: {}, houses: {}", i, layout.price(), layout.houses().len()); - optimization::iterate_improvements(&mut layout, &mut rng, false); + optimization::iterate_improvements(&mut layout, &mut rng, false, false); //eprintln!("Finished iterated improvements {}, price: {}, houses: {}", i, layout.price(), layout.houses().len()); layout.houses().clone() })); for houses in &weighted_random_layouts { - subcity_db.add_layout(houses, true); + subcity_db.add_layout(houses, true).expect("Failed to add new layout"); } let w_best: u32 = weighted_random_layouts.iter().map(|houses| houses.iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); let w_worst: u32 = weighted_random_layouts.iter().map(|houses| houses.iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); - let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); - let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); eprintln!("Finished weighted random population, price range {}-{}", w_best, w_worst); - eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len()); + print_status(&subcity_db, subcity.city()); } }