From 14a1be71b284cad753fd92afdbce495bfe562a57 Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Mon, 12 Aug 2024 13:08:49 +0200 Subject: [PATCH] examples: sync notebooks --- examples/notebook/algorithms/knapsack.ipynb | 2 +- .../algorithms/simple_knapsack_program.ipynb | 2 +- .../constraint_solver/cp_is_fun_cp.ipynb | 2 +- .../constraint_solver/nqueens_cp.ipynb | 2 +- .../constraint_solver/simple_cp_program.ipynb | 2 +- examples/notebook/contrib/3_jugs_mip.ipynb | 2 +- .../notebook/contrib/3_jugs_regular.ipynb | 2 +- .../notebook/contrib/a_round_of_golf.ipynb | 2 +- examples/notebook/contrib/all_interval.ipynb | 2 +- .../contrib/alldifferent_except_0.ipynb | 2 +- examples/notebook/contrib/alphametic.ipynb | 2 +- examples/notebook/contrib/assignment.ipynb | 2 +- .../notebook/contrib/assignment6_mip.ipynb | 2 +- examples/notebook/contrib/bacp.ipynb | 2 +- examples/notebook/contrib/blending.ipynb | 2 +- .../notebook/contrib/broken_weights.ipynb | 2 +- examples/notebook/contrib/bus_schedule.ipynb | 2 +- examples/notebook/contrib/car.ipynb | 2 +- .../notebook/contrib/check_dependencies.ipynb | 2 +- examples/notebook/contrib/circuit.ipynb | 2 +- examples/notebook/contrib/coins3.ipynb | 2 +- examples/notebook/contrib/coins_grid.ipynb | 2 +- .../notebook/contrib/coins_grid_mip.ipynb | 2 +- examples/notebook/contrib/coloring_ip.ipynb | 2 +- .../contrib/combinatorial_auction2.ipynb | 2 +- .../notebook/contrib/contiguity_regular.ipynb | 2 +- examples/notebook/contrib/costas_array.ipynb | 2 +- examples/notebook/contrib/covering_opl.ipynb | 2 +- examples/notebook/contrib/crew.ipynb | 2 +- examples/notebook/contrib/crossword2.ipynb | 2 +- examples/notebook/contrib/crypta.ipynb | 2 +- examples/notebook/contrib/crypto.ipynb | 2 +- .../contrib/curious_set_of_integers.ipynb | 2 +- .../notebook/contrib/debruijn_binary.ipynb | 2 +- examples/notebook/contrib/diet1.ipynb | 2 +- examples/notebook/contrib/diet1_b.ipynb | 2 +- examples/notebook/contrib/diet1_mip.ipynb | 2 +- .../contrib/discrete_tomography.ipynb | 2 +- .../contrib/divisible_by_9_through_1.ipynb | 2 +- examples/notebook/contrib/dudeney.ipynb | 2 +- examples/notebook/contrib/einav_puzzle.ipynb | 2 +- examples/notebook/contrib/einav_puzzle2.ipynb | 2 +- examples/notebook/contrib/eq10.ipynb | 2 +- examples/notebook/contrib/eq20.ipynb | 2 +- examples/notebook/contrib/fill_a_pix.ipynb | 2 +- .../notebook/contrib/furniture_moving.ipynb | 2 +- examples/notebook/contrib/futoshiki.ipynb | 2 +- .../notebook/contrib/game_theory_taha.ipynb | 2 +- examples/notebook/contrib/grocery.ipynb | 2 +- examples/notebook/contrib/hidato.ipynb | 2 +- .../notebook/contrib/just_forgotten.ipynb | 2 +- examples/notebook/contrib/kakuro.ipynb | 2 +- examples/notebook/contrib/kenken2.ipynb | 2 +- examples/notebook/contrib/killer_sudoku.ipynb | 2 +- examples/notebook/contrib/knapsack_cp.ipynb | 2 +- examples/notebook/contrib/knapsack_mip.ipynb | 2 +- examples/notebook/contrib/labeled_dice.ipynb | 2 +- examples/notebook/contrib/langford.ipynb | 2 +- examples/notebook/contrib/least_diff.ipynb | 2 +- examples/notebook/contrib/least_square.ipynb | 2 +- examples/notebook/contrib/lectures.ipynb | 2 +- .../notebook/contrib/magic_sequence_sat.ipynb | 2 +- examples/notebook/contrib/magic_square.ipynb | 2 +- .../contrib/magic_square_and_cards.ipynb | 2 +- .../notebook/contrib/magic_square_mip.ipynb | 2 +- examples/notebook/contrib/map.ipynb | 2 +- examples/notebook/contrib/marathon2.ipynb | 2 +- examples/notebook/contrib/max_flow_taha.ipynb | 2 +- .../notebook/contrib/max_flow_winston1.ipynb | 2 +- examples/notebook/contrib/minesweeper.ipynb | 2 +- examples/notebook/contrib/mr_smith.ipynb | 2 +- .../contrib/nonogram_default_search.ipynb | 2 +- .../notebook/contrib/nonogram_regular.ipynb | 2 +- .../notebook/contrib/nonogram_table.ipynb | 2 +- .../notebook/contrib/nonogram_table2.ipynb | 2 +- .../notebook/contrib/nontransitive_dice.ipynb | 2 +- examples/notebook/contrib/nqueens.ipynb | 2 +- examples/notebook/contrib/nqueens2.ipynb | 2 +- examples/notebook/contrib/nqueens3.ipynb | 2 +- .../notebook/contrib/nurse_rostering.ipynb | 2 +- examples/notebook/contrib/nurses_cp.ipynb | 2 +- examples/notebook/contrib/olympic.ipynb | 2 +- examples/notebook/contrib/organize_day.ipynb | 2 +- examples/notebook/contrib/p_median.ipynb | 2 +- .../notebook/contrib/pandigital_numbers.ipynb | 2 +- .../contrib/permutation_flow_shop.ipynb | 2 +- examples/notebook/contrib/photo_problem.ipynb | 2 +- .../contrib/place_number_puzzle.ipynb | 2 +- .../contrib/post_office_problem2.ipynb | 2 +- examples/notebook/contrib/production.ipynb | 2 +- .../contrib/project_scheduling_sat.ipynb | 2 +- examples/notebook/contrib/pyls_api.ipynb | 2 +- .../contrib/quasigroup_completion.ipynb | 2 +- examples/notebook/contrib/regular.ipynb | 2 +- examples/notebook/contrib/regular_table.ipynb | 2 +- .../notebook/contrib/regular_table2.ipynb | 2 +- examples/notebook/contrib/rogo2.ipynb | 2 +- .../contrib/rostering_with_travel.ipynb | 2 +- examples/notebook/contrib/safe_cracking.ipynb | 2 +- .../contrib/scheduling_speakers.ipynb | 2 +- .../scheduling_with_transitions_sat.ipynb | 2 +- .../contrib/school_scheduling_sat.ipynb | 2 +- examples/notebook/contrib/secret_santa.ipynb | 2 +- examples/notebook/contrib/secret_santa2.ipynb | 2 +- .../contrib/send_more_money_any_base.ipynb | 2 +- .../notebook/contrib/send_most_money.ipynb | 2 +- examples/notebook/contrib/seseman.ipynb | 2 +- examples/notebook/contrib/seseman_b.ipynb | 2 +- examples/notebook/contrib/set_covering.ipynb | 2 +- examples/notebook/contrib/set_covering2.ipynb | 2 +- examples/notebook/contrib/set_covering3.ipynb | 2 +- examples/notebook/contrib/set_covering4.ipynb | 2 +- .../contrib/set_covering_deployment.ipynb | 2 +- .../contrib/set_covering_skiena.ipynb | 2 +- examples/notebook/contrib/set_partition.ipynb | 2 +- .../notebook/contrib/sicherman_dice.ipynb | 2 +- .../notebook/contrib/ski_assignment.ipynb | 2 +- examples/notebook/contrib/slitherlink.ipynb | 2 +- .../contrib/sports_schedule_sat.ipynb | 2 +- .../notebook/contrib/stable_marriage.ipynb | 2 +- .../contrib/stable_marriage_sat.ipynb | 2 +- examples/notebook/contrib/steel.ipynb | 2 +- examples/notebook/contrib/steel_lns.ipynb | 2 +- .../notebook/contrib/stigler_contrib.ipynb | 2 +- examples/notebook/contrib/strimko2.ipynb | 2 +- examples/notebook/contrib/subset_sum.ipynb | 2 +- examples/notebook/contrib/survo_puzzle.ipynb | 2 +- examples/notebook/contrib/toNum.ipynb | 2 +- .../notebook/contrib/traffic_lights.ipynb | 2 +- .../notebook/contrib/vendor_scheduling.ipynb | 2 +- examples/notebook/contrib/volsay.ipynb | 2 +- examples/notebook/contrib/volsay2.ipynb | 2 +- examples/notebook/contrib/volsay3.ipynb | 2 +- .../contrib/wedding_optimal_chart.ipynb | 2 +- .../notebook/contrib/who_killed_agatha.ipynb | 2 +- examples/notebook/contrib/xkcd.ipynb | 2 +- .../notebook/contrib/young_tableaux.ipynb | 2 +- examples/notebook/examples/appointments.ipynb | 2 +- .../examples/arc_flow_cutting_stock_sat.ipynb | 152 +- .../assignment_with_constraints_sat.ipynb | 30 +- .../notebook/examples/balance_group_sat.ipynb | 41 +- .../bus_driver_scheduling_flow_sat.ipynb | 3294 +++++++++-------- .../examples/bus_driver_scheduling_sat.ipynb | 2 +- .../examples/chemical_balance_lp.ipynb | 2 +- .../examples/chemical_balance_sat.ipynb | 38 +- .../notebook/examples/clustering_sat.ipynb | 4 +- .../examples/cover_rectangle_sat.ipynb | 2 +- .../notebook/examples/cryptarithm_sat.ipynb | 6 +- examples/notebook/examples/cvrptw_plot.ipynb | 2 +- .../examples/flexible_job_shop_sat.ipynb | 65 +- .../examples/gate_scheduling_sat.ipynb | 48 +- examples/notebook/examples/golomb8.ipynb | 7 +- examples/notebook/examples/golomb_sat.ipynb | 11 +- examples/notebook/examples/hidato_sat.ipynb | 21 +- .../examples/integer_programming.ipynb | 2 +- .../examples/jobshop_ft06_distance_sat.ipynb | 19 +- .../notebook/examples/jobshop_ft06_sat.ipynb | 10 +- .../jobshop_with_maintenance_sat.ipynb | 34 +- .../notebook/examples/knapsack_2d_sat.ipynb | 12 +- .../examples/line_balancing_sat.ipynb | 97 +- .../examples/linear_assignment_api.ipynb | 2 +- .../examples/linear_programming.ipynb | 2 +- .../examples/magic_sequence_distribute.ipynb | 2 +- .../examples/maximize_combinations_sat.ipynb | 149 + .../notebook/examples/maze_escape_sat.ipynb | 30 +- .../memory_layout_and_infeasibility_sat.ipynb | 14 +- .../no_wait_baking_scheduling_sat.ipynb | 36 +- examples/notebook/examples/nqueens_sat.ipynb | 20 +- .../notebook/examples/pell_equation_sat.ipynb | 141 + .../notebook/examples/pentominoes_sat.ipynb | 26 +- .../examples/prize_collecting_tsp.ipynb | 10 +- .../examples/prize_collecting_tsp_sat.ipynb | 2 +- .../examples/prize_collecting_vrp.ipynb | 10 +- .../examples/prize_collecting_vrp_sat.ipynb | 2 +- examples/notebook/examples/proto_solve.ipynb | 2 +- .../notebook/examples/pyflow_example.ipynb | 2 +- examples/notebook/examples/qubo_sat.ipynb | 2 +- examples/notebook/examples/random_tsp.ipynb | 10 +- examples/notebook/examples/rcpsp_sat.ipynb | 2 +- .../notebook/examples/reallocate_sat.ipynb | 39 +- .../examples/shift_scheduling_sat.ipynb | 31 +- ...ing_with_setup_release_due_dates_sat.ipynb | 39 +- .../notebook/examples/spread_robots_sat.ipynb | 4 +- .../examples/steel_mill_slab_sat.ipynb | 130 +- examples/notebook/examples/sudoku_sat.ipynb | 4 +- .../examples/task_allocation_sat.ipynb | 15 +- .../tasks_and_workers_assignment_sat.ipynb | 4 +- .../examples/test_scheduling_sat.ipynb | 251 ++ examples/notebook/examples/transit_time.ipynb | 2 +- examples/notebook/examples/tsp_sat.ipynb | 2 +- .../examples/vendor_scheduling_sat.ipynb | 4 +- .../examples/wedding_optimal_chart_sat.ipynb | 4 +- .../weighted_latency_problem_sat.ipynb | 4 +- examples/notebook/examples/zebra_sat.ipynb | 2 +- .../assignment_linear_sum_assignment.ipynb | 2 +- .../notebook/graph/assignment_min_flow.ipynb | 2 +- .../notebook/graph/balance_min_flow.ipynb | 2 +- .../graph/simple_max_flow_program.ipynb | 2 +- .../graph/simple_min_cost_flow_program.ipynb | 2 +- .../linear_solver/assignment_groups_mip.ipynb | 2 +- .../linear_solver/assignment_mb.ipynb | 2 +- .../linear_solver/assignment_mip.ipynb | 2 +- .../assignment_task_sizes_mip.ipynb | 2 +- .../linear_solver/assignment_teams_mip.ipynb | 2 +- .../linear_solver/basic_example.ipynb | 2 +- .../linear_solver/bin_packing_mb.ipynb | 2 +- .../linear_solver/bin_packing_mip.ipynb | 2 +- .../linear_solver/clone_model_mb.ipynb | 2 +- .../integer_programming_example.ipynb | 2 +- .../linear_programming_example.ipynb | 2 +- .../linear_solver/mip_var_array.ipynb | 2 +- .../linear_solver/multiple_knapsack_mip.ipynb | 2 +- .../linear_solver/simple_lp_program.ipynb | 2 +- .../linear_solver/simple_lp_program_mb.ipynb | 2 +- .../linear_solver/simple_mip_program.ipynb | 2 +- .../linear_solver/simple_mip_program_mb.ipynb | 2 +- .../notebook/linear_solver/stigler_diet.ipynb | 2 +- .../notebook/pdlp/simple_pdlp_program.ipynb | 2 +- examples/notebook/routing/cvrp_reload.ipynb | 2 +- examples/notebook/routing/cvrptw.ipynb | 334 ++ examples/notebook/routing/cvrptw_break.ipynb | 2 +- .../routing/simple_routing_program.ipynb | 2 +- examples/notebook/routing/tsp.ipynb | 2 +- .../notebook/routing/tsp_circuit_board.ipynb | 4 +- examples/notebook/routing/tsp_cities.ipynb | 4 +- .../routing/tsp_distance_matrix.ipynb | 2 +- examples/notebook/routing/tsp_legacy.ipynb | 2 +- examples/notebook/routing/vrp.ipynb | 3 +- examples/notebook/routing/vrp_breaks.ipynb | 2 +- .../routing/vrp_breaks_from_start.ipynb | 2 +- examples/notebook/routing/vrp_capacity.ipynb | 2 +- .../notebook/routing/vrp_drop_nodes.ipynb | 2 +- .../notebook/routing/vrp_global_span.ipynb | 3 +- .../notebook/routing/vrp_initial_routes.ipynb | 3 +- .../routing/vrp_items_to_deliver.ipynb | 2 +- examples/notebook/routing/vrp_node_max.ipynb | 4 +- .../notebook/routing/vrp_nodes_indices.ipynb | 2 +- .../routing/vrp_pickup_delivery.ipynb | 2 +- .../routing/vrp_pickup_delivery_fifo.ipynb | 2 +- .../routing/vrp_pickup_delivery_lifo.ipynb | 2 +- examples/notebook/routing/vrp_resources.ipynb | 2 +- .../routing/vrp_solution_callback.ipynb | 2 +- .../notebook/routing/vrp_starts_ends.ipynb | 2 +- .../notebook/routing/vrp_time_windows.ipynb | 2 +- .../vrp_time_windows_per_vehicles.ipynb | 2 +- examples/notebook/routing/vrp_tokens.ipynb | 2 +- .../routing/vrp_with_time_limit.ipynb | 2 +- .../routing/vrptw_store_solution_data.ipynb | 2 +- ...all_different_except_zero_sample_sat.ipynb | 2 +- .../notebook/sat/assignment_groups_sat.ipynb | 2 +- examples/notebook/sat/assignment_sat.ipynb | 2 +- .../sat/assignment_task_sizes_sat.ipynb | 2 +- .../notebook/sat/assignment_teams_sat.ipynb | 2 +- .../notebook/sat/assumptions_sample_sat.ipynb | 2 +- examples/notebook/sat/bin_packing_sat.ipynb | 2 +- .../notebook/sat/binpacking_problem_sat.ipynb | 2 +- .../bool_and_int_var_product_sample_sat.ipynb | 2 +- .../notebook/sat/bool_or_sample_sat.ipynb | 2 +- .../sat/boolean_product_sample_sat.ipynb | 2 +- .../notebook/sat/channeling_sample_sat.ipynb | 2 +- .../notebook/sat/clone_model_sample_sat.ipynb | 2 +- examples/notebook/sat/cp_is_fun_sat.ipynb | 2 +- examples/notebook/sat/cp_sat_example.ipynb | 2 +- ...mulative_variable_profile_sample_sat.ipynb | 2 +- .../earliness_tardiness_cost_sample_sat.ipynb | 2 +- .../index_first_boolvar_true_sample_sat.ipynb | 2 +- .../sat/interval_relations_sample_sat.ipynb | 156 + .../notebook/sat/interval_sample_sat.ipynb | 2 +- .../notebook/sat/literal_sample_sat.ipynb | 2 +- .../notebook/sat/minimal_jobshop_sat.ipynb | 2 +- .../notebook/sat/multiple_knapsack_sat.ipynb | 2 +- .../notebook/sat/no_overlap_sample_sat.ipynb | 2 +- examples/notebook/sat/non_linear_sat.ipynb | 2 +- examples/notebook/sat/nqueens_sat.ipynb | 2 +- examples/notebook/sat/nurses_sat.ipynb | 2 +- .../sat/optional_interval_sample_sat.ipynb | 2 +- .../overlapping_intervals_sample_sat.ipynb | 2 +- .../sat/rabbits_and_pheasants_sat.ipynb | 2 +- .../sat/ranking_circuit_sample_sat.ipynb | 8 +- .../notebook/sat/ranking_sample_sat.ipynb | 2 +- .../notebook/sat/reified_sample_sat.ipynb | 2 +- .../notebook/sat/schedule_requests_sat.ipynb | 2 +- .../scheduling_with_calendar_sample_sat.ipynb | 2 +- .../search_for_all_solutions_sample_sat.ipynb | 2 +- .../notebook/sat/simple_sat_program.ipynb | 2 +- .../sat/solution_hinting_sample_sat.ipynb | 2 +- ...nt_intermediate_solutions_sample_sat.ipynb | 2 +- .../solve_with_time_limit_sample_sat.ipynb | 2 +- .../sat/step_function_sample_sat.ipynb | 2 +- .../stop_after_n_solutions_sample_sat.ipynb | 2 +- ...transitions_in_no_overlap_sample_sat.ipynb | 280 ++ 291 files changed, 3798 insertions(+), 2349 deletions(-) create mode 100644 examples/notebook/examples/maximize_combinations_sat.ipynb create mode 100644 examples/notebook/examples/pell_equation_sat.ipynb create mode 100644 examples/notebook/examples/test_scheduling_sat.ipynb create mode 100644 examples/notebook/routing/cvrptw.ipynb create mode 100644 examples/notebook/sat/interval_relations_sample_sat.ipynb create mode 100644 examples/notebook/sat/transitions_in_no_overlap_sample_sat.ipynb diff --git a/examples/notebook/algorithms/knapsack.ipynb b/examples/notebook/algorithms/knapsack.ipynb index d0909076d19..9d430ded3b7 100644 --- a/examples/notebook/algorithms/knapsack.ipynb +++ b/examples/notebook/algorithms/knapsack.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/algorithms/simple_knapsack_program.ipynb b/examples/notebook/algorithms/simple_knapsack_program.ipynb index eefae5c088c..2db82375a3b 100644 --- a/examples/notebook/algorithms/simple_knapsack_program.ipynb +++ b/examples/notebook/algorithms/simple_knapsack_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/constraint_solver/cp_is_fun_cp.ipynb b/examples/notebook/constraint_solver/cp_is_fun_cp.ipynb index eccca4c8303..bbddc10de84 100644 --- a/examples/notebook/constraint_solver/cp_is_fun_cp.ipynb +++ b/examples/notebook/constraint_solver/cp_is_fun_cp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/constraint_solver/nqueens_cp.ipynb b/examples/notebook/constraint_solver/nqueens_cp.ipynb index 49b80d9f05b..52e67137ae6 100644 --- a/examples/notebook/constraint_solver/nqueens_cp.ipynb +++ b/examples/notebook/constraint_solver/nqueens_cp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/constraint_solver/simple_cp_program.ipynb b/examples/notebook/constraint_solver/simple_cp_program.ipynb index 93ab4f5c145..e57f62343fa 100644 --- a/examples/notebook/constraint_solver/simple_cp_program.ipynb +++ b/examples/notebook/constraint_solver/simple_cp_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/3_jugs_mip.ipynb b/examples/notebook/contrib/3_jugs_mip.ipynb index ee8a8251b88..bc0c4ce2436 100644 --- a/examples/notebook/contrib/3_jugs_mip.ipynb +++ b/examples/notebook/contrib/3_jugs_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/3_jugs_regular.ipynb b/examples/notebook/contrib/3_jugs_regular.ipynb index d09dc39bfcd..4b5cdeeba01 100644 --- a/examples/notebook/contrib/3_jugs_regular.ipynb +++ b/examples/notebook/contrib/3_jugs_regular.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/a_round_of_golf.ipynb b/examples/notebook/contrib/a_round_of_golf.ipynb index f661c46724a..53e4401a64f 100644 --- a/examples/notebook/contrib/a_round_of_golf.ipynb +++ b/examples/notebook/contrib/a_round_of_golf.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/all_interval.ipynb b/examples/notebook/contrib/all_interval.ipynb index 6238de5ca93..54070f36622 100644 --- a/examples/notebook/contrib/all_interval.ipynb +++ b/examples/notebook/contrib/all_interval.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/alldifferent_except_0.ipynb b/examples/notebook/contrib/alldifferent_except_0.ipynb index 287ca64cd1a..a4b7f0ef4f8 100644 --- a/examples/notebook/contrib/alldifferent_except_0.ipynb +++ b/examples/notebook/contrib/alldifferent_except_0.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/alphametic.ipynb b/examples/notebook/contrib/alphametic.ipynb index d6184c59a8d..da69bc36e72 100644 --- a/examples/notebook/contrib/alphametic.ipynb +++ b/examples/notebook/contrib/alphametic.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/assignment.ipynb b/examples/notebook/contrib/assignment.ipynb index 8bc379abec7..5c6c316bf74 100644 --- a/examples/notebook/contrib/assignment.ipynb +++ b/examples/notebook/contrib/assignment.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/assignment6_mip.ipynb b/examples/notebook/contrib/assignment6_mip.ipynb index 1fbb2d5243a..81b7a831cff 100644 --- a/examples/notebook/contrib/assignment6_mip.ipynb +++ b/examples/notebook/contrib/assignment6_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/bacp.ipynb b/examples/notebook/contrib/bacp.ipynb index 82ff8d3542f..f7e01f2a3d1 100644 --- a/examples/notebook/contrib/bacp.ipynb +++ b/examples/notebook/contrib/bacp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/blending.ipynb b/examples/notebook/contrib/blending.ipynb index 4be44edc8b9..8f999b1e98a 100644 --- a/examples/notebook/contrib/blending.ipynb +++ b/examples/notebook/contrib/blending.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/broken_weights.ipynb b/examples/notebook/contrib/broken_weights.ipynb index 8c3e1f44600..641f2dbbace 100644 --- a/examples/notebook/contrib/broken_weights.ipynb +++ b/examples/notebook/contrib/broken_weights.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/bus_schedule.ipynb b/examples/notebook/contrib/bus_schedule.ipynb index eb98ba762a2..d10280c6075 100644 --- a/examples/notebook/contrib/bus_schedule.ipynb +++ b/examples/notebook/contrib/bus_schedule.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/car.ipynb b/examples/notebook/contrib/car.ipynb index 7933e3f9dec..f2582c5aafd 100644 --- a/examples/notebook/contrib/car.ipynb +++ b/examples/notebook/contrib/car.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/check_dependencies.ipynb b/examples/notebook/contrib/check_dependencies.ipynb index e5c203bb299..e7460d693fa 100644 --- a/examples/notebook/contrib/check_dependencies.ipynb +++ b/examples/notebook/contrib/check_dependencies.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/circuit.ipynb b/examples/notebook/contrib/circuit.ipynb index 4dc7eec26a7..5c0d3b996de 100644 --- a/examples/notebook/contrib/circuit.ipynb +++ b/examples/notebook/contrib/circuit.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/coins3.ipynb b/examples/notebook/contrib/coins3.ipynb index f22afa819a2..2acc61bf25f 100644 --- a/examples/notebook/contrib/coins3.ipynb +++ b/examples/notebook/contrib/coins3.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/coins_grid.ipynb b/examples/notebook/contrib/coins_grid.ipynb index 9d5286172fc..25ac1f2ef82 100644 --- a/examples/notebook/contrib/coins_grid.ipynb +++ b/examples/notebook/contrib/coins_grid.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/coins_grid_mip.ipynb b/examples/notebook/contrib/coins_grid_mip.ipynb index f0a146e73d7..89be73fe5fa 100644 --- a/examples/notebook/contrib/coins_grid_mip.ipynb +++ b/examples/notebook/contrib/coins_grid_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/coloring_ip.ipynb b/examples/notebook/contrib/coloring_ip.ipynb index bebf939dca9..0c4733271b8 100644 --- a/examples/notebook/contrib/coloring_ip.ipynb +++ b/examples/notebook/contrib/coloring_ip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/combinatorial_auction2.ipynb b/examples/notebook/contrib/combinatorial_auction2.ipynb index 3513d029b59..fde253c5005 100644 --- a/examples/notebook/contrib/combinatorial_auction2.ipynb +++ b/examples/notebook/contrib/combinatorial_auction2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/contiguity_regular.ipynb b/examples/notebook/contrib/contiguity_regular.ipynb index 0ba85444d27..33011747c64 100644 --- a/examples/notebook/contrib/contiguity_regular.ipynb +++ b/examples/notebook/contrib/contiguity_regular.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/costas_array.ipynb b/examples/notebook/contrib/costas_array.ipynb index 71ee63633df..a14993b9c9a 100644 --- a/examples/notebook/contrib/costas_array.ipynb +++ b/examples/notebook/contrib/costas_array.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/covering_opl.ipynb b/examples/notebook/contrib/covering_opl.ipynb index 26dd09df982..45024be9d34 100644 --- a/examples/notebook/contrib/covering_opl.ipynb +++ b/examples/notebook/contrib/covering_opl.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/crew.ipynb b/examples/notebook/contrib/crew.ipynb index a9434f07e43..cff12bc40e3 100644 --- a/examples/notebook/contrib/crew.ipynb +++ b/examples/notebook/contrib/crew.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/crossword2.ipynb b/examples/notebook/contrib/crossword2.ipynb index 9b1d5bec3d8..3bced776ea9 100644 --- a/examples/notebook/contrib/crossword2.ipynb +++ b/examples/notebook/contrib/crossword2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/crypta.ipynb b/examples/notebook/contrib/crypta.ipynb index ab01f2af06f..7c659f09bd6 100644 --- a/examples/notebook/contrib/crypta.ipynb +++ b/examples/notebook/contrib/crypta.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/crypto.ipynb b/examples/notebook/contrib/crypto.ipynb index 1fb774b8a71..8a4c505b752 100644 --- a/examples/notebook/contrib/crypto.ipynb +++ b/examples/notebook/contrib/crypto.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/curious_set_of_integers.ipynb b/examples/notebook/contrib/curious_set_of_integers.ipynb index 9e3549413e8..882151fc2e7 100644 --- a/examples/notebook/contrib/curious_set_of_integers.ipynb +++ b/examples/notebook/contrib/curious_set_of_integers.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/debruijn_binary.ipynb b/examples/notebook/contrib/debruijn_binary.ipynb index 55b7012b6ae..820c777dba7 100644 --- a/examples/notebook/contrib/debruijn_binary.ipynb +++ b/examples/notebook/contrib/debruijn_binary.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/diet1.ipynb b/examples/notebook/contrib/diet1.ipynb index 583e6008954..948da29917d 100644 --- a/examples/notebook/contrib/diet1.ipynb +++ b/examples/notebook/contrib/diet1.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/diet1_b.ipynb b/examples/notebook/contrib/diet1_b.ipynb index 398efbbbd16..b0c692160b6 100644 --- a/examples/notebook/contrib/diet1_b.ipynb +++ b/examples/notebook/contrib/diet1_b.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/diet1_mip.ipynb b/examples/notebook/contrib/diet1_mip.ipynb index a272b6741e8..ba426559fd1 100644 --- a/examples/notebook/contrib/diet1_mip.ipynb +++ b/examples/notebook/contrib/diet1_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/discrete_tomography.ipynb b/examples/notebook/contrib/discrete_tomography.ipynb index 702065109ba..551757a2606 100644 --- a/examples/notebook/contrib/discrete_tomography.ipynb +++ b/examples/notebook/contrib/discrete_tomography.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/divisible_by_9_through_1.ipynb b/examples/notebook/contrib/divisible_by_9_through_1.ipynb index 5c3fa505a4c..e4df8c6810e 100644 --- a/examples/notebook/contrib/divisible_by_9_through_1.ipynb +++ b/examples/notebook/contrib/divisible_by_9_through_1.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/dudeney.ipynb b/examples/notebook/contrib/dudeney.ipynb index a06fa46c92e..841542197d2 100644 --- a/examples/notebook/contrib/dudeney.ipynb +++ b/examples/notebook/contrib/dudeney.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/einav_puzzle.ipynb b/examples/notebook/contrib/einav_puzzle.ipynb index 1e561ba719d..fb10e72c6f7 100644 --- a/examples/notebook/contrib/einav_puzzle.ipynb +++ b/examples/notebook/contrib/einav_puzzle.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/einav_puzzle2.ipynb b/examples/notebook/contrib/einav_puzzle2.ipynb index cf73b64ec74..1c14453eb24 100644 --- a/examples/notebook/contrib/einav_puzzle2.ipynb +++ b/examples/notebook/contrib/einav_puzzle2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/eq10.ipynb b/examples/notebook/contrib/eq10.ipynb index 8d73138164a..59bab8f0572 100644 --- a/examples/notebook/contrib/eq10.ipynb +++ b/examples/notebook/contrib/eq10.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/eq20.ipynb b/examples/notebook/contrib/eq20.ipynb index 14d43152475..c9fc2dabd63 100644 --- a/examples/notebook/contrib/eq20.ipynb +++ b/examples/notebook/contrib/eq20.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/fill_a_pix.ipynb b/examples/notebook/contrib/fill_a_pix.ipynb index 91a8780ac60..88f31c747b0 100644 --- a/examples/notebook/contrib/fill_a_pix.ipynb +++ b/examples/notebook/contrib/fill_a_pix.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/furniture_moving.ipynb b/examples/notebook/contrib/furniture_moving.ipynb index 71ff8a961ac..eb3b35036ed 100644 --- a/examples/notebook/contrib/furniture_moving.ipynb +++ b/examples/notebook/contrib/furniture_moving.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/futoshiki.ipynb b/examples/notebook/contrib/futoshiki.ipynb index 00719e963bf..9789b29beec 100644 --- a/examples/notebook/contrib/futoshiki.ipynb +++ b/examples/notebook/contrib/futoshiki.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/game_theory_taha.ipynb b/examples/notebook/contrib/game_theory_taha.ipynb index d24c0a2f3aa..3de60774aa1 100644 --- a/examples/notebook/contrib/game_theory_taha.ipynb +++ b/examples/notebook/contrib/game_theory_taha.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/grocery.ipynb b/examples/notebook/contrib/grocery.ipynb index 7c342809f09..2fdfbb61d13 100644 --- a/examples/notebook/contrib/grocery.ipynb +++ b/examples/notebook/contrib/grocery.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/hidato.ipynb b/examples/notebook/contrib/hidato.ipynb index 7443716a855..942aa784280 100644 --- a/examples/notebook/contrib/hidato.ipynb +++ b/examples/notebook/contrib/hidato.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/just_forgotten.ipynb b/examples/notebook/contrib/just_forgotten.ipynb index d21153566d7..888f3163f26 100644 --- a/examples/notebook/contrib/just_forgotten.ipynb +++ b/examples/notebook/contrib/just_forgotten.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/kakuro.ipynb b/examples/notebook/contrib/kakuro.ipynb index 7d2ee5b163e..fa524843b38 100644 --- a/examples/notebook/contrib/kakuro.ipynb +++ b/examples/notebook/contrib/kakuro.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/kenken2.ipynb b/examples/notebook/contrib/kenken2.ipynb index 156c4628d0a..eace922e2ef 100644 --- a/examples/notebook/contrib/kenken2.ipynb +++ b/examples/notebook/contrib/kenken2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/killer_sudoku.ipynb b/examples/notebook/contrib/killer_sudoku.ipynb index d4b450e1003..7b8e84e06df 100644 --- a/examples/notebook/contrib/killer_sudoku.ipynb +++ b/examples/notebook/contrib/killer_sudoku.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/knapsack_cp.ipynb b/examples/notebook/contrib/knapsack_cp.ipynb index 79c306fce8c..f4c10df6c23 100644 --- a/examples/notebook/contrib/knapsack_cp.ipynb +++ b/examples/notebook/contrib/knapsack_cp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/knapsack_mip.ipynb b/examples/notebook/contrib/knapsack_mip.ipynb index ee9d0c12efe..7e3531fb978 100644 --- a/examples/notebook/contrib/knapsack_mip.ipynb +++ b/examples/notebook/contrib/knapsack_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/labeled_dice.ipynb b/examples/notebook/contrib/labeled_dice.ipynb index e7db02b0a7a..b1c3345a498 100644 --- a/examples/notebook/contrib/labeled_dice.ipynb +++ b/examples/notebook/contrib/labeled_dice.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/langford.ipynb b/examples/notebook/contrib/langford.ipynb index 6ac7a6083ae..1ce419ecd29 100644 --- a/examples/notebook/contrib/langford.ipynb +++ b/examples/notebook/contrib/langford.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/least_diff.ipynb b/examples/notebook/contrib/least_diff.ipynb index e41126ba4ed..34ac0ff0168 100644 --- a/examples/notebook/contrib/least_diff.ipynb +++ b/examples/notebook/contrib/least_diff.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/least_square.ipynb b/examples/notebook/contrib/least_square.ipynb index e9d11d9d010..469d07772d4 100644 --- a/examples/notebook/contrib/least_square.ipynb +++ b/examples/notebook/contrib/least_square.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/lectures.ipynb b/examples/notebook/contrib/lectures.ipynb index a17cfbb0c02..dc7c4c43549 100644 --- a/examples/notebook/contrib/lectures.ipynb +++ b/examples/notebook/contrib/lectures.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/magic_sequence_sat.ipynb b/examples/notebook/contrib/magic_sequence_sat.ipynb index d082364a40f..1093abee03a 100644 --- a/examples/notebook/contrib/magic_sequence_sat.ipynb +++ b/examples/notebook/contrib/magic_sequence_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/magic_square.ipynb b/examples/notebook/contrib/magic_square.ipynb index 8d4cf7bb095..e33cf541211 100644 --- a/examples/notebook/contrib/magic_square.ipynb +++ b/examples/notebook/contrib/magic_square.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/magic_square_and_cards.ipynb b/examples/notebook/contrib/magic_square_and_cards.ipynb index 6bc65edccc8..feb9bee0df6 100644 --- a/examples/notebook/contrib/magic_square_and_cards.ipynb +++ b/examples/notebook/contrib/magic_square_and_cards.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/magic_square_mip.ipynb b/examples/notebook/contrib/magic_square_mip.ipynb index 3b4ada67791..7dd7edd998d 100644 --- a/examples/notebook/contrib/magic_square_mip.ipynb +++ b/examples/notebook/contrib/magic_square_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/map.ipynb b/examples/notebook/contrib/map.ipynb index df84f637ef5..6f383791cfd 100644 --- a/examples/notebook/contrib/map.ipynb +++ b/examples/notebook/contrib/map.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/marathon2.ipynb b/examples/notebook/contrib/marathon2.ipynb index 6d351846520..d3581cd2f91 100644 --- a/examples/notebook/contrib/marathon2.ipynb +++ b/examples/notebook/contrib/marathon2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/max_flow_taha.ipynb b/examples/notebook/contrib/max_flow_taha.ipynb index d52cf2acecc..79139584908 100644 --- a/examples/notebook/contrib/max_flow_taha.ipynb +++ b/examples/notebook/contrib/max_flow_taha.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/max_flow_winston1.ipynb b/examples/notebook/contrib/max_flow_winston1.ipynb index 4eb9f3dea2f..462d0999f3e 100644 --- a/examples/notebook/contrib/max_flow_winston1.ipynb +++ b/examples/notebook/contrib/max_flow_winston1.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/minesweeper.ipynb b/examples/notebook/contrib/minesweeper.ipynb index 0e008b1c070..236b8cbc8cf 100644 --- a/examples/notebook/contrib/minesweeper.ipynb +++ b/examples/notebook/contrib/minesweeper.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/mr_smith.ipynb b/examples/notebook/contrib/mr_smith.ipynb index 76f57a97cb1..0767bedb005 100644 --- a/examples/notebook/contrib/mr_smith.ipynb +++ b/examples/notebook/contrib/mr_smith.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nonogram_default_search.ipynb b/examples/notebook/contrib/nonogram_default_search.ipynb index c9a0abf4c87..17665e777ac 100644 --- a/examples/notebook/contrib/nonogram_default_search.ipynb +++ b/examples/notebook/contrib/nonogram_default_search.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nonogram_regular.ipynb b/examples/notebook/contrib/nonogram_regular.ipynb index 12b3bc9ece2..01edff0a1e3 100644 --- a/examples/notebook/contrib/nonogram_regular.ipynb +++ b/examples/notebook/contrib/nonogram_regular.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nonogram_table.ipynb b/examples/notebook/contrib/nonogram_table.ipynb index aba767b93ec..0de937699cf 100644 --- a/examples/notebook/contrib/nonogram_table.ipynb +++ b/examples/notebook/contrib/nonogram_table.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nonogram_table2.ipynb b/examples/notebook/contrib/nonogram_table2.ipynb index ad25175beb0..d7dfa8fb54a 100644 --- a/examples/notebook/contrib/nonogram_table2.ipynb +++ b/examples/notebook/contrib/nonogram_table2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nontransitive_dice.ipynb b/examples/notebook/contrib/nontransitive_dice.ipynb index d5fa172d434..da3cd48537b 100644 --- a/examples/notebook/contrib/nontransitive_dice.ipynb +++ b/examples/notebook/contrib/nontransitive_dice.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nqueens.ipynb b/examples/notebook/contrib/nqueens.ipynb index b5ca63c2cf0..437a3de4079 100644 --- a/examples/notebook/contrib/nqueens.ipynb +++ b/examples/notebook/contrib/nqueens.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nqueens2.ipynb b/examples/notebook/contrib/nqueens2.ipynb index 43ebd964346..492c898c8c8 100644 --- a/examples/notebook/contrib/nqueens2.ipynb +++ b/examples/notebook/contrib/nqueens2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nqueens3.ipynb b/examples/notebook/contrib/nqueens3.ipynb index 373dbc126e8..d5b310fc082 100644 --- a/examples/notebook/contrib/nqueens3.ipynb +++ b/examples/notebook/contrib/nqueens3.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nurse_rostering.ipynb b/examples/notebook/contrib/nurse_rostering.ipynb index 3a0490f896c..ca1f438affa 100644 --- a/examples/notebook/contrib/nurse_rostering.ipynb +++ b/examples/notebook/contrib/nurse_rostering.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/nurses_cp.ipynb b/examples/notebook/contrib/nurses_cp.ipynb index 036eb4d3ab1..4247c3b9b39 100644 --- a/examples/notebook/contrib/nurses_cp.ipynb +++ b/examples/notebook/contrib/nurses_cp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/olympic.ipynb b/examples/notebook/contrib/olympic.ipynb index fc438a146df..4c7675c3f1a 100644 --- a/examples/notebook/contrib/olympic.ipynb +++ b/examples/notebook/contrib/olympic.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/organize_day.ipynb b/examples/notebook/contrib/organize_day.ipynb index 1cfd0e775da..78884447516 100644 --- a/examples/notebook/contrib/organize_day.ipynb +++ b/examples/notebook/contrib/organize_day.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/p_median.ipynb b/examples/notebook/contrib/p_median.ipynb index 14a71c3b9fc..937e78c03da 100644 --- a/examples/notebook/contrib/p_median.ipynb +++ b/examples/notebook/contrib/p_median.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/pandigital_numbers.ipynb b/examples/notebook/contrib/pandigital_numbers.ipynb index 3a2391e8cd5..bf4746b3e56 100644 --- a/examples/notebook/contrib/pandigital_numbers.ipynb +++ b/examples/notebook/contrib/pandigital_numbers.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/permutation_flow_shop.ipynb b/examples/notebook/contrib/permutation_flow_shop.ipynb index a17370930d7..2ff6db78898 100644 --- a/examples/notebook/contrib/permutation_flow_shop.ipynb +++ b/examples/notebook/contrib/permutation_flow_shop.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/photo_problem.ipynb b/examples/notebook/contrib/photo_problem.ipynb index 3c926c0d9e2..9a75a3413f4 100644 --- a/examples/notebook/contrib/photo_problem.ipynb +++ b/examples/notebook/contrib/photo_problem.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/place_number_puzzle.ipynb b/examples/notebook/contrib/place_number_puzzle.ipynb index f0e9472e4a9..8fc2cd1ba81 100644 --- a/examples/notebook/contrib/place_number_puzzle.ipynb +++ b/examples/notebook/contrib/place_number_puzzle.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/post_office_problem2.ipynb b/examples/notebook/contrib/post_office_problem2.ipynb index 7e4b3f90dd7..7271628b0ab 100644 --- a/examples/notebook/contrib/post_office_problem2.ipynb +++ b/examples/notebook/contrib/post_office_problem2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/production.ipynb b/examples/notebook/contrib/production.ipynb index 90a4ce14097..f450e585750 100644 --- a/examples/notebook/contrib/production.ipynb +++ b/examples/notebook/contrib/production.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/project_scheduling_sat.ipynb b/examples/notebook/contrib/project_scheduling_sat.ipynb index 1705b4dc4f5..328bbdc1349 100644 --- a/examples/notebook/contrib/project_scheduling_sat.ipynb +++ b/examples/notebook/contrib/project_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/pyls_api.ipynb b/examples/notebook/contrib/pyls_api.ipynb index f58f9c01dd7..f0d5503213b 100644 --- a/examples/notebook/contrib/pyls_api.ipynb +++ b/examples/notebook/contrib/pyls_api.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/quasigroup_completion.ipynb b/examples/notebook/contrib/quasigroup_completion.ipynb index c7e5a1c2e32..3a897295996 100644 --- a/examples/notebook/contrib/quasigroup_completion.ipynb +++ b/examples/notebook/contrib/quasigroup_completion.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/regular.ipynb b/examples/notebook/contrib/regular.ipynb index db879beecc4..1f53a18eb5a 100644 --- a/examples/notebook/contrib/regular.ipynb +++ b/examples/notebook/contrib/regular.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/regular_table.ipynb b/examples/notebook/contrib/regular_table.ipynb index 9f671a39450..bad77bc5fa8 100644 --- a/examples/notebook/contrib/regular_table.ipynb +++ b/examples/notebook/contrib/regular_table.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/regular_table2.ipynb b/examples/notebook/contrib/regular_table2.ipynb index 5df0798cadd..73b7fe76eeb 100644 --- a/examples/notebook/contrib/regular_table2.ipynb +++ b/examples/notebook/contrib/regular_table2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/rogo2.ipynb b/examples/notebook/contrib/rogo2.ipynb index 2acf87de4c9..9bae765dc53 100644 --- a/examples/notebook/contrib/rogo2.ipynb +++ b/examples/notebook/contrib/rogo2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/rostering_with_travel.ipynb b/examples/notebook/contrib/rostering_with_travel.ipynb index 794bc3e8a30..a5545bbf536 100644 --- a/examples/notebook/contrib/rostering_with_travel.ipynb +++ b/examples/notebook/contrib/rostering_with_travel.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/safe_cracking.ipynb b/examples/notebook/contrib/safe_cracking.ipynb index 8c2184de87e..4baaa5c1254 100644 --- a/examples/notebook/contrib/safe_cracking.ipynb +++ b/examples/notebook/contrib/safe_cracking.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/scheduling_speakers.ipynb b/examples/notebook/contrib/scheduling_speakers.ipynb index 29ec70d428f..8f3de342672 100644 --- a/examples/notebook/contrib/scheduling_speakers.ipynb +++ b/examples/notebook/contrib/scheduling_speakers.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/scheduling_with_transitions_sat.ipynb b/examples/notebook/contrib/scheduling_with_transitions_sat.ipynb index 16275fec822..b67470ee1d7 100644 --- a/examples/notebook/contrib/scheduling_with_transitions_sat.ipynb +++ b/examples/notebook/contrib/scheduling_with_transitions_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/school_scheduling_sat.ipynb b/examples/notebook/contrib/school_scheduling_sat.ipynb index bf5507e2c20..423ff950f07 100644 --- a/examples/notebook/contrib/school_scheduling_sat.ipynb +++ b/examples/notebook/contrib/school_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/secret_santa.ipynb b/examples/notebook/contrib/secret_santa.ipynb index 789a3884125..1997541e8ad 100644 --- a/examples/notebook/contrib/secret_santa.ipynb +++ b/examples/notebook/contrib/secret_santa.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/secret_santa2.ipynb b/examples/notebook/contrib/secret_santa2.ipynb index 63b73e38273..24314c8a4de 100644 --- a/examples/notebook/contrib/secret_santa2.ipynb +++ b/examples/notebook/contrib/secret_santa2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/send_more_money_any_base.ipynb b/examples/notebook/contrib/send_more_money_any_base.ipynb index 18ad26402d2..a3039842df8 100644 --- a/examples/notebook/contrib/send_more_money_any_base.ipynb +++ b/examples/notebook/contrib/send_more_money_any_base.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/send_most_money.ipynb b/examples/notebook/contrib/send_most_money.ipynb index 3cfc024843e..418e7dd237c 100644 --- a/examples/notebook/contrib/send_most_money.ipynb +++ b/examples/notebook/contrib/send_most_money.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/seseman.ipynb b/examples/notebook/contrib/seseman.ipynb index e4ba7742e7e..4b6d7a91518 100644 --- a/examples/notebook/contrib/seseman.ipynb +++ b/examples/notebook/contrib/seseman.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/seseman_b.ipynb b/examples/notebook/contrib/seseman_b.ipynb index 35bddea7f32..5d91bb53f81 100644 --- a/examples/notebook/contrib/seseman_b.ipynb +++ b/examples/notebook/contrib/seseman_b.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_covering.ipynb b/examples/notebook/contrib/set_covering.ipynb index 31123d37e75..88816144192 100644 --- a/examples/notebook/contrib/set_covering.ipynb +++ b/examples/notebook/contrib/set_covering.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_covering2.ipynb b/examples/notebook/contrib/set_covering2.ipynb index ee0af37f978..f68a1563794 100644 --- a/examples/notebook/contrib/set_covering2.ipynb +++ b/examples/notebook/contrib/set_covering2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_covering3.ipynb b/examples/notebook/contrib/set_covering3.ipynb index 99d5ce74812..2464fc62f5a 100644 --- a/examples/notebook/contrib/set_covering3.ipynb +++ b/examples/notebook/contrib/set_covering3.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_covering4.ipynb b/examples/notebook/contrib/set_covering4.ipynb index aedf1099b5f..30632d5cced 100644 --- a/examples/notebook/contrib/set_covering4.ipynb +++ b/examples/notebook/contrib/set_covering4.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_covering_deployment.ipynb b/examples/notebook/contrib/set_covering_deployment.ipynb index 779bc0b5567..bd6e6fed72b 100644 --- a/examples/notebook/contrib/set_covering_deployment.ipynb +++ b/examples/notebook/contrib/set_covering_deployment.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_covering_skiena.ipynb b/examples/notebook/contrib/set_covering_skiena.ipynb index 2e9c36e3fb4..01057394493 100644 --- a/examples/notebook/contrib/set_covering_skiena.ipynb +++ b/examples/notebook/contrib/set_covering_skiena.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/set_partition.ipynb b/examples/notebook/contrib/set_partition.ipynb index 41e415e1ed5..d151380bc14 100644 --- a/examples/notebook/contrib/set_partition.ipynb +++ b/examples/notebook/contrib/set_partition.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/sicherman_dice.ipynb b/examples/notebook/contrib/sicherman_dice.ipynb index 11f1f6c91bd..41531e4954e 100644 --- a/examples/notebook/contrib/sicherman_dice.ipynb +++ b/examples/notebook/contrib/sicherman_dice.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/ski_assignment.ipynb b/examples/notebook/contrib/ski_assignment.ipynb index 0fd19263f66..cfa236c1914 100644 --- a/examples/notebook/contrib/ski_assignment.ipynb +++ b/examples/notebook/contrib/ski_assignment.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/slitherlink.ipynb b/examples/notebook/contrib/slitherlink.ipynb index afae049f1f8..4ce404c3910 100644 --- a/examples/notebook/contrib/slitherlink.ipynb +++ b/examples/notebook/contrib/slitherlink.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/sports_schedule_sat.ipynb b/examples/notebook/contrib/sports_schedule_sat.ipynb index a8d95bfe384..1425f40d414 100644 --- a/examples/notebook/contrib/sports_schedule_sat.ipynb +++ b/examples/notebook/contrib/sports_schedule_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/stable_marriage.ipynb b/examples/notebook/contrib/stable_marriage.ipynb index 1209a47734e..7d9c87ddb12 100644 --- a/examples/notebook/contrib/stable_marriage.ipynb +++ b/examples/notebook/contrib/stable_marriage.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/stable_marriage_sat.ipynb b/examples/notebook/contrib/stable_marriage_sat.ipynb index 6db62119405..5cd6df58561 100644 --- a/examples/notebook/contrib/stable_marriage_sat.ipynb +++ b/examples/notebook/contrib/stable_marriage_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/steel.ipynb b/examples/notebook/contrib/steel.ipynb index 895afb030fb..73ebdf511a9 100644 --- a/examples/notebook/contrib/steel.ipynb +++ b/examples/notebook/contrib/steel.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/steel_lns.ipynb b/examples/notebook/contrib/steel_lns.ipynb index c8f1937bbe5..df75ee18dfb 100644 --- a/examples/notebook/contrib/steel_lns.ipynb +++ b/examples/notebook/contrib/steel_lns.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/stigler_contrib.ipynb b/examples/notebook/contrib/stigler_contrib.ipynb index 2e8e24bb6a9..a9f73e4444c 100644 --- a/examples/notebook/contrib/stigler_contrib.ipynb +++ b/examples/notebook/contrib/stigler_contrib.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/strimko2.ipynb b/examples/notebook/contrib/strimko2.ipynb index 0255610cc0a..49411e6dc25 100644 --- a/examples/notebook/contrib/strimko2.ipynb +++ b/examples/notebook/contrib/strimko2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/subset_sum.ipynb b/examples/notebook/contrib/subset_sum.ipynb index 8825f23e148..17f716fc716 100644 --- a/examples/notebook/contrib/subset_sum.ipynb +++ b/examples/notebook/contrib/subset_sum.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/survo_puzzle.ipynb b/examples/notebook/contrib/survo_puzzle.ipynb index 76a96d52fbb..217186c4e6c 100644 --- a/examples/notebook/contrib/survo_puzzle.ipynb +++ b/examples/notebook/contrib/survo_puzzle.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/toNum.ipynb b/examples/notebook/contrib/toNum.ipynb index cf9efddccbd..86392c650bd 100644 --- a/examples/notebook/contrib/toNum.ipynb +++ b/examples/notebook/contrib/toNum.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/traffic_lights.ipynb b/examples/notebook/contrib/traffic_lights.ipynb index 2f1a2b379c8..9e695c55cc7 100644 --- a/examples/notebook/contrib/traffic_lights.ipynb +++ b/examples/notebook/contrib/traffic_lights.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/vendor_scheduling.ipynb b/examples/notebook/contrib/vendor_scheduling.ipynb index 15808dcbd19..e819b7a59b6 100644 --- a/examples/notebook/contrib/vendor_scheduling.ipynb +++ b/examples/notebook/contrib/vendor_scheduling.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/volsay.ipynb b/examples/notebook/contrib/volsay.ipynb index 54d49053529..d344e95c0fd 100644 --- a/examples/notebook/contrib/volsay.ipynb +++ b/examples/notebook/contrib/volsay.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/volsay2.ipynb b/examples/notebook/contrib/volsay2.ipynb index baadaa11b7d..c8d6e891299 100644 --- a/examples/notebook/contrib/volsay2.ipynb +++ b/examples/notebook/contrib/volsay2.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/volsay3.ipynb b/examples/notebook/contrib/volsay3.ipynb index b15b62ff299..9b64f26d0cb 100644 --- a/examples/notebook/contrib/volsay3.ipynb +++ b/examples/notebook/contrib/volsay3.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/wedding_optimal_chart.ipynb b/examples/notebook/contrib/wedding_optimal_chart.ipynb index 49f26a266bc..2d25a22f9e5 100644 --- a/examples/notebook/contrib/wedding_optimal_chart.ipynb +++ b/examples/notebook/contrib/wedding_optimal_chart.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/who_killed_agatha.ipynb b/examples/notebook/contrib/who_killed_agatha.ipynb index da0b53b75c1..b35d1a6313e 100644 --- a/examples/notebook/contrib/who_killed_agatha.ipynb +++ b/examples/notebook/contrib/who_killed_agatha.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/xkcd.ipynb b/examples/notebook/contrib/xkcd.ipynb index 363385a51d1..87e1a351f99 100644 --- a/examples/notebook/contrib/xkcd.ipynb +++ b/examples/notebook/contrib/xkcd.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/contrib/young_tableaux.ipynb b/examples/notebook/contrib/young_tableaux.ipynb index 86ca0ee19e0..a5aa3cbdc34 100644 --- a/examples/notebook/contrib/young_tableaux.ipynb +++ b/examples/notebook/contrib/young_tableaux.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/appointments.ipynb b/examples/notebook/examples/appointments.ipynb index 3976bab8ac2..e9473305c38 100644 --- a/examples/notebook/examples/appointments.ipynb +++ b/examples/notebook/examples/appointments.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/arc_flow_cutting_stock_sat.ipynb b/examples/notebook/examples/arc_flow_cutting_stock_sat.ipynb index 66eb9fbcd0a..59374a19589 100644 --- a/examples/notebook/examples/arc_flow_cutting_stock_sat.ipynb +++ b/examples/notebook/examples/arc_flow_cutting_stock_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -93,7 +93,6 @@ "from ortools.linear_solver.python import model_builder as mb\n", "from ortools.sat.python import cp_model\n", "\n", - "FLAGS = flags.FLAGS\n", "\n", "_OUTPUT_PROTO = flags.define_string(\n", " \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n", @@ -107,17 +106,143 @@ "\n", "\n", "DESIRED_LENGTHS = [\n", - " 2490, 3980, 2490, 3980, 2391, 2391, 2391, 596, 596, 596, 2456, 2456, 3018,\n", - " 938, 3018, 938, 943, 3018, 943, 3018, 2490, 3980, 2490, 3980, 2391, 2391,\n", - " 2391, 596, 596, 596, 2456, 2456, 3018, 938, 3018, 938, 943, 3018, 943,\n", - " 3018, 2890, 3980, 2890, 3980, 2391, 2391, 2391, 596, 596, 596, 2856, 2856,\n", - " 3018, 938, 3018, 938, 943, 3018, 943, 3018, 3290, 3980, 3290, 3980, 2391,\n", - " 2391, 2391, 596, 596, 596, 3256, 3256, 3018, 938, 3018, 938, 943, 3018,\n", - " 943, 3018, 3690, 3980, 3690, 3980, 2391, 2391, 2391, 596, 596, 596, 3656,\n", - " 3656, 3018, 938, 3018, 938, 943, 3018, 943, 3018, 2790, 3980, 2790, 3980,\n", - " 2391, 2391, 2391, 596, 596, 596, 2756, 2756, 3018, 938, 3018, 938, 943,\n", - " 3018, 943, 3018, 2790, 3980, 2790, 3980, 2391, 2391, 2391, 596, 596, 596,\n", - " 2756, 2756, 3018, 938, 3018, 938, 943\n", + " 2490,\n", + " 3980,\n", + " 2490,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 2456,\n", + " 2456,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", + " 3018,\n", + " 943,\n", + " 3018,\n", + " 2490,\n", + " 3980,\n", + " 2490,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 2456,\n", + " 2456,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", + " 3018,\n", + " 943,\n", + " 3018,\n", + " 2890,\n", + " 3980,\n", + " 2890,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 2856,\n", + " 2856,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", + " 3018,\n", + " 943,\n", + " 3018,\n", + " 3290,\n", + " 3980,\n", + " 3290,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 3256,\n", + " 3256,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", + " 3018,\n", + " 943,\n", + " 3018,\n", + " 3690,\n", + " 3980,\n", + " 3690,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 3656,\n", + " 3656,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", + " 3018,\n", + " 943,\n", + " 3018,\n", + " 2790,\n", + " 3980,\n", + " 2790,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 2756,\n", + " 2756,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", + " 3018,\n", + " 943,\n", + " 3018,\n", + " 2790,\n", + " 3980,\n", + " 2790,\n", + " 3980,\n", + " 2391,\n", + " 2391,\n", + " 2391,\n", + " 596,\n", + " 596,\n", + " 596,\n", + " 2756,\n", + " 2756,\n", + " 3018,\n", + " 938,\n", + " 3018,\n", + " 938,\n", + " 943,\n", "]\n", "POSSIBLE_CAPACITIES = [4000, 5000, 6000, 7000, 8000]\n", "\n", @@ -167,7 +292,6 @@ " new_state = current_state + size * (card + 1)\n", " if new_state > max_capacity:\n", " break\n", - " new_state_index = -1\n", " if new_state in state_to_index:\n", " new_state_index = state_to_index[new_state]\n", " else:\n", diff --git a/examples/notebook/examples/assignment_with_constraints_sat.ipynb b/examples/notebook/examples/assignment_with_constraints_sat.ipynb index 7f4bd7437ad..4faf8406e83 100644 --- a/examples/notebook/examples/assignment_with_constraints_sat.ipynb +++ b/examples/notebook/examples/assignment_with_constraints_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -110,24 +110,24 @@ " [0, 1, 0, 1], # Workers 1, 3\n", " [0, 1, 1, 0], # Workers 1, 2\n", " [1, 1, 0, 0], # Workers 0, 1\n", - " [1, 0, 1, 0],\n", - " ] # Workers 0, 2\n", + " [1, 0, 1, 0], # Workers 0, 2\n", + " ]\n", "\n", " group2 = [\n", " [0, 0, 1, 1], # Workers 6, 7\n", " [0, 1, 0, 1], # Workers 5, 7\n", " [0, 1, 1, 0], # Workers 5, 6\n", " [1, 1, 0, 0], # Workers 4, 5\n", - " [1, 0, 0, 1],\n", - " ] # Workers 4, 7\n", + " [1, 0, 0, 1], # Workers 4, 7\n", + " ]\n", "\n", " group3 = [\n", " [0, 0, 1, 1], # Workers 10, 11\n", " [0, 1, 0, 1], # Workers 9, 11\n", " [0, 1, 1, 0], # Workers 9, 10\n", " [1, 0, 1, 0], # Workers 8, 10\n", - " [1, 0, 0, 1],\n", - " ] # Workers 8, 11\n", + " [1, 0, 0, 1], # Workers 8, 11\n", + " ]\n", "\n", " sizes = [10, 7, 3, 12, 15, 4, 11, 5]\n", " total_size_max = 15\n", @@ -141,10 +141,9 @@ " model = cp_model.CpModel()\n", " # Variables\n", " selected = [\n", - " [model.new_bool_var(\"x[%i,%i]\" % (i, j)) for j in all_tasks]\n", - " for i in all_workers\n", + " [model.new_bool_var(f\"x[{i},{j}]\") for j in all_tasks] for i in all_workers\n", " ]\n", - " works = [model.new_bool_var(\"works[%i]\" % i) for i in all_workers]\n", + " works = [model.new_bool_var(f\"works[{i}]\") for i in all_workers]\n", "\n", " # Constraints\n", "\n", @@ -175,21 +174,16 @@ " status = solver.solve(model)\n", "\n", " if status == cp_model.OPTIMAL:\n", - " print(\"Total cost = %i\" % solver.objective_value)\n", + " print(f\"Total cost = {solver.objective_value}\")\n", " print()\n", " for i in all_workers:\n", " for j in all_tasks:\n", " if solver.boolean_value(selected[i][j]):\n", - " print(\n", - " \"Worker \", i, \" assigned to task \", j, \" Cost = \", cost[i][j]\n", - " )\n", + " print(f\"Worker {i} assigned to task {j} with Cost = {cost[i][j]}\")\n", "\n", " print()\n", "\n", - " print(\"Statistics\")\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " print(solver.response_stats())\n", "\n", "\n", "def main(argv: Sequence[str]) -> None:\n", diff --git a/examples/notebook/examples/balance_group_sat.ipynb b/examples/notebook/examples/balance_group_sat.ipynb index 61b74bf4a46..2e966e0e289 100644 --- a/examples/notebook/examples/balance_group_sat.ipynb +++ b/examples/notebook/examples/balance_group_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -88,7 +88,8 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Sequence\n", + "from typing import Dict, Sequence\n", + "\n", "from ortools.sat.python import cp_model\n", "\n", "\n", @@ -106,10 +107,10 @@ " self.__item_in_group = item_in_group\n", "\n", " def on_solution_callback(self):\n", - " print(\"Solution %i\" % self.__solution_count)\n", + " print(f\"Solution {self.__solution_count}\")\n", " self.__solution_count += 1\n", "\n", - " print(\" objective value = %i\" % self.objective_value)\n", + " print(f\" objective value = {self.objective_value}\")\n", " groups = {}\n", " sums = {}\n", " for g in self.__all_groups:\n", @@ -122,11 +123,11 @@ "\n", " for g in self.__all_groups:\n", " group = groups[g]\n", - " print(\"group %i: sum = %0.2f [\" % (g, sums[g]), end=\"\")\n", + " print(f\"group {g}: sum = {sums[g]:0.2f} [\", end=\"\")\n", " for item in group:\n", " value = self.__values[item]\n", " color = self.__colors[item]\n", - " print(\" (%i, %i, %i)\" % (item, value, color), end=\"\")\n", + " print(f\" ({item}, {value}, {color})\", end=\"\")\n", " print(\"]\")\n", "\n", "\n", @@ -156,18 +157,17 @@ " num_items_per_group = num_items // num_groups\n", "\n", " # Collect all items in a given color.\n", - " items_per_color = {}\n", - " for c in all_colors:\n", - " items_per_color[c] = []\n", + " items_per_color: Dict[int, list[int]] = {}\n", + " for color in all_colors:\n", + " items_per_color[color] = []\n", " for i in all_items:\n", - " if colors[i] == c:\n", - " items_per_color[c].append(i)\n", + " if colors[i] == color:\n", + " items_per_color[color].append(i)\n", "\n", " print(\n", - " \"Model has %i items, %i groups, and %i colors\"\n", - " % (num_items, num_groups, num_colors)\n", + " f\"Model has {num_items} items, {num_groups} groups, and\" f\" {num_colors} colors\"\n", " )\n", - " print(\" average sum per group = %i\" % average_sum_per_group)\n", + " print(f\" average sum per group = {average_sum_per_group}\")\n", "\n", " # Model.\n", "\n", @@ -176,7 +176,7 @@ " item_in_group = {}\n", " for i in all_items:\n", " for g in all_groups:\n", - " item_in_group[(i, g)] = model.new_bool_var(\"item %d in group %d\" % (i, g))\n", + " item_in_group[(i, g)] = model.new_bool_var(f\"item {i} in group {g}\")\n", "\n", " # Each group must have the same size.\n", " for g in all_groups:\n", @@ -204,9 +204,7 @@ " color_in_group = {}\n", " for g in all_groups:\n", " for c in all_colors:\n", - " color_in_group[(c, g)] = model.new_bool_var(\n", - " \"color %d is in group %d\" % (c, g)\n", - " )\n", + " color_in_group[(c, g)] = model.new_bool_var(f\"color {c} is in group {g}\")\n", "\n", " # Item is in a group implies its color is in that group.\n", " for i in all_items:\n", @@ -243,11 +241,8 @@ " status = solver.solve(model, solution_printer)\n", "\n", " if status == cp_model.OPTIMAL:\n", - " print(\"Optimal epsilon: %i\" % solver.objective_value)\n", - " print(\"Statistics\")\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " print(f\"Optimal epsilon: {solver.objective_value}\")\n", + " print(solver.response_stats())\n", " else:\n", " print(\"No solution found\")\n", "\n", diff --git a/examples/notebook/examples/bus_driver_scheduling_flow_sat.ipynb b/examples/notebook/examples/bus_driver_scheduling_flow_sat.ipynb index 3f4b5cd2fd1..59a31620011 100644 --- a/examples/notebook/examples/bus_driver_scheduling_flow_sat.ipynb +++ b/examples/notebook/examples/bus_driver_scheduling_flow_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -99,14 +99,13 @@ "from ortools.sat.python import cp_model\n", "\n", "PARSER = argparse.ArgumentParser()\n", + "PARSER.add_argument(\"--instance\", default=1, type=int, help=\"Instance number (1..3).\")\n", "PARSER.add_argument(\n", - " '--instance', default=1, type=int, help='Instance number (1..3).')\n", - "PARSER.add_argument(\n", - " '--output_proto_file',\n", + " \"--output_proto_file\",\n", " default=\"\",\n", - " help='Output file to write the cp_model'\n", - " 'proto to.')\n", - "PARSER.add_argument('--params', default=\"\", help='Sat solver parameters.')\n", + " help=\"Output file to write the cp_model\" \"proto to.\",\n", + ")\n", + "PARSER.add_argument(\"--params\", default=\"\", help=\"Sat solver parameters.\")\n", "\n", "SAMPLE_SHIFTS_SMALL = [\n", " #\n", @@ -118,1618 +117,1618 @@ " # - shift end minute\n", " # - shift duration in minutes\n", " #\n", - " [0, '05:18', '06:00', 318, 360, 42],\n", - " [1, '05:26', '06:08', 326, 368, 42],\n", - " [2, '05:40', '05:56', 340, 356, 16],\n", - " [3, '06:06', '06:51', 366, 411, 45],\n", - " [4, '06:40', '07:52', 400, 472, 72],\n", - " [5, '06:42', '07:13', 402, 433, 31],\n", - " [6, '06:48', '08:15', 408, 495, 87],\n", - " [7, '06:59', '08:07', 419, 487, 68],\n", - " [8, '07:20', '07:36', 440, 456, 16],\n", - " [9, '07:35', '08:22', 455, 502, 47],\n", - " [10, '07:50', '08:55', 470, 535, 65],\n", - " [11, '08:00', '09:05', 480, 545, 65],\n", - " [12, '08:00', '08:35', 480, 515, 35],\n", - " [13, '08:11', '09:41', 491, 581, 90],\n", - " [14, '08:28', '08:50', 508, 530, 22],\n", - " [15, '08:35', '08:45', 515, 525, 10],\n", - " [16, '08:40', '08:50', 520, 530, 10],\n", - " [17, '09:03', '10:28', 543, 628, 85],\n", - " [18, '09:23', '09:49', 563, 589, 26],\n", - " [19, '09:30', '09:40', 570, 580, 10],\n", - " [20, '09:57', '10:20', 597, 620, 23],\n", - " [21, '10:09', '11:03', 609, 663, 54],\n", - " [22, '10:20', '10:30', 620, 630, 10],\n", - " [23, '11:00', '11:10', 660, 670, 10],\n", - " [24, '11:45', '12:24', 705, 744, 39],\n", - " [25, '12:18', '13:00', 738, 780, 42],\n", - " [26, '13:18', '14:44', 798, 884, 86],\n", - " [27, '13:53', '14:49', 833, 889, 56],\n", - " [28, '14:03', '14:50', 843, 890, 47],\n", - " [29, '14:28', '15:15', 868, 915, 47],\n", - " [30, '14:30', '15:41', 870, 941, 71],\n", - " [31, '14:48', '15:35', 888, 935, 47],\n", - " [32, '15:03', '15:50', 903, 950, 47],\n", - " [33, '15:28', '16:54', 928, 1014, 86],\n", - " [34, '15:38', '16:25', 938, 985, 47],\n", - " [35, '15:40', '15:56', 940, 956, 16],\n", - " [36, '15:58', '16:45', 958, 1005, 47],\n", - " [37, '16:04', '17:30', 964, 1050, 86],\n", - " [38, '16:28', '17:15', 988, 1035, 47],\n", - " [39, '16:36', '17:21', 996, 1041, 45],\n", - " [40, '16:50', '17:00', 1010, 1020, 10],\n", - " [41, '16:54', '18:20', 1014, 1100, 86],\n", - " [42, '17:01', '17:13', 1021, 1033, 12],\n", - " [43, '17:19', '18:31', 1039, 1111, 72],\n", - " [44, '17:23', '18:10', 1043, 1090, 47],\n", - " [45, '17:34', '18:15', 1054, 1095, 41],\n", - " [46, '18:04', '19:29', 1084, 1169, 85],\n", - " [47, '18:34', '19:58', 1114, 1198, 84],\n", - " [48, '19:56', '20:34', 1196, 1234, 38],\n", - " [49, '20:05', '20:48', 1205, 1248, 43]\n", + " [0, \"05:18\", \"06:00\", 318, 360, 42],\n", + " [1, \"05:26\", \"06:08\", 326, 368, 42],\n", + " [2, \"05:40\", \"05:56\", 340, 356, 16],\n", + " [3, \"06:06\", \"06:51\", 366, 411, 45],\n", + " [4, \"06:40\", \"07:52\", 400, 472, 72],\n", + " [5, \"06:42\", \"07:13\", 402, 433, 31],\n", + " [6, \"06:48\", \"08:15\", 408, 495, 87],\n", + " [7, \"06:59\", \"08:07\", 419, 487, 68],\n", + " [8, \"07:20\", \"07:36\", 440, 456, 16],\n", + " [9, \"07:35\", \"08:22\", 455, 502, 47],\n", + " [10, \"07:50\", \"08:55\", 470, 535, 65],\n", + " [11, \"08:00\", \"09:05\", 480, 545, 65],\n", + " [12, \"08:00\", \"08:35\", 480, 515, 35],\n", + " [13, \"08:11\", \"09:41\", 491, 581, 90],\n", + " [14, \"08:28\", \"08:50\", 508, 530, 22],\n", + " [15, \"08:35\", \"08:45\", 515, 525, 10],\n", + " [16, \"08:40\", \"08:50\", 520, 530, 10],\n", + " [17, \"09:03\", \"10:28\", 543, 628, 85],\n", + " [18, \"09:23\", \"09:49\", 563, 589, 26],\n", + " [19, \"09:30\", \"09:40\", 570, 580, 10],\n", + " [20, \"09:57\", \"10:20\", 597, 620, 23],\n", + " [21, \"10:09\", \"11:03\", 609, 663, 54],\n", + " [22, \"10:20\", \"10:30\", 620, 630, 10],\n", + " [23, \"11:00\", \"11:10\", 660, 670, 10],\n", + " [24, \"11:45\", \"12:24\", 705, 744, 39],\n", + " [25, \"12:18\", \"13:00\", 738, 780, 42],\n", + " [26, \"13:18\", \"14:44\", 798, 884, 86],\n", + " [27, \"13:53\", \"14:49\", 833, 889, 56],\n", + " [28, \"14:03\", \"14:50\", 843, 890, 47],\n", + " [29, \"14:28\", \"15:15\", 868, 915, 47],\n", + " [30, \"14:30\", \"15:41\", 870, 941, 71],\n", + " [31, \"14:48\", \"15:35\", 888, 935, 47],\n", + " [32, \"15:03\", \"15:50\", 903, 950, 47],\n", + " [33, \"15:28\", \"16:54\", 928, 1014, 86],\n", + " [34, \"15:38\", \"16:25\", 938, 985, 47],\n", + " [35, \"15:40\", \"15:56\", 940, 956, 16],\n", + " [36, \"15:58\", \"16:45\", 958, 1005, 47],\n", + " [37, \"16:04\", \"17:30\", 964, 1050, 86],\n", + " [38, \"16:28\", \"17:15\", 988, 1035, 47],\n", + " [39, \"16:36\", \"17:21\", 996, 1041, 45],\n", + " [40, \"16:50\", \"17:00\", 1010, 1020, 10],\n", + " [41, \"16:54\", \"18:20\", 1014, 1100, 86],\n", + " [42, \"17:01\", \"17:13\", 1021, 1033, 12],\n", + " [43, \"17:19\", \"18:31\", 1039, 1111, 72],\n", + " [44, \"17:23\", \"18:10\", 1043, 1090, 47],\n", + " [45, \"17:34\", \"18:15\", 1054, 1095, 41],\n", + " [46, \"18:04\", \"19:29\", 1084, 1169, 85],\n", + " [47, \"18:34\", \"19:58\", 1114, 1198, 84],\n", + " [48, \"19:56\", \"20:34\", 1196, 1234, 38],\n", + " [49, \"20:05\", \"20:48\", 1205, 1248, 43],\n", "] # yapf:disable\n", "\n", "SAMPLE_SHIFTS_MEDIUM = [\n", - " [0, '04:30', '04:53', 270, 293, 23],\n", - " [1, '04:46', '04:56', 286, 296, 10],\n", - " [2, '04:52', '05:56', 292, 356, 64],\n", - " [3, '04:53', '05:23', 293, 323, 30],\n", - " [4, '05:07', '05:44', 307, 344, 37],\n", - " [5, '05:10', '06:06', 310, 366, 56],\n", - " [6, '05:18', '06:03', 318, 363, 45],\n", - " [7, '05:30', '05:40', 330, 340, 10],\n", - " [8, '05:30', '05:40', 330, 340, 10],\n", - " [9, '05:33', '06:15', 333, 375, 42],\n", - " [10, '05:40', '05:50', 340, 350, 10],\n", - " [11, '05:43', '06:08', 343, 368, 25],\n", - " [12, '05:54', '07:20', 354, 440, 86],\n", - " [13, '06:04', '06:37', 364, 397, 33],\n", - " [14, '06:13', '06:58', 373, 418, 45],\n", - " [15, '06:14', '07:40', 374, 460, 86],\n", - " [16, '06:15', '07:15', 375, 435, 60],\n", - " [17, '06:16', '06:26', 376, 386, 10],\n", - " [18, '06:17', '06:34', 377, 394, 17],\n", - " [19, '06:20', '06:36', 380, 396, 16],\n", - " [20, '06:22', '07:06', 382, 426, 44],\n", - " [21, '06:24', '07:50', 384, 470, 86],\n", - " [22, '06:27', '06:44', 387, 404, 17],\n", - " [23, '06:30', '06:40', 390, 400, 10],\n", - " [24, '06:31', '06:43', 391, 403, 12],\n", - " [25, '06:33', '07:53', 393, 473, 80],\n", - " [26, '06:34', '07:09', 394, 429, 35],\n", - " [27, '06:40', '06:56', 400, 416, 16],\n", - " [28, '06:44', '07:17', 404, 437, 33],\n", - " [29, '06:46', '06:58', 406, 418, 12],\n", - " [30, '06:49', '07:43', 409, 463, 54],\n", - " [31, '06:50', '07:05', 410, 425, 15],\n", - " [32, '06:52', '07:36', 412, 456, 44],\n", - " [33, '06:54', '07:27', 414, 447, 33],\n", - " [34, '06:56', '08:23', 416, 503, 87],\n", - " [35, '07:04', '07:44', 424, 464, 40],\n", - " [36, '07:11', '08:36', 431, 516, 85],\n", - " [37, '07:17', '07:35', 437, 455, 18],\n", - " [38, '07:22', '08:06', 442, 486, 44],\n", - " [39, '07:27', '08:15', 447, 495, 48],\n", - " [40, '07:35', '07:45', 455, 465, 10],\n", - " [41, '07:43', '08:08', 463, 488, 25],\n", - " [42, '07:50', '08:37', 470, 517, 47],\n", - " [43, '07:58', '08:45', 478, 525, 47],\n", - " [44, '08:00', '08:35', 480, 515, 35],\n", - " [45, '08:06', '08:51', 486, 531, 45],\n", - " [46, '08:10', '08:45', 490, 525, 35],\n", - " [47, '08:15', '08:30', 495, 510, 15],\n", - " [48, '08:16', '09:00', 496, 540, 44],\n", - " [49, '08:18', '09:16', 498, 556, 58],\n", - " [50, '08:20', '08:36', 500, 516, 16],\n", - " [51, '08:27', '09:07', 507, 547, 40],\n", - " [52, '08:30', '08:45', 510, 525, 15],\n", - " [53, '08:35', '09:15', 515, 555, 40],\n", - " [54, '08:46', '09:30', 526, 570, 44],\n", - " [55, '08:51', '09:17', 531, 557, 26],\n", - " [56, '08:55', '09:15', 535, 555, 20],\n", - " [57, '08:58', '09:38', 538, 578, 40],\n", - " [58, '09:00', '09:35', 540, 575, 35],\n", - " [59, '09:00', '09:16', 540, 556, 16],\n", - " [60, '09:20', '09:36', 560, 576, 16],\n", - " [61, '09:31', '09:43', 571, 583, 12],\n", - " [62, '09:33', '10:15', 573, 615, 42],\n", - " [63, '09:54', '10:05', 594, 605, 11],\n", - " [64, '10:11', '10:38', 611, 638, 27],\n", - " [65, '10:18', '11:00', 618, 660, 42],\n", - " [66, '10:21', '10:47', 621, 647, 26],\n", - " [67, '10:25', '11:04', 625, 664, 39],\n", - " [68, '10:26', '11:08', 626, 668, 42],\n", - " [69, '10:44', '12:11', 644, 731, 87],\n", - " [70, '11:00', '11:16', 660, 676, 16],\n", - " [71, '11:15', '11:54', 675, 714, 39],\n", - " [72, '11:16', '11:28', 676, 688, 12],\n", - " [73, '11:20', '11:30', 680, 690, 10],\n", - " [74, '11:21', '11:47', 681, 707, 26],\n", - " [75, '11:25', '12:04', 685, 724, 39],\n", - " [76, '11:34', '11:45', 694, 705, 11],\n", - " [77, '11:35', '12:14', 695, 734, 39],\n", - " [78, '11:41', '12:23', 701, 743, 42],\n", - " [79, '11:44', '12:35', 704, 755, 51],\n", - " [80, '11:46', '11:58', 706, 718, 12],\n", - " [81, '12:00', '12:10', 720, 730, 10],\n", - " [82, '12:04', '12:15', 724, 735, 11],\n", - " [83, '12:04', '13:04', 724, 784, 60],\n", - " [84, '12:11', '12:38', 731, 758, 27],\n", - " [85, '12:15', '12:54', 735, 774, 39],\n", - " [86, '12:25', '13:10', 745, 790, 45],\n", - " [87, '12:30', '12:40', 750, 760, 10],\n", - " [88, '12:34', '13:58', 754, 838, 84],\n", - " [89, '12:38', '13:25', 758, 805, 47],\n", - " [90, '12:48', '13:35', 768, 815, 47],\n", - " [91, '13:00', '13:16', 780, 796, 16],\n", - " [92, '13:05', '13:44', 785, 824, 39],\n", - " [93, '13:08', '13:55', 788, 835, 47],\n", - " [94, '13:14', '14:38', 794, 878, 84],\n", - " [95, '13:23', '13:49', 803, 829, 26],\n", - " [96, '13:25', '14:04', 805, 844, 39],\n", - " [97, '13:28', '14:54', 808, 894, 86],\n", - " [98, '13:31', '13:43', 811, 823, 12],\n", - " [99, '13:34', '14:58', 814, 898, 84],\n", - " [100, '13:38', '14:25', 818, 865, 47],\n", - " [101, '13:38', '15:04', 818, 904, 86],\n", - " [102, '13:39', '14:33', 819, 873, 54],\n", - " [103, '13:40', '13:50', 820, 830, 10],\n", - " [104, '13:43', '14:10', 823, 850, 27],\n", - " [105, '13:48', '14:35', 828, 875, 47],\n", - " [106, '13:48', '14:35', 828, 875, 47],\n", - " [107, '13:53', '14:40', 833, 880, 47],\n", - " [108, '13:58', '15:24', 838, 924, 86],\n", - " [109, '13:58', '14:25', 838, 865, 27],\n", - " [110, '14:00', '14:16', 840, 856, 16],\n", - " [111, '14:13', '15:00', 853, 900, 47],\n", - " [112, '14:20', '15:31', 860, 931, 71],\n", - " [113, '14:25', '15:02', 865, 902, 37],\n", - " [114, '14:34', '14:45', 874, 885, 11],\n", - " [115, '14:40', '15:51', 880, 951, 71],\n", - " [116, '14:40', '14:56', 880, 896, 16],\n", - " [117, '14:46', '14:58', 886, 898, 12],\n", - " [118, '14:49', '15:43', 889, 943, 54],\n", - " [119, '14:52', '15:21', 892, 921, 29],\n", - " [120, '14:58', '16:24', 898, 984, 86],\n", - " [121, '14:59', '15:53', 899, 953, 54],\n", - " [122, '15:00', '15:10', 900, 910, 10],\n", - " [123, '15:00', '15:35', 900, 935, 35],\n", - " [124, '15:08', '15:45', 908, 945, 37],\n", - " [125, '15:12', '15:36', 912, 936, 24],\n", - " [126, '15:18', '16:05', 918, 965, 47],\n", - " [127, '15:24', '16:05', 924, 965, 41],\n", - " [128, '15:31', '15:43', 931, 943, 12],\n", - " [129, '15:35', '15:54', 935, 954, 19],\n", - " [130, '15:36', '16:21', 936, 981, 45],\n", - " [131, '15:39', '16:33', 939, 993, 54],\n", - " [132, '15:48', '16:35', 948, 995, 47],\n", - " [133, '15:50', '17:01', 950, 1021, 71],\n", - " [134, '16:03', '16:50', 963, 1010, 47],\n", - " [135, '16:18', '17:44', 978, 1064, 86],\n", - " [136, '16:24', '17:05', 984, 1025, 41],\n", - " [137, '16:28', '17:15', 988, 1035, 47],\n", - " [138, '16:34', '17:15', 994, 1035, 41],\n", - " [139, '16:38', '17:25', 998, 1045, 47],\n", - " [140, '16:40', '16:56', 1000, 1016, 16],\n", - " [141, '16:45', '17:04', 1005, 1024, 19],\n", - " [142, '16:52', '17:36', 1012, 1056, 44],\n", - " [143, '16:58', '17:45', 1018, 1065, 47],\n", - " [144, '17:04', '18:30', 1024, 1110, 86],\n", - " [145, '17:04', '17:45', 1024, 1065, 41],\n", - " [146, '17:09', '18:03', 1029, 1083, 54],\n", - " [147, '17:18', '18:44', 1038, 1124, 86],\n", - " [148, '17:28', '18:15', 1048, 1095, 47],\n", - " [149, '17:29', '18:41', 1049, 1121, 72],\n", - " [150, '17:36', '18:21', 1056, 1101, 45],\n", - " [151, '17:38', '18:25', 1058, 1105, 47],\n", - " [152, '17:40', '17:56', 1060, 1076, 16],\n", - " [153, '17:45', '18:04', 1065, 1084, 19],\n", - " [154, '17:46', '17:58', 1066, 1078, 12],\n", - " [155, '17:48', '18:35', 1068, 1115, 47],\n", - " [156, '17:49', '18:43', 1069, 1123, 54],\n", - " [157, '17:55', '18:14', 1075, 1094, 19],\n", - " [158, '17:58', '18:45', 1078, 1125, 47],\n", - " [159, '18:00', '19:11', 1080, 1151, 71],\n", - " [160, '18:04', '18:45', 1084, 1125, 41],\n", - " [161, '18:09', '19:03', 1089, 1143, 54],\n", - " [162, '18:13', '19:00', 1093, 1140, 47],\n", - " [163, '18:13', '18:40', 1093, 1120, 27],\n", - " [164, '18:19', '19:13', 1099, 1153, 54],\n", - " [165, '18:28', '19:25', 1108, 1165, 57],\n", - " [166, '18:48', '19:28', 1128, 1168, 40],\n", - " [167, '19:03', '19:45', 1143, 1185, 42],\n", - " [168, '19:20', '19:36', 1160, 1176, 16],\n", - " [169, '19:21', '19:31', 1161, 1171, 10],\n", - " [170, '19:25', '20:04', 1165, 1204, 39],\n", - " [171, '19:26', '20:08', 1166, 1208, 42],\n", - " [172, '19:30', '19:40', 1170, 1180, 10],\n", - " [173, '19:44', '20:33', 1184, 1233, 49],\n", - " [174, '19:48', '21:09', 1188, 1269, 81],\n", - " [175, '19:53', '21:02', 1193, 1262, 69],\n", - " [176, '20:04', '20:29', 1204, 1229, 25],\n", - " [177, '20:17', '21:03', 1217, 1263, 46],\n", - " [178, '20:20', '20:57', 1220, 1257, 37],\n", - " [179, '20:29', '21:18', 1229, 1278, 49],\n", - " [180, '20:35', '21:54', 1235, 1314, 79],\n", - " [181, '20:40', '20:50', 1240, 1250, 10],\n", - " [182, '20:47', '21:42', 1247, 1302, 55],\n", - " [183, '21:00', '21:10', 1260, 1270, 10],\n", - " [184, '21:07', '21:44', 1267, 1304, 37],\n", - " [185, '21:14', '22:03', 1274, 1323, 49],\n", - " [186, '21:39', '21:55', 1299, 1315, 16],\n", - " [187, '21:40', '22:17', 1300, 1337, 37],\n", - " [188, '21:40', '21:50', 1300, 1310, 10],\n", - " [189, '21:48', '22:03', 1308, 1323, 15],\n", - " [190, '22:17', '23:03', 1337, 1383, 46],\n", - " [191, '22:43', '23:08', 1363, 1388, 25],\n", - " [192, '23:35', '01:05', 1415, 1505, 90],\n", - " [193, '23:46', '00:01', 1426, 1441, 15],\n", - " [194, '23:47', '00:33', 1427, 1473, 46],\n", - " [195, '23:52', '00:24', 1432, 1464, 32],\n", - " [196, '23:58', '00:38', 1438, 1478, 40],\n", - " [197, '00:02', '00:12', 1442, 1452, 10],\n", - " [198, '00:07', '00:39', 1447, 1479, 32],\n", - " [199, '00:25', '01:12', 1465, 1512, 47]\n", + " [0, \"04:30\", \"04:53\", 270, 293, 23],\n", + " [1, \"04:46\", \"04:56\", 286, 296, 10],\n", + " [2, \"04:52\", \"05:56\", 292, 356, 64],\n", + " [3, \"04:53\", \"05:23\", 293, 323, 30],\n", + " [4, \"05:07\", \"05:44\", 307, 344, 37],\n", + " [5, \"05:10\", \"06:06\", 310, 366, 56],\n", + " [6, \"05:18\", \"06:03\", 318, 363, 45],\n", + " [7, \"05:30\", \"05:40\", 330, 340, 10],\n", + " [8, \"05:30\", \"05:40\", 330, 340, 10],\n", + " [9, \"05:33\", \"06:15\", 333, 375, 42],\n", + " [10, \"05:40\", \"05:50\", 340, 350, 10],\n", + " [11, \"05:43\", \"06:08\", 343, 368, 25],\n", + " [12, \"05:54\", \"07:20\", 354, 440, 86],\n", + " [13, \"06:04\", \"06:37\", 364, 397, 33],\n", + " [14, \"06:13\", \"06:58\", 373, 418, 45],\n", + " [15, \"06:14\", \"07:40\", 374, 460, 86],\n", + " [16, \"06:15\", \"07:15\", 375, 435, 60],\n", + " [17, \"06:16\", \"06:26\", 376, 386, 10],\n", + " [18, \"06:17\", \"06:34\", 377, 394, 17],\n", + " [19, \"06:20\", \"06:36\", 380, 396, 16],\n", + " [20, \"06:22\", \"07:06\", 382, 426, 44],\n", + " [21, \"06:24\", \"07:50\", 384, 470, 86],\n", + " [22, \"06:27\", \"06:44\", 387, 404, 17],\n", + " [23, \"06:30\", \"06:40\", 390, 400, 10],\n", + " [24, \"06:31\", \"06:43\", 391, 403, 12],\n", + " [25, \"06:33\", \"07:53\", 393, 473, 80],\n", + " [26, \"06:34\", \"07:09\", 394, 429, 35],\n", + " [27, \"06:40\", \"06:56\", 400, 416, 16],\n", + " [28, \"06:44\", \"07:17\", 404, 437, 33],\n", + " [29, \"06:46\", \"06:58\", 406, 418, 12],\n", + " [30, \"06:49\", \"07:43\", 409, 463, 54],\n", + " [31, \"06:50\", \"07:05\", 410, 425, 15],\n", + " [32, \"06:52\", \"07:36\", 412, 456, 44],\n", + " [33, \"06:54\", \"07:27\", 414, 447, 33],\n", + " [34, \"06:56\", \"08:23\", 416, 503, 87],\n", + " [35, \"07:04\", \"07:44\", 424, 464, 40],\n", + " [36, \"07:11\", \"08:36\", 431, 516, 85],\n", + " [37, \"07:17\", \"07:35\", 437, 455, 18],\n", + " [38, \"07:22\", \"08:06\", 442, 486, 44],\n", + " [39, \"07:27\", \"08:15\", 447, 495, 48],\n", + " [40, \"07:35\", \"07:45\", 455, 465, 10],\n", + " [41, \"07:43\", \"08:08\", 463, 488, 25],\n", + " [42, \"07:50\", \"08:37\", 470, 517, 47],\n", + " [43, \"07:58\", \"08:45\", 478, 525, 47],\n", + " [44, \"08:00\", \"08:35\", 480, 515, 35],\n", + " [45, \"08:06\", \"08:51\", 486, 531, 45],\n", + " [46, \"08:10\", \"08:45\", 490, 525, 35],\n", + " [47, \"08:15\", \"08:30\", 495, 510, 15],\n", + " [48, \"08:16\", \"09:00\", 496, 540, 44],\n", + " [49, \"08:18\", \"09:16\", 498, 556, 58],\n", + " [50, \"08:20\", \"08:36\", 500, 516, 16],\n", + " [51, \"08:27\", \"09:07\", 507, 547, 40],\n", + " [52, \"08:30\", \"08:45\", 510, 525, 15],\n", + " [53, \"08:35\", \"09:15\", 515, 555, 40],\n", + " [54, \"08:46\", \"09:30\", 526, 570, 44],\n", + " [55, \"08:51\", \"09:17\", 531, 557, 26],\n", + " [56, \"08:55\", \"09:15\", 535, 555, 20],\n", + " [57, \"08:58\", \"09:38\", 538, 578, 40],\n", + " [58, \"09:00\", \"09:35\", 540, 575, 35],\n", + " [59, \"09:00\", \"09:16\", 540, 556, 16],\n", + " [60, \"09:20\", \"09:36\", 560, 576, 16],\n", + " [61, \"09:31\", \"09:43\", 571, 583, 12],\n", + " [62, \"09:33\", \"10:15\", 573, 615, 42],\n", + " [63, \"09:54\", \"10:05\", 594, 605, 11],\n", + " [64, \"10:11\", \"10:38\", 611, 638, 27],\n", + " [65, \"10:18\", \"11:00\", 618, 660, 42],\n", + " [66, \"10:21\", \"10:47\", 621, 647, 26],\n", + " [67, \"10:25\", \"11:04\", 625, 664, 39],\n", + " [68, \"10:26\", \"11:08\", 626, 668, 42],\n", + " [69, \"10:44\", \"12:11\", 644, 731, 87],\n", + " [70, \"11:00\", \"11:16\", 660, 676, 16],\n", + " [71, \"11:15\", \"11:54\", 675, 714, 39],\n", + " [72, \"11:16\", \"11:28\", 676, 688, 12],\n", + " [73, \"11:20\", \"11:30\", 680, 690, 10],\n", + " [74, \"11:21\", \"11:47\", 681, 707, 26],\n", + " [75, \"11:25\", \"12:04\", 685, 724, 39],\n", + " [76, \"11:34\", \"11:45\", 694, 705, 11],\n", + " [77, \"11:35\", \"12:14\", 695, 734, 39],\n", + " [78, \"11:41\", \"12:23\", 701, 743, 42],\n", + " [79, \"11:44\", \"12:35\", 704, 755, 51],\n", + " [80, \"11:46\", \"11:58\", 706, 718, 12],\n", + " [81, \"12:00\", \"12:10\", 720, 730, 10],\n", + " [82, \"12:04\", \"12:15\", 724, 735, 11],\n", + " [83, \"12:04\", \"13:04\", 724, 784, 60],\n", + " [84, \"12:11\", \"12:38\", 731, 758, 27],\n", + " [85, \"12:15\", \"12:54\", 735, 774, 39],\n", + " [86, \"12:25\", \"13:10\", 745, 790, 45],\n", + " [87, \"12:30\", \"12:40\", 750, 760, 10],\n", + " [88, \"12:34\", \"13:58\", 754, 838, 84],\n", + " [89, \"12:38\", \"13:25\", 758, 805, 47],\n", + " [90, \"12:48\", \"13:35\", 768, 815, 47],\n", + " [91, \"13:00\", \"13:16\", 780, 796, 16],\n", + " [92, \"13:05\", \"13:44\", 785, 824, 39],\n", + " [93, \"13:08\", \"13:55\", 788, 835, 47],\n", + " [94, \"13:14\", \"14:38\", 794, 878, 84],\n", + " [95, \"13:23\", \"13:49\", 803, 829, 26],\n", + " [96, \"13:25\", \"14:04\", 805, 844, 39],\n", + " [97, \"13:28\", \"14:54\", 808, 894, 86],\n", + " [98, \"13:31\", \"13:43\", 811, 823, 12],\n", + " [99, \"13:34\", \"14:58\", 814, 898, 84],\n", + " [100, \"13:38\", \"14:25\", 818, 865, 47],\n", + " [101, \"13:38\", \"15:04\", 818, 904, 86],\n", + " [102, \"13:39\", \"14:33\", 819, 873, 54],\n", + " [103, \"13:40\", \"13:50\", 820, 830, 10],\n", + " [104, \"13:43\", \"14:10\", 823, 850, 27],\n", + " [105, \"13:48\", \"14:35\", 828, 875, 47],\n", + " [106, \"13:48\", \"14:35\", 828, 875, 47],\n", + " [107, \"13:53\", \"14:40\", 833, 880, 47],\n", + " [108, \"13:58\", \"15:24\", 838, 924, 86],\n", + " [109, \"13:58\", \"14:25\", 838, 865, 27],\n", + " [110, \"14:00\", \"14:16\", 840, 856, 16],\n", + " [111, \"14:13\", \"15:00\", 853, 900, 47],\n", + " [112, \"14:20\", \"15:31\", 860, 931, 71],\n", + " [113, \"14:25\", \"15:02\", 865, 902, 37],\n", + " [114, \"14:34\", \"14:45\", 874, 885, 11],\n", + " [115, \"14:40\", \"15:51\", 880, 951, 71],\n", + " [116, \"14:40\", \"14:56\", 880, 896, 16],\n", + " [117, \"14:46\", \"14:58\", 886, 898, 12],\n", + " [118, \"14:49\", \"15:43\", 889, 943, 54],\n", + " [119, \"14:52\", \"15:21\", 892, 921, 29],\n", + " [120, \"14:58\", \"16:24\", 898, 984, 86],\n", + " [121, \"14:59\", \"15:53\", 899, 953, 54],\n", + " [122, \"15:00\", \"15:10\", 900, 910, 10],\n", + " [123, \"15:00\", \"15:35\", 900, 935, 35],\n", + " [124, \"15:08\", \"15:45\", 908, 945, 37],\n", + " [125, \"15:12\", \"15:36\", 912, 936, 24],\n", + " [126, \"15:18\", \"16:05\", 918, 965, 47],\n", + " [127, \"15:24\", \"16:05\", 924, 965, 41],\n", + " [128, \"15:31\", \"15:43\", 931, 943, 12],\n", + " [129, \"15:35\", \"15:54\", 935, 954, 19],\n", + " [130, \"15:36\", \"16:21\", 936, 981, 45],\n", + " [131, \"15:39\", \"16:33\", 939, 993, 54],\n", + " [132, \"15:48\", \"16:35\", 948, 995, 47],\n", + " [133, \"15:50\", \"17:01\", 950, 1021, 71],\n", + " [134, \"16:03\", \"16:50\", 963, 1010, 47],\n", + " [135, \"16:18\", \"17:44\", 978, 1064, 86],\n", + " [136, \"16:24\", \"17:05\", 984, 1025, 41],\n", + " [137, \"16:28\", \"17:15\", 988, 1035, 47],\n", + " [138, \"16:34\", \"17:15\", 994, 1035, 41],\n", + " [139, \"16:38\", \"17:25\", 998, 1045, 47],\n", + " [140, \"16:40\", \"16:56\", 1000, 1016, 16],\n", + " [141, \"16:45\", \"17:04\", 1005, 1024, 19],\n", + " [142, \"16:52\", \"17:36\", 1012, 1056, 44],\n", + " [143, \"16:58\", \"17:45\", 1018, 1065, 47],\n", + " [144, \"17:04\", \"18:30\", 1024, 1110, 86],\n", + " [145, \"17:04\", \"17:45\", 1024, 1065, 41],\n", + " [146, \"17:09\", \"18:03\", 1029, 1083, 54],\n", + " [147, \"17:18\", \"18:44\", 1038, 1124, 86],\n", + " [148, \"17:28\", \"18:15\", 1048, 1095, 47],\n", + " [149, \"17:29\", \"18:41\", 1049, 1121, 72],\n", + " [150, \"17:36\", \"18:21\", 1056, 1101, 45],\n", + " [151, \"17:38\", \"18:25\", 1058, 1105, 47],\n", + " [152, \"17:40\", \"17:56\", 1060, 1076, 16],\n", + " [153, \"17:45\", \"18:04\", 1065, 1084, 19],\n", + " [154, \"17:46\", \"17:58\", 1066, 1078, 12],\n", + " [155, \"17:48\", \"18:35\", 1068, 1115, 47],\n", + " [156, \"17:49\", \"18:43\", 1069, 1123, 54],\n", + " [157, \"17:55\", \"18:14\", 1075, 1094, 19],\n", + " [158, \"17:58\", \"18:45\", 1078, 1125, 47],\n", + " [159, \"18:00\", \"19:11\", 1080, 1151, 71],\n", + " [160, \"18:04\", \"18:45\", 1084, 1125, 41],\n", + " [161, \"18:09\", \"19:03\", 1089, 1143, 54],\n", + " [162, \"18:13\", \"19:00\", 1093, 1140, 47],\n", + " [163, \"18:13\", \"18:40\", 1093, 1120, 27],\n", + " [164, \"18:19\", \"19:13\", 1099, 1153, 54],\n", + " [165, \"18:28\", \"19:25\", 1108, 1165, 57],\n", + " [166, \"18:48\", \"19:28\", 1128, 1168, 40],\n", + " [167, \"19:03\", \"19:45\", 1143, 1185, 42],\n", + " [168, \"19:20\", \"19:36\", 1160, 1176, 16],\n", + " [169, \"19:21\", \"19:31\", 1161, 1171, 10],\n", + " [170, \"19:25\", \"20:04\", 1165, 1204, 39],\n", + " [171, \"19:26\", \"20:08\", 1166, 1208, 42],\n", + " [172, \"19:30\", \"19:40\", 1170, 1180, 10],\n", + " [173, \"19:44\", \"20:33\", 1184, 1233, 49],\n", + " [174, \"19:48\", \"21:09\", 1188, 1269, 81],\n", + " [175, \"19:53\", \"21:02\", 1193, 1262, 69],\n", + " [176, \"20:04\", \"20:29\", 1204, 1229, 25],\n", + " [177, \"20:17\", \"21:03\", 1217, 1263, 46],\n", + " [178, \"20:20\", \"20:57\", 1220, 1257, 37],\n", + " [179, \"20:29\", \"21:18\", 1229, 1278, 49],\n", + " [180, \"20:35\", \"21:54\", 1235, 1314, 79],\n", + " [181, \"20:40\", \"20:50\", 1240, 1250, 10],\n", + " [182, \"20:47\", \"21:42\", 1247, 1302, 55],\n", + " [183, \"21:00\", \"21:10\", 1260, 1270, 10],\n", + " [184, \"21:07\", \"21:44\", 1267, 1304, 37],\n", + " [185, \"21:14\", \"22:03\", 1274, 1323, 49],\n", + " [186, \"21:39\", \"21:55\", 1299, 1315, 16],\n", + " [187, \"21:40\", \"22:17\", 1300, 1337, 37],\n", + " [188, \"21:40\", \"21:50\", 1300, 1310, 10],\n", + " [189, \"21:48\", \"22:03\", 1308, 1323, 15],\n", + " [190, \"22:17\", \"23:03\", 1337, 1383, 46],\n", + " [191, \"22:43\", \"23:08\", 1363, 1388, 25],\n", + " [192, \"23:35\", \"01:05\", 1415, 1505, 90],\n", + " [193, \"23:46\", \"00:01\", 1426, 1441, 15],\n", + " [194, \"23:47\", \"00:33\", 1427, 1473, 46],\n", + " [195, \"23:52\", \"00:24\", 1432, 1464, 32],\n", + " [196, \"23:58\", \"00:38\", 1438, 1478, 40],\n", + " [197, \"00:02\", \"00:12\", 1442, 1452, 10],\n", + " [198, \"00:07\", \"00:39\", 1447, 1479, 32],\n", + " [199, \"00:25\", \"01:12\", 1465, 1512, 47],\n", "] # yapf:disable\n", "\n", "SAMPLE_SHIFTS_LARGE = [\n", - " [0, '04:18', '05:00', 258, 300, 42],\n", - " [1, '04:27', '05:08', 267, 308, 41],\n", - " [2, '04:29', '05:26', 269, 326, 57],\n", - " [3, '04:29', '04:55', 269, 295, 26],\n", - " [4, '04:30', '04:53', 270, 293, 23],\n", - " [5, '04:30', '04:51', 270, 291, 21],\n", - " [6, '04:31', '04:53', 271, 293, 22],\n", - " [7, '04:33', '05:15', 273, 315, 42],\n", - " [8, '04:34', '04:44', 274, 284, 10],\n", - " [9, '04:34', '05:03', 274, 303, 29],\n", - " [10, '04:35', '04:50', 275, 290, 15],\n", - " [11, '04:36', '04:46', 276, 286, 10],\n", - " [12, '04:37', '05:18', 277, 318, 41],\n", - " [13, '04:41', '05:13', 281, 313, 32],\n", - " [14, '04:42', '05:23', 282, 323, 41],\n", - " [15, '04:43', '04:53', 283, 293, 10],\n", - " [16, '04:44', '05:45', 284, 345, 61],\n", - " [17, '04:45', '05:11', 285, 311, 26],\n", - " [18, '04:46', '05:01', 286, 301, 15],\n", - " [19, '04:46', '04:56', 286, 296, 10],\n", - " [20, '04:47', '05:14', 287, 314, 27],\n", - " [21, '04:48', '05:30', 288, 330, 42],\n", - " [22, '04:49', '05:41', 289, 341, 52],\n", - " [23, '04:49', '05:18', 289, 318, 29],\n", - " [24, '04:50', '05:33', 290, 333, 43],\n", - " [25, '04:52', '05:56', 292, 356, 64],\n", - " [26, '04:52', '05:07', 292, 307, 15],\n", - " [27, '04:53', '05:19', 293, 319, 26],\n", - " [28, '04:53', '05:23', 293, 323, 30],\n", - " [29, '04:55', '05:27', 295, 327, 32],\n", - " [30, '04:57', '05:38', 297, 338, 41],\n", - " [31, '05:00', '06:00', 300, 360, 60],\n", - " [32, '05:00', '05:54', 300, 354, 54],\n", - " [33, '05:01', '05:33', 301, 333, 32],\n", - " [34, '05:01', '05:26', 301, 326, 25],\n", - " [35, '05:02', '05:29', 302, 329, 27],\n", - " [36, '05:02', '05:12', 302, 312, 10],\n", - " [37, '05:03', '05:45', 303, 345, 42],\n", - " [38, '05:03', '05:18', 303, 318, 15],\n", - " [39, '05:03', '06:28', 303, 388, 85],\n", - " [40, '05:03', '05:13', 303, 313, 10],\n", - " [41, '05:04', '06:24', 304, 384, 80],\n", - " [42, '05:07', '05:44', 307, 344, 37],\n", - " [43, '05:08', '05:48', 308, 348, 40],\n", - " [44, '05:10', '06:06', 310, 366, 56],\n", - " [45, '05:11', '05:37', 311, 337, 26],\n", - " [46, '05:11', '05:53', 311, 353, 42],\n", - " [47, '05:13', '06:15', 313, 375, 62],\n", - " [48, '05:13', '05:38', 313, 338, 25],\n", - " [49, '05:16', '05:44', 316, 344, 28],\n", - " [50, '05:17', '05:27', 317, 327, 10],\n", - " [51, '05:18', '06:40', 318, 400, 82],\n", - " [52, '05:18', '06:03', 318, 363, 45],\n", - " [53, '05:18', '06:11', 318, 371, 53],\n", - " [54, '05:18', '06:00', 318, 360, 42],\n", - " [55, '05:19', '06:34', 319, 394, 75],\n", - " [56, '05:20', '06:17', 320, 377, 57],\n", - " [57, '05:22', '05:59', 322, 359, 37],\n", - " [58, '05:24', '05:48', 324, 348, 24],\n", - " [59, '05:25', '05:40', 325, 340, 15],\n", - " [60, '05:26', '06:08', 326, 368, 42],\n", - " [61, '05:27', '06:30', 327, 390, 63],\n", - " [62, '05:27', '05:54', 327, 354, 27],\n", - " [63, '05:28', '05:53', 328, 353, 25],\n", - " [64, '05:29', '05:44', 329, 344, 15],\n", - " [65, '05:30', '05:40', 330, 340, 10],\n", - " [66, '05:30', '05:40', 330, 340, 10],\n", - " [67, '05:30', '05:40', 330, 340, 10],\n", - " [68, '05:32', '06:53', 332, 413, 81],\n", - " [69, '05:33', '07:00', 333, 420, 87],\n", - " [70, '05:33', '06:15', 333, 375, 42],\n", - " [71, '05:33', '05:47', 333, 347, 14],\n", - " [72, '05:37', '06:13', 337, 373, 36],\n", - " [73, '05:37', '06:05', 337, 365, 28],\n", - " [74, '05:38', '06:33', 338, 393, 55],\n", - " [75, '05:38', '06:04', 338, 364, 26],\n", - " [76, '05:38', '06:18', 338, 378, 40],\n", - " [77, '05:39', '05:54', 339, 354, 15],\n", - " [78, '05:40', '05:56', 340, 356, 16],\n", - " [79, '05:40', '06:41', 340, 401, 61],\n", - " [80, '05:40', '05:50', 340, 350, 10],\n", - " [81, '05:41', '06:23', 341, 383, 42],\n", - " [82, '05:41', '06:01', 341, 361, 20],\n", - " [83, '05:43', '06:08', 343, 368, 25],\n", - " [84, '05:44', '07:10', 344, 430, 86],\n", - " [85, '05:44', '05:55', 344, 355, 11],\n", - " [86, '05:45', '06:44', 345, 404, 59],\n", - " [87, '05:47', '06:17', 347, 377, 30],\n", - " [88, '05:48', '07:08', 348, 428, 80],\n", - " [89, '05:48', '06:30', 348, 390, 42],\n", - " [90, '05:50', '06:50', 350, 410, 60],\n", - " [91, '05:50', '06:00', 350, 360, 10],\n", - " [92, '05:50', '06:00', 350, 360, 10],\n", - " [93, '05:50', '06:51', 350, 411, 61],\n", - " [94, '05:52', '06:33', 352, 393, 41],\n", - " [95, '05:52', '06:36', 352, 396, 44],\n", - " [96, '05:52', '06:23', 352, 383, 31],\n", - " [97, '05:54', '06:14', 354, 374, 20],\n", - " [98, '05:54', '07:20', 354, 440, 86],\n", - " [99, '05:55', '06:40', 355, 400, 45],\n", - " [100, '05:55', '06:27', 355, 387, 32],\n", - " [101, '05:56', '06:35', 356, 395, 39],\n", - " [102, '05:56', '06:06', 356, 366, 10],\n", - " [103, '05:57', '06:21', 357, 381, 24],\n", - " [104, '05:58', '07:23', 358, 443, 85],\n", - " [105, '05:58', '06:23', 358, 383, 25],\n", - " [106, '05:58', '06:08', 358, 368, 10],\n", - " [107, '05:58', '06:43', 358, 403, 45],\n", - " [108, '06:00', '06:10', 360, 370, 10],\n", - " [109, '06:00', '06:16', 360, 376, 16],\n", - " [110, '06:00', '07:01', 360, 421, 61],\n", - " [111, '06:01', '07:00', 361, 420, 59],\n", - " [112, '06:01', '06:13', 361, 373, 12],\n", - " [113, '06:01', '06:45', 361, 405, 44],\n", - " [114, '06:03', '06:50', 363, 410, 47],\n", - " [115, '06:04', '06:37', 364, 397, 33],\n", - " [116, '06:04', '07:30', 364, 450, 86],\n", - " [117, '06:05', '06:24', 365, 384, 19],\n", - " [118, '06:06', '06:51', 366, 411, 45],\n", - " [119, '06:07', '06:43', 367, 403, 36],\n", - " [120, '06:08', '07:30', 368, 450, 82],\n", - " [121, '06:10', '06:20', 370, 380, 10],\n", - " [122, '06:10', '07:17', 370, 437, 67],\n", - " [123, '06:11', '06:54', 371, 414, 43],\n", - " [124, '06:11', '06:21', 371, 381, 10],\n", - " [125, '06:13', '06:38', 373, 398, 25],\n", - " [126, '06:13', '06:58', 373, 418, 45],\n", - " [127, '06:13', '06:53', 373, 413, 40],\n", - " [128, '06:14', '07:03', 374, 423, 49],\n", - " [129, '06:14', '06:47', 374, 407, 33],\n", - " [130, '06:14', '07:40', 374, 460, 86],\n", - " [131, '06:15', '07:15', 375, 435, 60],\n", - " [132, '06:16', '06:28', 376, 388, 12],\n", - " [133, '06:16', '06:26', 376, 386, 10],\n", - " [134, '06:17', '06:34', 377, 394, 17],\n", - " [135, '06:18', '07:06', 378, 426, 48],\n", - " [136, '06:18', '07:38', 378, 458, 80],\n", - " [137, '06:18', '07:02', 378, 422, 44],\n", - " [138, '06:19', '06:53', 379, 413, 34],\n", - " [139, '06:20', '07:25', 380, 445, 65],\n", - " [140, '06:20', '06:36', 380, 396, 16],\n", - " [141, '06:20', '06:30', 380, 390, 10],\n", - " [142, '06:20', '06:30', 380, 390, 10],\n", - " [143, '06:21', '06:49', 381, 409, 28],\n", - " [144, '06:22', '07:06', 382, 426, 44],\n", - " [145, '06:24', '07:50', 384, 470, 86],\n", - " [146, '06:24', '06:57', 384, 417, 33],\n", - " [147, '06:26', '07:45', 386, 465, 79],\n", - " [148, '06:26', '07:10', 386, 430, 44],\n", - " [149, '06:27', '06:44', 387, 404, 17],\n", - " [150, '06:28', '06:53', 388, 413, 25],\n", - " [151, '06:28', '07:14', 388, 434, 46],\n", - " [152, '06:29', '07:03', 389, 423, 34],\n", - " [153, '06:30', '06:40', 390, 400, 10],\n", - " [154, '06:30', '07:37', 390, 457, 67],\n", - " [155, '06:31', '06:43', 391, 403, 12],\n", - " [156, '06:33', '07:14', 393, 434, 41],\n", - " [157, '06:33', '07:53', 393, 473, 80],\n", - " [158, '06:34', '08:16', 394, 496, 102],\n", - " [159, '06:34', '07:09', 394, 429, 35],\n", - " [160, '06:34', '07:07', 394, 427, 33],\n", - " [161, '06:36', '07:21', 396, 441, 45],\n", - " [162, '06:37', '07:22', 397, 442, 45],\n", - " [163, '06:37', '06:54', 397, 414, 17],\n", - " [164, '06:38', '07:30', 398, 450, 52],\n", - " [165, '06:38', '07:18', 398, 438, 40],\n", - " [166, '06:39', '07:33', 399, 453, 54],\n", - " [167, '06:40', '07:52', 400, 472, 72],\n", - " [168, '06:40', '06:50', 400, 410, 10],\n", - " [169, '06:40', '07:22', 400, 442, 42],\n", - " [170, '06:40', '06:56', 400, 416, 16],\n", - " [171, '06:41', '08:00', 401, 480, 79],\n", - " [172, '06:42', '07:26', 402, 446, 44],\n", - " [173, '06:42', '07:13', 402, 433, 31],\n", - " [174, '06:43', '07:08', 403, 428, 25],\n", - " [175, '06:43', '07:30', 403, 450, 47],\n", - " [176, '06:43', '07:23', 403, 443, 40],\n", - " [177, '06:44', '07:17', 404, 437, 33],\n", - " [178, '06:44', '08:13', 404, 493, 89],\n", - " [179, '06:46', '07:01', 406, 421, 15],\n", - " [180, '06:46', '06:58', 406, 418, 12],\n", - " [181, '06:47', '07:04', 407, 424, 17],\n", - " [182, '06:48', '08:15', 408, 495, 87],\n", - " [183, '06:48', '07:34', 408, 454, 46],\n", - " [184, '06:48', '07:37', 408, 457, 49],\n", - " [185, '06:49', '07:43', 409, 463, 54],\n", - " [186, '06:50', '08:00', 410, 480, 70],\n", - " [187, '06:50', '07:00', 410, 420, 10],\n", - " [188, '06:50', '07:05', 410, 425, 15],\n", - " [189, '06:51', '07:18', 411, 438, 27],\n", - " [190, '06:52', '07:36', 412, 456, 44],\n", - " [191, '06:53', '07:37', 413, 457, 44],\n", - " [192, '06:54', '08:20', 414, 500, 86],\n", - " [193, '06:54', '07:27', 414, 447, 33],\n", - " [194, '06:54', '07:20', 414, 440, 26],\n", - " [195, '06:56', '08:23', 416, 503, 87],\n", - " [196, '06:57', '07:12', 417, 432, 15],\n", - " [197, '06:57', '07:58', 417, 478, 61],\n", - " [198, '06:57', '07:45', 417, 465, 48],\n", - " [199, '06:57', '07:40', 417, 460, 43],\n", - " [200, '06:58', '07:23', 418, 443, 25],\n", - " [201, '06:59', '07:53', 419, 473, 54],\n", - " [202, '06:59', '08:07', 419, 487, 68],\n", - " [203, '07:00', '07:10', 420, 430, 10],\n", - " [204, '07:00', '07:16', 420, 436, 16],\n", - " [205, '07:01', '08:30', 421, 510, 89],\n", - " [206, '07:01', '07:13', 421, 433, 12],\n", - " [207, '07:01', '07:43', 421, 463, 42],\n", - " [208, '07:03', '08:30', 423, 510, 87],\n", - " [209, '07:04', '07:37', 424, 457, 33],\n", - " [210, '07:04', '07:44', 424, 464, 40],\n", - " [211, '07:05', '07:52', 425, 472, 47],\n", - " [212, '07:05', '08:05', 425, 485, 60],\n", - " [213, '07:05', '07:46', 425, 466, 41],\n", - " [214, '07:06', '07:51', 426, 471, 45],\n", - " [215, '07:07', '08:08', 427, 488, 61],\n", - " [216, '07:07', '07:52', 427, 472, 45],\n", - " [217, '07:07', '08:16', 427, 496, 69],\n", - " [218, '07:07', '07:27', 427, 447, 20],\n", - " [219, '07:09', '07:50', 429, 470, 41],\n", - " [220, '07:09', '08:40', 429, 520, 91],\n", - " [221, '07:09', '08:03', 429, 483, 54],\n", - " [222, '07:10', '07:20', 430, 440, 10],\n", - " [223, '07:11', '08:36', 431, 516, 85],\n", - " [224, '07:12', '08:00', 432, 480, 48],\n", - " [225, '07:12', '07:47', 432, 467, 35],\n", - " [226, '07:13', '07:54', 433, 474, 41],\n", - " [227, '07:13', '07:38', 433, 458, 25],\n", - " [228, '07:14', '07:59', 434, 479, 45],\n", - " [229, '07:16', '08:50', 436, 530, 94],\n", - " [230, '07:16', '07:28', 436, 448, 12],\n", - " [231, '07:17', '07:35', 437, 455, 18],\n", - " [232, '07:17', '07:58', 437, 478, 41],\n", - " [233, '07:18', '08:06', 438, 486, 48],\n", - " [234, '07:18', '08:44', 438, 524, 86],\n", - " [235, '07:19', '08:13', 439, 493, 54],\n", - " [236, '07:20', '08:02', 440, 482, 42],\n", - " [237, '07:20', '08:07', 440, 487, 47],\n", - " [238, '07:20', '07:30', 440, 450, 10],\n", - " [239, '07:20', '07:57', 440, 477, 37],\n", - " [240, '07:20', '07:36', 440, 456, 16],\n", - " [241, '07:21', '07:48', 441, 468, 27],\n", - " [242, '07:22', '08:06', 442, 486, 44],\n", - " [243, '07:22', '08:25', 442, 505, 63],\n", - " [244, '07:24', '08:27', 444, 507, 63],\n", - " [245, '07:24', '08:05', 444, 485, 41],\n", - " [246, '07:26', '08:23', 446, 503, 57],\n", - " [247, '07:26', '08:52', 446, 532, 86],\n", - " [248, '07:27', '08:07', 447, 487, 40],\n", - " [249, '07:27', '07:42', 447, 462, 15],\n", - " [250, '07:27', '08:15', 447, 495, 48],\n", - " [251, '07:28', '07:53', 448, 473, 25],\n", - " [252, '07:28', '08:09', 448, 489, 41],\n", - " [253, '07:28', '07:38', 448, 458, 10],\n", - " [254, '07:30', '08:35', 450, 515, 65],\n", - " [255, '07:31', '07:43', 451, 463, 12],\n", - " [256, '07:32', '08:13', 452, 493, 41],\n", - " [257, '07:34', '09:00', 454, 540, 86],\n", - " [258, '07:34', '08:33', 454, 513, 59],\n", - " [259, '07:34', '09:04', 454, 544, 90],\n", - " [260, '07:35', '08:22', 455, 502, 47],\n", - " [261, '07:35', '07:45', 455, 465, 10],\n", - " [262, '07:35', '08:16', 455, 496, 41],\n", - " [263, '07:36', '08:17', 456, 497, 41],\n", - " [264, '07:36', '08:36', 456, 516, 60],\n", - " [265, '07:37', '07:50', 457, 470, 13],\n", - " [266, '07:40', '07:56', 460, 476, 16],\n", - " [267, '07:40', '08:20', 460, 500, 40],\n", - " [268, '07:40', '08:45', 460, 525, 65],\n", - " [269, '07:41', '08:39', 461, 519, 58],\n", - " [270, '07:41', '07:51', 461, 471, 10],\n", - " [271, '07:42', '08:30', 462, 510, 48],\n", - " [272, '07:42', '08:21', 462, 501, 39],\n", - " [273, '07:43', '08:08', 463, 488, 25],\n", - " [274, '07:43', '08:24', 463, 504, 41],\n", - " [275, '07:44', '09:10', 464, 550, 86],\n", - " [276, '07:44', '08:43', 464, 523, 59],\n", - " [277, '07:46', '08:28', 466, 508, 42],\n", - " [278, '07:46', '07:58', 466, 478, 12],\n", - " [279, '07:47', '08:00', 467, 480, 13],\n", - " [280, '07:48', '09:14', 468, 554, 86],\n", - " [281, '07:49', '08:32', 469, 512, 43],\n", - " [282, '07:50', '08:55', 470, 535, 65],\n", - " [283, '07:50', '08:00', 470, 480, 10],\n", - " [284, '07:50', '08:37', 470, 517, 47],\n", - " [285, '07:50', '08:26', 470, 506, 36],\n", - " [286, '07:51', '08:18', 471, 498, 27],\n", - " [287, '07:52', '08:21', 472, 501, 29],\n", - " [288, '07:53', '08:35', 473, 515, 42],\n", - " [289, '07:54', '09:19', 474, 559, 85],\n", - " [290, '07:55', '08:53', 475, 533, 58],\n", - " [291, '07:56', '08:54', 476, 534, 58],\n", - " [292, '07:57', '08:39', 477, 519, 42],\n", - " [293, '07:57', '08:10', 477, 490, 13],\n", - " [294, '07:58', '08:45', 478, 525, 47],\n", - " [295, '07:58', '08:23', 478, 503, 25],\n", - " [296, '08:00', '08:10', 480, 490, 10],\n", - " [297, '08:00', '09:05', 480, 545, 65],\n", - " [298, '08:00', '08:16', 480, 496, 16],\n", - " [299, '08:00', '08:35', 480, 515, 35],\n", - " [300, '08:01', '08:13', 481, 493, 12],\n", - " [301, '08:01', '08:43', 481, 523, 42],\n", - " [302, '08:03', '09:26', 483, 566, 83],\n", - " [303, '08:04', '09:29', 484, 569, 85],\n", - " [304, '08:05', '08:21', 485, 501, 16],\n", - " [305, '08:05', '08:47', 485, 527, 42],\n", - " [306, '08:06', '08:51', 486, 531, 45],\n", - " [307, '08:06', '09:03', 486, 543, 57],\n", - " [308, '08:07', '08:20', 487, 500, 13],\n", - " [309, '08:08', '08:55', 488, 535, 47],\n", - " [310, '08:08', '08:50', 488, 530, 42],\n", - " [311, '08:10', '08:45', 490, 525, 35],\n", - " [312, '08:10', '09:15', 490, 555, 65],\n", - " [313, '08:10', '08:20', 490, 500, 10],\n", - " [314, '08:11', '09:41', 491, 581, 90],\n", - " [315, '08:12', '08:55', 492, 535, 43],\n", - " [316, '08:13', '08:38', 493, 518, 25],\n", - " [317, '08:14', '09:38', 494, 578, 84],\n", - " [318, '08:15', '08:30', 495, 510, 15],\n", - " [319, '08:16', '08:30', 496, 510, 14],\n", - " [320, '08:16', '08:28', 496, 508, 12],\n", - " [321, '08:16', '09:00', 496, 540, 44],\n", - " [322, '08:17', '09:13', 497, 553, 56],\n", - " [323, '08:18', '09:16', 498, 556, 58],\n", - " [324, '08:18', '09:05', 498, 545, 47],\n", - " [325, '08:20', '08:36', 500, 516, 16],\n", - " [326, '08:20', '08:55', 500, 535, 35],\n", - " [327, '08:20', '09:05', 500, 545, 45],\n", - " [328, '08:20', '08:30', 500, 510, 10],\n", - " [329, '08:20', '09:25', 500, 565, 65],\n", - " [330, '08:21', '08:38', 501, 518, 17],\n", - " [331, '08:21', '08:47', 501, 527, 26],\n", - " [332, '08:22', '08:45', 502, 525, 23],\n", - " [333, '08:23', '09:10', 503, 550, 47],\n", - " [334, '08:24', '09:48', 504, 588, 84],\n", - " [335, '08:26', '08:46', 506, 526, 20],\n", - " [336, '08:27', '09:07', 507, 547, 40],\n", - " [337, '08:28', '08:50', 508, 530, 22],\n", - " [338, '08:28', '09:56', 508, 596, 88],\n", - " [339, '08:28', '09:23', 508, 563, 55],\n", - " [340, '08:29', '09:20', 509, 560, 51],\n", - " [341, '08:30', '09:05', 510, 545, 35],\n", - " [342, '08:30', '08:45', 510, 525, 15],\n", - " [343, '08:30', '08:40', 510, 520, 10],\n", - " [344, '08:30', '09:35', 510, 575, 65],\n", - " [345, '08:31', '08:43', 511, 523, 12],\n", - " [346, '08:31', '09:13', 511, 553, 42],\n", - " [347, '08:34', '09:58', 514, 598, 84],\n", - " [348, '08:35', '08:55', 515, 535, 20],\n", - " [349, '08:35', '09:15', 515, 555, 40],\n", - " [350, '08:35', '08:45', 515, 525, 10],\n", - " [351, '08:36', '08:46', 516, 526, 10],\n", - " [352, '08:36', '09:00', 516, 540, 24],\n", - " [353, '08:38', '09:20', 518, 560, 42],\n", - " [354, '08:38', '09:35', 518, 575, 57],\n", - " [355, '08:38', '09:14', 518, 554, 36],\n", - " [356, '08:39', '09:33', 519, 573, 54],\n", - " [357, '08:40', '09:45', 520, 585, 65],\n", - " [358, '08:40', '08:50', 520, 530, 10],\n", - " [359, '08:40', '08:56', 520, 536, 16],\n", - " [360, '08:42', '09:25', 522, 565, 43],\n", - " [361, '08:43', '09:08', 523, 548, 25],\n", - " [362, '08:44', '09:35', 524, 575, 51],\n", - " [363, '08:45', '09:00', 525, 540, 15],\n", - " [364, '08:45', '09:05', 525, 545, 20],\n", - " [365, '08:46', '09:24', 526, 564, 38],\n", - " [366, '08:46', '08:58', 526, 538, 12],\n", - " [367, '08:46', '09:30', 526, 570, 44],\n", - " [368, '08:48', '10:11', 528, 611, 83],\n", - " [369, '08:48', '10:13', 528, 613, 85],\n", - " [370, '08:49', '09:43', 529, 583, 54],\n", - " [371, '08:50', '09:30', 530, 570, 40],\n", - " [372, '08:50', '10:00', 530, 600, 70],\n", - " [373, '08:50', '09:00', 530, 540, 10],\n", - " [374, '08:51', '09:17', 531, 557, 26],\n", - " [375, '08:53', '09:20', 533, 560, 27],\n", - " [376, '08:53', '09:35', 533, 575, 42],\n", - " [377, '08:55', '09:34', 535, 574, 39],\n", - " [378, '08:55', '09:15', 535, 555, 20],\n", - " [379, '08:58', '09:38', 538, 578, 40],\n", - " [380, '08:58', '10:26', 538, 626, 88],\n", - " [381, '08:59', '09:53', 539, 593, 54],\n", - " [382, '08:59', '09:50', 539, 590, 51],\n", - " [383, '09:00', '09:35', 540, 575, 35],\n", - " [384, '09:00', '09:16', 540, 556, 16],\n", - " [385, '09:00', '09:10', 540, 550, 10],\n", - " [386, '09:00', '09:16', 540, 556, 16],\n", - " [387, '09:01', '09:13', 541, 553, 12],\n", - " [388, '09:03', '09:45', 543, 585, 42],\n", - " [389, '09:03', '10:28', 543, 628, 85],\n", - " [390, '09:05', '09:44', 545, 584, 39],\n", - " [391, '09:05', '09:25', 545, 565, 20],\n", - " [392, '09:08', '09:53', 548, 593, 45],\n", - " [393, '09:08', '10:04', 548, 604, 56],\n", - " [394, '09:09', '10:03', 549, 603, 54],\n", - " [395, '09:10', '10:15', 550, 615, 65],\n", - " [396, '09:10', '09:20', 550, 560, 10],\n", - " [397, '09:11', '09:38', 551, 578, 27],\n", - " [398, '09:13', '10:00', 553, 600, 47],\n", - " [399, '09:14', '09:39', 554, 579, 25],\n", - " [400, '09:14', '10:05', 554, 605, 51],\n", - " [401, '09:15', '09:54', 555, 594, 39],\n", - " [402, '09:16', '09:28', 556, 568, 12],\n", - " [403, '09:18', '10:43', 558, 643, 85],\n", - " [404, '09:18', '10:41', 558, 641, 83],\n", - " [405, '09:18', '09:58', 558, 598, 40],\n", - " [406, '09:19', '10:13', 559, 613, 54],\n", - " [407, '09:20', '09:30', 560, 570, 10],\n", - " [408, '09:20', '09:36', 560, 576, 16],\n", - " [409, '09:21', '09:47', 561, 587, 26],\n", - " [410, '09:23', '10:30', 563, 630, 67],\n", - " [411, '09:23', '10:05', 563, 605, 42],\n", - " [412, '09:23', '09:49', 563, 589, 26],\n", - " [413, '09:24', '09:35', 564, 575, 11],\n", - " [414, '09:25', '09:35', 565, 575, 10],\n", - " [415, '09:25', '10:04', 565, 604, 39],\n", - " [416, '09:28', '10:08', 568, 608, 40],\n", - " [417, '09:29', '09:45', 569, 585, 16],\n", - " [418, '09:29', '10:20', 569, 620, 51],\n", - " [419, '09:29', '10:56', 569, 656, 87],\n", - " [420, '09:29', '10:23', 569, 623, 54],\n", - " [421, '09:30', '09:40', 570, 580, 10],\n", - " [422, '09:31', '09:43', 571, 583, 12],\n", - " [423, '09:33', '10:58', 573, 658, 85],\n", - " [424, '09:33', '10:15', 573, 615, 42],\n", - " [425, '09:34', '09:45', 574, 585, 11],\n", - " [426, '09:35', '10:14', 575, 614, 39],\n", - " [427, '09:38', '10:45', 578, 645, 67],\n", - " [428, '09:39', '10:33', 579, 633, 54],\n", - " [429, '09:40', '09:56', 580, 596, 16],\n", - " [430, '09:40', '09:50', 580, 590, 10],\n", - " [431, '09:41', '10:08', 581, 608, 27],\n", - " [432, '09:41', '10:23', 581, 623, 42],\n", - " [433, '09:44', '10:35', 584, 635, 51],\n", - " [434, '09:44', '11:11', 584, 671, 87],\n", - " [435, '09:44', '09:55', 584, 595, 11],\n", - " [436, '09:45', '10:24', 585, 624, 39],\n", - " [437, '09:46', '09:58', 586, 598, 12],\n", - " [438, '09:48', '10:30', 588, 630, 42],\n", - " [439, '09:48', '11:13', 588, 673, 85],\n", - " [440, '09:48', '10:04', 588, 604, 16],\n", - " [441, '09:49', '10:43', 589, 643, 54],\n", - " [442, '09:50', '10:00', 590, 600, 10],\n", - " [443, '09:51', '10:17', 591, 617, 26],\n", - " [444, '09:53', '10:49', 593, 649, 56],\n", - " [445, '09:53', '11:00', 593, 660, 67],\n", - " [446, '09:54', '10:05', 594, 605, 11],\n", - " [447, '09:55', '10:34', 595, 634, 39],\n", - " [448, '09:56', '10:38', 596, 638, 42],\n", - " [449, '09:57', '10:20', 597, 620, 23],\n", - " [450, '09:59', '11:26', 599, 686, 87],\n", - " [451, '09:59', '10:50', 599, 650, 51],\n", - " [452, '09:59', '10:53', 599, 653, 54],\n", - " [453, '10:00', '10:16', 600, 616, 16],\n", - " [454, '10:00', '10:10', 600, 610, 10],\n", - " [455, '10:01', '10:13', 601, 613, 12],\n", - " [456, '10:03', '11:28', 603, 688, 85],\n", - " [457, '10:03', '10:45', 603, 645, 42],\n", - " [458, '10:04', '10:15', 604, 615, 11],\n", - " [459, '10:05', '10:44', 605, 644, 39],\n", - " [460, '10:08', '11:15', 608, 675, 67],\n", - " [461, '10:09', '11:03', 609, 663, 54],\n", - " [462, '10:10', '10:20', 610, 620, 10],\n", - " [463, '10:11', '10:38', 611, 638, 27],\n", - " [464, '10:11', '10:53', 611, 653, 42],\n", - " [465, '10:14', '11:05', 614, 665, 51],\n", - " [466, '10:14', '11:41', 614, 701, 87],\n", - " [467, '10:14', '10:25', 614, 625, 11],\n", - " [468, '10:15', '10:54', 615, 654, 39],\n", - " [469, '10:16', '10:28', 616, 628, 12],\n", - " [470, '10:18', '11:43', 618, 703, 85],\n", - " [471, '10:18', '11:00', 618, 660, 42],\n", - " [472, '10:19', '11:13', 619, 673, 54],\n", - " [473, '10:20', '10:30', 620, 630, 10],\n", - " [474, '10:20', '10:36', 620, 636, 16],\n", - " [475, '10:21', '10:47', 621, 647, 26],\n", - " [476, '10:23', '11:30', 623, 690, 67],\n", - " [477, '10:23', '10:45', 623, 645, 22],\n", - " [478, '10:24', '10:35', 624, 635, 11],\n", - " [479, '10:25', '11:04', 625, 664, 39],\n", - " [480, '10:26', '11:08', 626, 668, 42],\n", - " [481, '10:29', '11:20', 629, 680, 51],\n", - " [482, '10:29', '11:23', 629, 683, 54],\n", - " [483, '10:29', '11:56', 629, 716, 87],\n", - " [484, '10:30', '10:40', 630, 640, 10],\n", - " [485, '10:31', '10:43', 631, 643, 12],\n", - " [486, '10:33', '11:15', 633, 675, 42],\n", - " [487, '10:33', '11:58', 633, 718, 85],\n", - " [488, '10:34', '10:45', 634, 645, 11],\n", - " [489, '10:35', '11:14', 635, 674, 39],\n", - " [490, '10:38', '11:45', 638, 705, 67],\n", - " [491, '10:39', '11:33', 639, 693, 54],\n", - " [492, '10:40', '10:50', 640, 650, 10],\n", - " [493, '10:40', '10:56', 640, 656, 16],\n", - " [494, '10:41', '11:23', 641, 683, 42],\n", - " [495, '10:41', '11:08', 641, 668, 27],\n", - " [496, '10:44', '12:11', 644, 731, 87],\n", - " [497, '10:44', '11:35', 644, 695, 51],\n", - " [498, '10:44', '10:55', 644, 655, 11],\n", - " [499, '10:45', '11:24', 645, 684, 39],\n", - " [500, '10:46', '10:58', 646, 658, 12],\n", - " [501, '10:48', '12:13', 648, 733, 85],\n", - " [502, '10:48', '11:30', 648, 690, 42],\n", - " [503, '10:49', '11:43', 649, 703, 54],\n", - " [504, '10:50', '11:00', 650, 660, 10],\n", - " [505, '10:51', '11:17', 651, 677, 26],\n", - " [506, '10:53', '12:00', 653, 720, 67],\n", - " [507, '10:53', '11:20', 653, 680, 27],\n", - " [508, '10:54', '11:05', 654, 665, 11],\n", - " [509, '10:55', '11:34', 655, 694, 39],\n", - " [510, '10:56', '11:38', 656, 698, 42],\n", - " [511, '10:59', '11:14', 659, 674, 15],\n", - " [512, '10:59', '12:26', 659, 746, 87],\n", - " [513, '10:59', '11:53', 659, 713, 54],\n", - " [514, '10:59', '11:50', 659, 710, 51],\n", - " [515, '11:00', '11:16', 660, 676, 16],\n", - " [516, '11:00', '11:10', 660, 670, 10],\n", - " [517, '11:01', '11:13', 661, 673, 12],\n", - " [518, '11:03', '11:45', 663, 705, 42],\n", - " [519, '11:03', '12:28', 663, 748, 85],\n", - " [520, '11:04', '11:15', 664, 675, 11],\n", - " [521, '11:05', '11:44', 665, 704, 39],\n", - " [522, '11:08', '12:15', 668, 735, 67],\n", - " [523, '11:09', '12:03', 669, 723, 54],\n", - " [524, '11:10', '11:20', 670, 680, 10],\n", - " [525, '11:11', '11:38', 671, 698, 27],\n", - " [526, '11:11', '11:53', 671, 713, 42],\n", - " [527, '11:14', '11:25', 674, 685, 11],\n", - " [528, '11:14', '12:05', 674, 725, 51],\n", - " [529, '11:14', '12:38', 674, 758, 84],\n", - " [530, '11:14', '12:41', 674, 761, 87],\n", - " [531, '11:15', '11:54', 675, 714, 39],\n", - " [532, '11:16', '11:28', 676, 688, 12],\n", - " [533, '11:18', '12:00', 678, 720, 42],\n", - " [534, '11:19', '12:13', 679, 733, 54],\n", - " [535, '11:20', '11:30', 680, 690, 10],\n", - " [536, '11:20', '11:36', 680, 696, 16],\n", - " [537, '11:21', '11:47', 681, 707, 26],\n", - " [538, '11:23', '12:30', 683, 750, 67],\n", - " [539, '11:23', '11:49', 683, 709, 26],\n", - " [540, '11:24', '12:48', 684, 768, 84],\n", - " [541, '11:24', '11:35', 684, 695, 11],\n", - " [542, '11:25', '12:04', 685, 724, 39],\n", - " [543, '11:26', '12:08', 686, 728, 42],\n", - " [544, '11:29', '11:44', 689, 704, 15],\n", - " [545, '11:29', '12:23', 689, 743, 54],\n", - " [546, '11:29', '12:20', 689, 740, 51],\n", - " [547, '11:29', '12:54', 689, 774, 85],\n", - " [548, '11:30', '11:40', 690, 700, 10],\n", - " [549, '11:31', '11:43', 691, 703, 12],\n", - " [550, '11:33', '12:15', 693, 735, 42],\n", - " [551, '11:34', '12:58', 694, 778, 84],\n", - " [552, '11:34', '11:45', 694, 705, 11],\n", - " [553, '11:35', '12:14', 695, 734, 39],\n", - " [554, '11:38', '12:45', 698, 765, 67],\n", - " [555, '11:39', '12:33', 699, 753, 54],\n", - " [556, '11:40', '11:56', 700, 716, 16],\n", - " [557, '11:40', '11:50', 700, 710, 10],\n", - " [558, '11:41', '12:08', 701, 728, 27],\n", - " [559, '11:41', '12:23', 701, 743, 42],\n", - " [560, '11:44', '11:55', 704, 715, 11],\n", - " [561, '11:44', '13:14', 704, 794, 90],\n", - " [562, '11:44', '13:08', 704, 788, 84],\n", - " [563, '11:44', '12:35', 704, 755, 51],\n", - " [564, '11:45', '12:24', 705, 744, 39],\n", - " [565, '11:46', '11:58', 706, 718, 12],\n", - " [566, '11:48', '12:30', 708, 750, 42],\n", - " [567, '11:49', '12:43', 709, 763, 54],\n", - " [568, '11:50', '12:00', 710, 720, 10],\n", - " [569, '11:51', '12:17', 711, 737, 26],\n", - " [570, '11:53', '12:49', 713, 769, 56],\n", - " [571, '11:53', '13:00', 713, 780, 67],\n", - " [572, '11:54', '13:18', 714, 798, 84],\n", - " [573, '11:54', '12:05', 714, 725, 11],\n", - " [574, '11:55', '12:40', 715, 760, 45],\n", - " [575, '11:55', '12:34', 715, 754, 39],\n", - " [576, '11:56', '12:35', 716, 755, 39],\n", - " [577, '11:57', '12:20', 717, 740, 23],\n", - " [578, '11:58', '12:29', 718, 749, 31],\n", - " [579, '11:59', '12:50', 719, 770, 51],\n", - " [580, '11:59', '12:53', 719, 773, 54],\n", - " [581, '11:59', '13:24', 719, 804, 85],\n", - " [582, '11:59', '12:14', 719, 734, 15],\n", - " [583, '12:00', '12:16', 720, 736, 16],\n", - " [584, '12:00', '12:10', 720, 730, 10],\n", - " [585, '12:01', '12:45', 721, 765, 44],\n", - " [586, '12:01', '12:13', 721, 733, 12],\n", - " [587, '12:03', '12:50', 723, 770, 47],\n", - " [588, '12:04', '12:15', 724, 735, 11],\n", - " [589, '12:04', '13:04', 724, 784, 60],\n", - " [590, '12:04', '13:28', 724, 808, 84],\n", - " [591, '12:05', '12:44', 725, 764, 39],\n", - " [592, '12:08', '13:11', 728, 791, 63],\n", - " [593, '12:08', '12:39', 728, 759, 31],\n", - " [594, '12:09', '13:03', 729, 783, 54],\n", - " [595, '12:10', '12:20', 730, 740, 10],\n", - " [596, '12:11', '12:55', 731, 775, 44],\n", - " [597, '12:11', '12:38', 731, 758, 27],\n", - " [598, '12:14', '13:05', 734, 785, 51],\n", - " [599, '12:14', '12:25', 734, 745, 11],\n", - " [600, '12:14', '13:44', 734, 824, 90],\n", - " [601, '12:14', '13:38', 734, 818, 84],\n", - " [602, '12:15', '12:54', 735, 774, 39],\n", - " [603, '12:16', '12:28', 736, 748, 12],\n", - " [604, '12:18', '13:00', 738, 780, 42],\n", - " [605, '12:19', '13:13', 739, 793, 54],\n", - " [606, '12:20', '12:30', 740, 750, 10],\n", - " [607, '12:20', '13:31', 740, 811, 71],\n", - " [608, '12:20', '12:30', 740, 750, 10],\n", - " [609, '12:20', '12:36', 740, 756, 16],\n", - " [610, '12:21', '12:47', 741, 767, 26],\n", - " [611, '12:23', '12:45', 743, 765, 22],\n", - " [612, '12:24', '12:35', 744, 755, 11],\n", - " [613, '12:24', '13:48', 744, 828, 84],\n", - " [614, '12:25', '13:10', 745, 790, 45],\n", - " [615, '12:25', '13:04', 745, 784, 39],\n", - " [616, '12:26', '13:05', 746, 785, 39],\n", - " [617, '12:28', '13:54', 748, 834, 86],\n", - " [618, '12:28', '12:38', 748, 758, 10],\n", - " [619, '12:28', '13:15', 748, 795, 47],\n", - " [620, '12:29', '13:23', 749, 803, 54],\n", - " [621, '12:30', '13:41', 750, 821, 71],\n", - " [622, '12:30', '12:40', 750, 760, 10],\n", - " [623, '12:31', '13:15', 751, 795, 44],\n", - " [624, '12:31', '12:43', 751, 763, 12],\n", - " [625, '12:33', '12:48', 753, 768, 15],\n", - " [626, '12:33', '13:20', 753, 800, 47],\n", - " [627, '12:34', '13:58', 754, 838, 84],\n", - " [628, '12:34', '13:34', 754, 814, 60],\n", - " [629, '12:34', '12:45', 754, 765, 11],\n", - " [630, '12:35', '13:14', 755, 794, 39],\n", - " [631, '12:38', '13:25', 758, 805, 47],\n", - " [632, '12:38', '13:25', 758, 805, 47],\n", - " [633, '12:38', '14:04', 758, 844, 86],\n", - " [634, '12:39', '13:33', 759, 813, 54],\n", - " [635, '12:40', '13:51', 760, 831, 71],\n", - " [636, '12:40', '12:50', 760, 770, 10],\n", - " [637, '12:40', '12:56', 760, 776, 16],\n", - " [638, '12:41', '13:08', 761, 788, 27],\n", - " [639, '12:43', '13:30', 763, 810, 47],\n", - " [640, '12:44', '12:55', 764, 775, 11],\n", - " [641, '12:44', '14:08', 764, 848, 84],\n", - " [642, '12:45', '13:24', 765, 804, 39],\n", - " [643, '12:46', '12:58', 766, 778, 12],\n", - " [644, '12:46', '13:21', 766, 801, 35],\n", - " [645, '12:48', '14:14', 768, 854, 86],\n", - " [646, '12:48', '13:35', 768, 815, 47],\n", - " [647, '12:48', '12:58', 768, 778, 10],\n", - " [648, '12:48', '13:35', 768, 815, 47],\n", - " [649, '12:49', '13:43', 769, 823, 54],\n", - " [650, '12:50', '14:01', 770, 841, 71],\n", - " [651, '12:50', '13:00', 770, 780, 10],\n", - " [652, '12:50', '13:00', 770, 780, 10],\n", - " [653, '12:51', '13:17', 771, 797, 26],\n", - " [654, '12:53', '13:20', 773, 800, 27],\n", - " [655, '12:53', '13:24', 773, 804, 31],\n", - " [656, '12:53', '13:40', 773, 820, 47],\n", - " [657, '12:54', '14:18', 774, 858, 84],\n", - " [658, '12:54', '13:05', 774, 785, 11],\n", - " [659, '12:55', '13:34', 775, 814, 39],\n", - " [660, '12:58', '14:24', 778, 864, 86],\n", - " [661, '12:58', '13:25', 778, 805, 27],\n", - " [662, '12:58', '13:45', 778, 825, 47],\n", - " [663, '12:58', '13:45', 778, 825, 47],\n", - " [664, '12:59', '13:53', 779, 833, 54],\n", - " [665, '13:00', '13:10', 780, 790, 10],\n", - " [666, '13:00', '13:16', 780, 796, 16],\n", - " [667, '13:00', '14:11', 780, 851, 71],\n", - " [668, '13:01', '13:13', 781, 793, 12],\n", - " [669, '13:03', '13:34', 783, 814, 31],\n", - " [670, '13:03', '13:50', 783, 830, 47],\n", - " [671, '13:04', '13:15', 784, 795, 11],\n", - " [672, '13:04', '14:28', 784, 868, 84],\n", - " [673, '13:05', '13:44', 785, 824, 39],\n", - " [674, '13:08', '13:55', 788, 835, 47],\n", - " [675, '13:08', '14:34', 788, 874, 86],\n", - " [676, '13:08', '13:55', 788, 835, 47],\n", - " [677, '13:09', '14:03', 789, 843, 54],\n", - " [678, '13:10', '13:20', 790, 800, 10],\n", - " [679, '13:10', '14:21', 790, 861, 71],\n", - " [680, '13:13', '14:00', 793, 840, 47],\n", - " [681, '13:13', '13:40', 793, 820, 27],\n", - " [682, '13:14', '14:38', 794, 878, 84],\n", - " [683, '13:14', '13:25', 794, 805, 11],\n", - " [684, '13:15', '13:54', 795, 834, 39],\n", - " [685, '13:16', '13:28', 796, 808, 12],\n", - " [686, '13:18', '14:05', 798, 845, 47],\n", - " [687, '13:18', '14:44', 798, 884, 86],\n", - " [688, '13:18', '14:05', 798, 845, 47],\n", - " [689, '13:19', '14:13', 799, 853, 54],\n", - " [690, '13:20', '13:36', 800, 816, 16],\n", - " [691, '13:20', '14:31', 800, 871, 71],\n", - " [692, '13:20', '13:30', 800, 810, 10],\n", - " [693, '13:21', '13:47', 801, 827, 26],\n", - " [694, '13:23', '14:10', 803, 850, 47],\n", - " [695, '13:23', '13:49', 803, 829, 26],\n", - " [696, '13:24', '14:48', 804, 888, 84],\n", - " [697, '13:24', '13:35', 804, 815, 11],\n", - " [698, '13:25', '14:04', 805, 844, 39],\n", - " [699, '13:28', '14:15', 808, 855, 47],\n", - " [700, '13:28', '14:54', 808, 894, 86],\n", - " [701, '13:28', '13:55', 808, 835, 27],\n", - " [702, '13:28', '14:15', 808, 855, 47],\n", - " [703, '13:29', '14:23', 809, 863, 54],\n", - " [704, '13:30', '13:40', 810, 820, 10],\n", - " [705, '13:30', '14:41', 810, 881, 71],\n", - " [706, '13:31', '13:43', 811, 823, 12],\n", - " [707, '13:33', '14:20', 813, 860, 47],\n", - " [708, '13:34', '14:58', 814, 898, 84],\n", - " [709, '13:34', '13:45', 814, 825, 11],\n", - " [710, '13:35', '14:14', 815, 854, 39],\n", - " [711, '13:38', '14:25', 818, 865, 47],\n", - " [712, '13:38', '14:25', 818, 865, 47],\n", - " [713, '13:38', '15:04', 818, 904, 86],\n", - " [714, '13:39', '14:33', 819, 873, 54],\n", - " [715, '13:40', '13:50', 820, 830, 10],\n", - " [716, '13:40', '13:56', 820, 836, 16],\n", - " [717, '13:40', '14:51', 820, 891, 71],\n", - " [718, '13:43', '14:30', 823, 870, 47],\n", - " [719, '13:43', '14:10', 823, 850, 27],\n", - " [720, '13:44', '15:09', 824, 909, 85],\n", - " [721, '13:44', '13:55', 824, 835, 11],\n", - " [722, '13:45', '14:24', 825, 864, 39],\n", - " [723, '13:46', '13:58', 826, 838, 12],\n", - " [724, '13:48', '14:35', 828, 875, 47],\n", - " [725, '13:48', '15:14', 828, 914, 86],\n", - " [726, '13:48', '14:35', 828, 875, 47],\n", - " [727, '13:49', '14:43', 829, 883, 54],\n", - " [728, '13:50', '14:00', 830, 840, 10],\n", - " [729, '13:50', '15:01', 830, 901, 71],\n", - " [730, '13:51', '14:17', 831, 857, 26],\n", - " [731, '13:53', '14:40', 833, 880, 47],\n", - " [732, '13:53', '14:49', 833, 889, 56],\n", - " [733, '13:54', '14:05', 834, 845, 11],\n", - " [734, '13:54', '15:19', 834, 919, 85],\n", - " [735, '13:55', '14:34', 835, 874, 39],\n", - " [736, '13:57', '14:20', 837, 860, 23],\n", - " [737, '13:58', '15:24', 838, 924, 86],\n", - " [738, '13:58', '14:45', 838, 885, 47],\n", - " [739, '13:58', '14:45', 838, 885, 47],\n", - " [740, '13:58', '14:25', 838, 865, 27],\n", - " [741, '13:59', '14:53', 839, 893, 54],\n", - " [742, '14:00', '14:16', 840, 856, 16],\n", - " [743, '14:00', '14:10', 840, 850, 10],\n", - " [744, '14:00', '15:11', 840, 911, 71],\n", - " [745, '14:01', '14:13', 841, 853, 12],\n", - " [746, '14:03', '14:50', 843, 890, 47],\n", - " [747, '14:04', '14:15', 844, 855, 11],\n", - " [748, '14:04', '15:29', 844, 929, 85],\n", - " [749, '14:05', '14:44', 845, 884, 39],\n", - " [750, '14:08', '14:55', 848, 895, 47],\n", - " [751, '14:08', '14:55', 848, 895, 47],\n", - " [752, '14:08', '15:34', 848, 934, 86],\n", - " [753, '14:09', '15:03', 849, 903, 54],\n", - " [754, '14:10', '15:21', 850, 921, 71],\n", - " [755, '14:10', '14:20', 850, 860, 10],\n", - " [756, '14:13', '15:00', 853, 900, 47],\n", - " [757, '14:13', '14:40', 853, 880, 27],\n", - " [758, '14:14', '15:40', 854, 940, 86],\n", - " [759, '14:14', '14:25', 854, 865, 11],\n", - " [760, '14:15', '14:54', 855, 894, 39],\n", - " [761, '14:16', '14:28', 856, 868, 12],\n", - " [762, '14:18', '15:05', 858, 905, 47],\n", - " [763, '14:18', '15:44', 858, 944, 86],\n", - " [764, '14:18', '15:05', 858, 905, 47],\n", - " [765, '14:19', '15:13', 859, 913, 54],\n", - " [766, '14:20', '15:31', 860, 931, 71],\n", - " [767, '14:20', '14:30', 860, 870, 10],\n", - " [768, '14:20', '14:36', 860, 876, 16],\n", - " [769, '14:21', '14:47', 861, 887, 26],\n", - " [770, '14:23', '15:10', 863, 910, 47],\n", - " [771, '14:23', '14:45', 863, 885, 22],\n", - " [772, '14:24', '15:50', 864, 950, 86],\n", - " [773, '14:24', '14:35', 864, 875, 11],\n", - " [774, '14:25', '15:02', 865, 902, 37],\n", - " [775, '14:26', '14:52', 866, 892, 26],\n", - " [776, '14:28', '15:15', 868, 915, 47],\n", - " [777, '14:28', '14:55', 868, 895, 27],\n", - " [778, '14:28', '15:54', 868, 954, 86],\n", - " [779, '14:28', '15:15', 868, 915, 47],\n", - " [780, '14:29', '15:23', 869, 923, 54],\n", - " [781, '14:30', '15:41', 870, 941, 71],\n", - " [782, '14:30', '14:40', 870, 880, 10],\n", - " [783, '14:31', '14:43', 871, 883, 12],\n", - " [784, '14:33', '15:20', 873, 920, 47],\n", - " [785, '14:34', '16:00', 874, 960, 86],\n", - " [786, '14:34', '14:45', 874, 885, 11],\n", - " [787, '14:35', '15:11', 875, 911, 36],\n", - " [788, '14:38', '15:25', 878, 925, 47],\n", - " [789, '14:38', '15:25', 878, 925, 47],\n", - " [790, '14:38', '16:04', 878, 964, 86],\n", - " [791, '14:39', '15:33', 879, 933, 54],\n", - " [792, '14:40', '14:50', 880, 890, 10],\n", - " [793, '14:40', '15:51', 880, 951, 71],\n", - " [794, '14:40', '14:56', 880, 896, 16],\n", - " [795, '14:43', '15:30', 883, 930, 47],\n", - " [796, '14:43', '15:10', 883, 910, 27],\n", - " [797, '14:44', '15:00', 884, 900, 16],\n", - " [798, '14:44', '16:10', 884, 970, 86],\n", - " [799, '14:45', '15:19', 885, 919, 34],\n", - " [800, '14:46', '14:58', 886, 898, 12],\n", - " [801, '14:48', '15:35', 888, 935, 47],\n", - " [802, '14:48', '15:35', 888, 935, 47],\n", - " [803, '14:48', '17:04', 888, 1024, 136],\n", - " [804, '14:49', '15:43', 889, 943, 54],\n", - " [805, '14:50', '16:01', 890, 961, 71],\n", - " [806, '14:50', '15:00', 890, 900, 10],\n", - " [807, '14:51', '15:17', 891, 917, 26],\n", - " [808, '14:52', '15:27', 892, 927, 35],\n", - " [809, '14:52', '15:21', 892, 921, 29],\n", - " [810, '14:53', '15:40', 893, 940, 47],\n", - " [811, '14:54', '15:08', 894, 908, 14],\n", - " [812, '14:54', '16:20', 894, 980, 86],\n", - " [813, '14:58', '16:24', 898, 984, 86],\n", - " [814, '14:58', '15:45', 898, 945, 47],\n", - " [815, '14:58', '15:25', 898, 925, 27],\n", - " [816, '14:58', '15:45', 898, 945, 47],\n", - " [817, '14:59', '15:53', 899, 953, 54],\n", - " [818, '15:00', '15:10', 900, 910, 10],\n", - " [819, '15:00', '15:35', 900, 935, 35],\n", - " [820, '15:00', '16:11', 900, 971, 71],\n", - " [821, '15:00', '15:16', 900, 916, 16],\n", - " [822, '15:01', '15:13', 901, 913, 12],\n", - " [823, '15:02', '15:16', 902, 916, 14],\n", - " [824, '15:03', '15:50', 903, 950, 47],\n", - " [825, '15:04', '16:30', 904, 990, 86],\n", - " [826, '15:08', '16:34', 908, 994, 86],\n", - " [827, '15:08', '15:55', 908, 955, 47],\n", - " [828, '15:08', '15:55', 908, 955, 47],\n", - " [829, '15:08', '15:45', 908, 945, 37],\n", - " [830, '15:09', '16:14', 909, 974, 65],\n", - " [831, '15:09', '16:03', 909, 963, 54],\n", - " [832, '15:10', '16:21', 910, 981, 71],\n", - " [833, '15:10', '15:20', 910, 920, 10],\n", - " [834, '15:11', '15:24', 911, 924, 13],\n", - " [835, '15:12', '15:36', 912, 936, 24],\n", - " [836, '15:13', '16:00', 913, 960, 47],\n", - " [837, '15:13', '15:40', 913, 940, 27],\n", - " [838, '15:14', '16:40', 914, 1000, 86],\n", - " [839, '15:16', '15:28', 916, 928, 12],\n", - " [840, '15:16', '15:55', 916, 955, 39],\n", - " [841, '15:18', '16:05', 918, 965, 47],\n", - " [842, '15:18', '16:44', 918, 1004, 86],\n", - " [843, '15:18', '16:05', 918, 965, 47],\n", - " [844, '15:19', '16:13', 919, 973, 54],\n", - " [845, '15:19', '15:34', 919, 934, 15],\n", - " [846, '15:20', '15:30', 920, 930, 10],\n", - " [847, '15:20', '16:31', 920, 991, 71],\n", - " [848, '15:20', '15:36', 920, 936, 16],\n", - " [849, '15:21', '15:47', 921, 947, 26],\n", - " [850, '15:21', '16:06', 921, 966, 45],\n", - " [851, '15:23', '16:10', 923, 970, 47],\n", - " [852, '15:24', '16:50', 924, 1010, 86],\n", - " [853, '15:24', '16:05', 924, 965, 41],\n", - " [854, '15:27', '15:51', 927, 951, 24],\n", - " [855, '15:27', '15:44', 927, 944, 17],\n", - " [856, '15:28', '16:15', 928, 975, 47],\n", - " [857, '15:28', '16:54', 928, 1014, 86],\n", - " [858, '15:28', '16:15', 928, 975, 47],\n", - " [859, '15:28', '15:55', 928, 955, 27],\n", - " [860, '15:29', '16:23', 929, 983, 54],\n", - " [861, '15:30', '16:41', 930, 1001, 71],\n", - " [862, '15:30', '15:40', 930, 940, 10],\n", - " [863, '15:31', '15:43', 931, 943, 12],\n", - " [864, '15:33', '16:20', 933, 980, 47],\n", - " [865, '15:34', '17:00', 934, 1020, 86],\n", - " [866, '15:34', '16:15', 934, 975, 41],\n", - " [867, '15:35', '15:54', 935, 954, 19],\n", - " [868, '15:36', '16:21', 936, 981, 45],\n", - " [869, '15:38', '16:25', 938, 985, 47],\n", - " [870, '15:38', '16:25', 938, 985, 47],\n", - " [871, '15:38', '16:39', 938, 999, 61],\n", - " [872, '15:39', '16:33', 939, 993, 54],\n", - " [873, '15:40', '15:50', 940, 950, 10],\n", - " [874, '15:40', '16:51', 940, 1011, 71],\n", - " [875, '15:40', '15:56', 940, 956, 16],\n", - " [876, '15:43', '16:10', 943, 970, 27],\n", - " [877, '15:43', '16:30', 943, 990, 47],\n", - " [878, '15:44', '17:10', 944, 1030, 86],\n", - " [879, '15:44', '16:25', 944, 985, 41],\n", - " [880, '15:45', '16:04', 945, 964, 19],\n", - " [881, '15:46', '15:58', 946, 958, 12],\n", - " [882, '15:48', '16:35', 948, 995, 47],\n", - " [883, '15:48', '16:35', 948, 995, 47],\n", - " [884, '15:48', '17:14', 948, 1034, 86],\n", - " [885, '15:49', '16:43', 949, 1003, 54],\n", - " [886, '15:50', '16:00', 950, 960, 10],\n", - " [887, '15:50', '17:01', 950, 1021, 71],\n", - " [888, '15:51', '16:18', 951, 978, 27],\n", - " [889, '15:52', '16:36', 952, 996, 44],\n", - " [890, '15:53', '16:40', 953, 1000, 47],\n", - " [891, '15:54', '17:20', 954, 1040, 86],\n", - " [892, '15:54', '16:35', 954, 995, 41],\n", - " [893, '15:55', '16:14', 955, 974, 19],\n", - " [894, '15:58', '16:25', 958, 985, 27],\n", - " [895, '15:58', '16:45', 958, 1005, 47],\n", - " [896, '15:58', '16:45', 958, 1005, 47],\n", - " [897, '15:58', '17:24', 958, 1044, 86],\n", - " [898, '15:59', '17:11', 959, 1031, 72],\n", - " [899, '15:59', '16:53', 959, 1013, 54],\n", - " [900, '16:00', '16:10', 960, 970, 10],\n", - " [901, '16:00', '16:16', 960, 976, 16],\n", - " [902, '16:01', '16:13', 961, 973, 12],\n", - " [903, '16:03', '16:50', 963, 1010, 47],\n", - " [904, '16:04', '17:30', 964, 1050, 86],\n", - " [905, '16:04', '16:45', 964, 1005, 41],\n", - " [906, '16:05', '16:24', 965, 984, 19],\n", - " [907, '16:06', '16:51', 966, 1011, 45],\n", - " [908, '16:08', '16:55', 968, 1015, 47],\n", - " [909, '16:08', '17:34', 968, 1054, 86],\n", - " [910, '16:08', '16:55', 968, 1015, 47],\n", - " [911, '16:09', '17:03', 969, 1023, 54],\n", - " [912, '16:09', '17:21', 969, 1041, 72],\n", - " [913, '16:10', '16:20', 970, 980, 10],\n", - " [914, '16:13', '16:40', 973, 1000, 27],\n", - " [915, '16:13', '17:00', 973, 1020, 47],\n", - " [916, '16:14', '16:55', 974, 1015, 41],\n", - " [917, '16:14', '17:40', 974, 1060, 86],\n", - " [918, '16:15', '16:34', 975, 994, 19],\n", - " [919, '16:16', '16:28', 976, 988, 12],\n", - " [920, '16:18', '17:05', 978, 1025, 47],\n", - " [921, '16:18', '17:05', 978, 1025, 47],\n", - " [922, '16:18', '17:44', 978, 1064, 86],\n", - " [923, '16:19', '17:31', 979, 1051, 72],\n", - " [924, '16:19', '17:13', 979, 1033, 54],\n", - " [925, '16:20', '16:30', 980, 990, 10],\n", - " [926, '16:20', '16:36', 980, 996, 16],\n", - " [927, '16:21', '16:48', 981, 1008, 27],\n", - " [928, '16:22', '17:06', 982, 1026, 44],\n", - " [929, '16:23', '17:10', 983, 1030, 47],\n", - " [930, '16:24', '17:05', 984, 1025, 41],\n", - " [931, '16:24', '17:50', 984, 1070, 86],\n", - " [932, '16:25', '16:44', 985, 1004, 19],\n", - " [933, '16:28', '17:15', 988, 1035, 47],\n", - " [934, '16:28', '17:15', 988, 1035, 47],\n", - " [935, '16:28', '16:55', 988, 1015, 27],\n", - " [936, '16:28', '17:54', 988, 1074, 86],\n", - " [937, '16:29', '17:23', 989, 1043, 54],\n", - " [938, '16:29', '17:41', 989, 1061, 72],\n", - " [939, '16:30', '16:40', 990, 1000, 10],\n", - " [940, '16:31', '16:43', 991, 1003, 12],\n", - " [941, '16:33', '17:20', 993, 1040, 47],\n", - " [942, '16:34', '17:15', 994, 1035, 41],\n", - " [943, '16:34', '18:00', 994, 1080, 86],\n", - " [944, '16:35', '16:54', 995, 1014, 19],\n", - " [945, '16:36', '17:21', 996, 1041, 45],\n", - " [946, '16:38', '17:25', 998, 1045, 47],\n", - " [947, '16:38', '17:25', 998, 1045, 47],\n", - " [948, '16:38', '18:04', 998, 1084, 86],\n", - " [949, '16:39', '17:33', 999, 1053, 54],\n", - " [950, '16:39', '17:51', 999, 1071, 72],\n", - " [951, '16:40', '16:56', 1000, 1016, 16],\n", - " [952, '16:40', '16:50', 1000, 1010, 10],\n", - " [953, '16:43', '17:10', 1003, 1030, 27],\n", - " [954, '16:43', '17:30', 1003, 1050, 47],\n", - " [955, '16:44', '17:25', 1004, 1045, 41],\n", - " [956, '16:44', '18:10', 1004, 1090, 86],\n", - " [957, '16:45', '17:04', 1005, 1024, 19],\n", - " [958, '16:46', '16:58', 1006, 1018, 12],\n", - " [959, '16:48', '18:14', 1008, 1094, 86],\n", - " [960, '16:48', '17:35', 1008, 1055, 47],\n", - " [961, '16:48', '17:35', 1008, 1055, 47],\n", - " [962, '16:49', '18:01', 1009, 1081, 72],\n", - " [963, '16:49', '17:43', 1009, 1063, 54],\n", - " [964, '16:50', '17:00', 1010, 1020, 10],\n", - " [965, '16:51', '17:18', 1011, 1038, 27],\n", - " [966, '16:52', '17:36', 1012, 1056, 44],\n", - " [967, '16:53', '17:40', 1013, 1060, 47],\n", - " [968, '16:54', '18:20', 1014, 1100, 86],\n", - " [969, '16:54', '17:35', 1014, 1055, 41],\n", - " [970, '16:55', '17:14', 1015, 1034, 19],\n", - " [971, '16:58', '17:25', 1018, 1045, 27],\n", - " [972, '16:58', '17:45', 1018, 1065, 47],\n", - " [973, '16:58', '17:45', 1018, 1065, 47],\n", - " [974, '16:58', '18:24', 1018, 1104, 86],\n", - " [975, '16:59', '18:11', 1019, 1091, 72],\n", - " [976, '16:59', '17:53', 1019, 1073, 54],\n", - " [977, '17:00', '17:16', 1020, 1036, 16],\n", - " [978, '17:00', '17:10', 1020, 1030, 10],\n", - " [979, '17:01', '17:13', 1021, 1033, 12],\n", - " [980, '17:03', '17:50', 1023, 1070, 47],\n", - " [981, '17:04', '18:30', 1024, 1110, 86],\n", - " [982, '17:04', '17:45', 1024, 1065, 41],\n", - " [983, '17:05', '17:24', 1025, 1044, 19],\n", - " [984, '17:06', '17:51', 1026, 1071, 45],\n", - " [985, '17:08', '17:55', 1028, 1075, 47],\n", - " [986, '17:08', '17:55', 1028, 1075, 47],\n", - " [987, '17:08', '18:34', 1028, 1114, 86],\n", - " [988, '17:09', '18:03', 1029, 1083, 54],\n", - " [989, '17:09', '18:21', 1029, 1101, 72],\n", - " [990, '17:10', '17:20', 1030, 1040, 10],\n", - " [991, '17:13', '17:40', 1033, 1060, 27],\n", - " [992, '17:13', '18:00', 1033, 1080, 47],\n", - " [993, '17:14', '17:55', 1034, 1075, 41],\n", - " [994, '17:14', '18:40', 1034, 1120, 86],\n", - " [995, '17:15', '17:34', 1035, 1054, 19],\n", - " [996, '17:16', '17:28', 1036, 1048, 12],\n", - " [997, '17:18', '18:05', 1038, 1085, 47],\n", - " [998, '17:18', '18:05', 1038, 1085, 47],\n", - " [999, '17:18', '18:44', 1038, 1124, 86],\n", - " [1000, '17:19', '18:31', 1039, 1111, 72],\n", - " [1001, '17:19', '18:13', 1039, 1093, 54],\n", - " [1002, '17:20', '17:36', 1040, 1056, 16],\n", - " [1003, '17:20', '17:30', 1040, 1050, 10],\n", - " [1004, '17:21', '17:47', 1041, 1067, 26],\n", - " [1005, '17:22', '18:06', 1042, 1086, 44],\n", - " [1006, '17:23', '18:10', 1043, 1090, 47],\n", - " [1007, '17:24', '18:50', 1044, 1130, 86],\n", - " [1008, '17:24', '18:05', 1044, 1085, 41],\n", - " [1009, '17:25', '17:44', 1045, 1064, 19],\n", - " [1010, '17:28', '17:55', 1048, 1075, 27],\n", - " [1011, '17:28', '18:15', 1048, 1095, 47],\n", - " [1012, '17:28', '18:15', 1048, 1095, 47],\n", - " [1013, '17:28', '18:54', 1048, 1134, 86],\n", - " [1014, '17:29', '18:41', 1049, 1121, 72],\n", - " [1015, '17:29', '18:23', 1049, 1103, 54],\n", - " [1016, '17:30', '17:40', 1050, 1060, 10],\n", - " [1017, '17:31', '17:43', 1051, 1063, 12],\n", - " [1018, '17:33', '18:20', 1053, 1100, 47],\n", - " [1019, '17:34', '18:15', 1054, 1095, 41],\n", - " [1020, '17:34', '19:00', 1054, 1140, 86],\n", - " [1021, '17:35', '17:54', 1055, 1074, 19],\n", - " [1022, '17:36', '18:21', 1056, 1101, 45],\n", - " [1023, '17:38', '18:25', 1058, 1105, 47],\n", - " [1024, '17:38', '19:04', 1058, 1144, 86],\n", - " [1025, '17:38', '18:25', 1058, 1105, 47],\n", - " [1026, '17:39', '18:51', 1059, 1131, 72],\n", - " [1027, '17:39', '18:33', 1059, 1113, 54],\n", - " [1028, '17:40', '17:56', 1060, 1076, 16],\n", - " [1029, '17:40', '17:50', 1060, 1070, 10],\n", - " [1030, '17:43', '18:10', 1063, 1090, 27],\n", - " [1031, '17:43', '18:30', 1063, 1110, 47],\n", - " [1032, '17:44', '18:25', 1064, 1105, 41],\n", - " [1033, '17:44', '19:14', 1064, 1154, 90],\n", - " [1034, '17:45', '18:04', 1065, 1084, 19],\n", - " [1035, '17:46', '17:58', 1066, 1078, 12],\n", - " [1036, '17:48', '18:35', 1068, 1115, 47],\n", - " [1037, '17:48', '18:35', 1068, 1115, 47],\n", - " [1038, '17:48', '19:14', 1068, 1154, 86],\n", - " [1039, '17:49', '19:01', 1069, 1141, 72],\n", - " [1040, '17:49', '18:43', 1069, 1123, 54],\n", - " [1041, '17:50', '18:00', 1070, 1080, 10],\n", - " [1042, '17:51', '18:17', 1071, 1097, 26],\n", - " [1043, '17:52', '18:36', 1072, 1116, 44],\n", - " [1044, '17:53', '18:40', 1073, 1120, 47],\n", - " [1045, '17:54', '18:35', 1074, 1115, 41],\n", - " [1046, '17:54', '18:57', 1074, 1137, 63],\n", - " [1047, '17:55', '18:14', 1075, 1094, 19],\n", - " [1048, '17:58', '18:45', 1078, 1125, 47],\n", - " [1049, '17:58', '18:45', 1078, 1125, 47],\n", - " [1050, '17:58', '18:25', 1078, 1105, 27],\n", - " [1051, '17:58', '19:26', 1078, 1166, 88],\n", - " [1052, '17:59', '18:53', 1079, 1133, 54],\n", - " [1053, '18:00', '19:11', 1080, 1151, 71],\n", - " [1054, '18:00', '18:10', 1080, 1090, 10],\n", - " [1055, '18:00', '18:16', 1080, 1096, 16],\n", - " [1056, '18:01', '18:13', 1081, 1093, 12],\n", - " [1057, '18:03', '18:50', 1083, 1130, 47],\n", - " [1058, '18:04', '18:45', 1084, 1125, 41],\n", - " [1059, '18:04', '19:29', 1084, 1169, 85],\n", - " [1060, '18:05', '18:24', 1085, 1104, 19],\n", - " [1061, '18:06', '18:51', 1086, 1131, 45],\n", - " [1062, '18:08', '18:55', 1088, 1135, 47],\n", - " [1063, '18:08', '19:06', 1088, 1146, 58],\n", - " [1064, '18:08', '18:55', 1088, 1135, 47],\n", - " [1065, '18:09', '19:03', 1089, 1143, 54],\n", - " [1066, '18:10', '18:20', 1090, 1100, 10],\n", - " [1067, '18:10', '19:21', 1090, 1161, 71],\n", - " [1068, '18:13', '19:00', 1093, 1140, 47],\n", - " [1069, '18:13', '18:40', 1093, 1120, 27],\n", - " [1070, '18:14', '19:43', 1094, 1183, 89],\n", - " [1071, '18:14', '18:55', 1094, 1135, 41],\n", - " [1072, '18:15', '18:34', 1095, 1114, 19],\n", - " [1073, '18:16', '18:28', 1096, 1108, 12],\n", - " [1074, '18:17', '18:27', 1097, 1107, 10],\n", - " [1075, '18:18', '19:41', 1098, 1181, 83],\n", - " [1076, '18:18', '18:58', 1098, 1138, 40],\n", - " [1077, '18:18', '19:05', 1098, 1145, 47],\n", - " [1078, '18:19', '19:13', 1099, 1153, 54],\n", - " [1079, '18:20', '19:31', 1100, 1171, 71],\n", - " [1080, '18:20', '18:36', 1100, 1116, 16],\n", - " [1081, '18:20', '18:30', 1100, 1110, 10],\n", - " [1082, '18:22', '19:05', 1102, 1145, 43],\n", - " [1083, '18:23', '19:05', 1103, 1145, 42],\n", - " [1084, '18:24', '19:27', 1104, 1167, 63],\n", - " [1085, '18:24', '19:05', 1104, 1145, 41],\n", - " [1086, '18:25', '18:44', 1105, 1124, 19],\n", - " [1087, '18:28', '19:25', 1108, 1165, 57],\n", - " [1088, '18:28', '18:55', 1108, 1135, 27],\n", - " [1089, '18:28', '19:08', 1108, 1148, 40],\n", - " [1090, '18:28', '19:15', 1108, 1155, 47],\n", - " [1091, '18:29', '19:23', 1109, 1163, 54],\n", - " [1092, '18:30', '19:05', 1110, 1145, 35],\n", - " [1093, '18:30', '18:40', 1110, 1120, 10],\n", - " [1094, '18:31', '18:43', 1111, 1123, 12],\n", - " [1095, '18:33', '19:15', 1113, 1155, 42],\n", - " [1096, '18:34', '19:58', 1114, 1198, 84],\n", - " [1097, '18:34', '19:14', 1114, 1154, 40],\n", - " [1098, '18:35', '18:55', 1115, 1135, 20],\n", - " [1099, '18:36', '19:20', 1116, 1160, 44],\n", - " [1100, '18:38', '19:25', 1118, 1165, 47],\n", - " [1101, '18:38', '19:23', 1118, 1163, 45],\n", - " [1102, '18:38', '19:56', 1118, 1196, 78],\n", - " [1103, '18:39', '19:33', 1119, 1173, 54],\n", - " [1104, '18:40', '18:50', 1120, 1130, 10],\n", - " [1105, '18:40', '19:45', 1120, 1185, 65],\n", - " [1106, '18:40', '18:56', 1120, 1136, 16],\n", - " [1107, '18:43', '19:10', 1123, 1150, 27],\n", - " [1108, '18:43', '19:30', 1123, 1170, 47],\n", - " [1109, '18:44', '19:24', 1124, 1164, 40],\n", - " [1110, '18:45', '19:05', 1125, 1145, 20],\n", - " [1111, '18:46', '18:58', 1126, 1138, 12],\n", - " [1112, '18:48', '19:35', 1128, 1175, 47],\n", - " [1113, '18:48', '20:12', 1128, 1212, 84],\n", - " [1114, '18:48', '20:11', 1128, 1211, 83],\n", - " [1115, '18:48', '19:28', 1128, 1168, 40],\n", - " [1116, '18:49', '19:43', 1129, 1183, 54],\n", - " [1117, '18:50', '19:00', 1130, 1140, 10],\n", - " [1118, '18:51', '19:01', 1131, 1141, 10],\n", - " [1119, '18:53', '19:35', 1133, 1175, 42],\n", - " [1120, '18:53', '19:15', 1133, 1155, 22],\n", - " [1121, '18:53', '20:00', 1133, 1200, 67],\n", - " [1122, '18:55', '19:15', 1135, 1155, 20],\n", - " [1123, '18:55', '19:34', 1135, 1174, 39],\n", - " [1124, '18:58', '19:38', 1138, 1178, 40],\n", - " [1125, '18:59', '19:53', 1139, 1193, 54],\n", - " [1126, '18:59', '19:50', 1139, 1190, 51],\n", - " [1127, '18:59', '19:53', 1139, 1193, 54],\n", - " [1128, '19:00', '19:16', 1140, 1156, 16],\n", - " [1129, '19:00', '19:10', 1140, 1150, 10],\n", - " [1130, '19:00', '19:16', 1140, 1156, 16],\n", - " [1131, '19:01', '19:13', 1141, 1153, 12],\n", - " [1132, '19:03', '20:26', 1143, 1226, 83],\n", - " [1133, '19:03', '19:45', 1143, 1185, 42],\n", - " [1134, '19:05', '19:44', 1145, 1184, 39],\n", - " [1135, '19:05', '19:25', 1145, 1165, 20],\n", - " [1136, '19:08', '20:15', 1148, 1215, 67],\n", - " [1137, '19:08', '19:35', 1148, 1175, 27],\n", - " [1138, '19:09', '19:49', 1149, 1189, 40],\n", - " [1139, '19:09', '20:03', 1149, 1203, 54],\n", - " [1140, '19:10', '19:20', 1150, 1160, 10],\n", - " [1141, '19:10', '19:20', 1150, 1160, 10],\n", - " [1142, '19:11', '19:53', 1151, 1193, 42],\n", - " [1143, '19:14', '20:26', 1154, 1226, 72],\n", - " [1144, '19:14', '19:35', 1154, 1175, 21],\n", - " [1145, '19:14', '19:24', 1154, 1164, 10],\n", - " [1146, '19:14', '20:05', 1154, 1205, 51],\n", - " [1147, '19:15', '19:30', 1155, 1170, 15],\n", - " [1148, '19:15', '19:54', 1155, 1194, 39],\n", - " [1149, '19:18', '20:39', 1158, 1239, 81],\n", - " [1150, '19:18', '20:00', 1158, 1200, 42],\n", - " [1151, '19:19', '20:14', 1159, 1214, 55],\n", - " [1152, '19:20', '19:30', 1160, 1170, 10],\n", - " [1153, '19:20', '19:36', 1160, 1176, 16],\n", - " [1154, '19:21', '19:31', 1161, 1171, 10],\n", - " [1155, '19:23', '20:30', 1163, 1230, 67],\n", - " [1156, '19:23', '19:35', 1163, 1175, 12],\n", - " [1157, '19:24', '19:45', 1164, 1185, 21],\n", - " [1158, '19:24', '19:45', 1164, 1185, 21],\n", - " [1159, '19:25', '20:04', 1165, 1204, 39],\n", - " [1160, '19:26', '20:08', 1166, 1208, 42],\n", - " [1161, '19:29', '20:02', 1169, 1202, 33],\n", - " [1162, '19:29', '20:18', 1169, 1218, 49],\n", - " [1163, '19:29', '20:41', 1169, 1241, 72],\n", - " [1164, '19:30', '19:40', 1170, 1180, 10],\n", - " [1165, '19:33', '20:54', 1173, 1254, 81],\n", - " [1166, '19:33', '20:17', 1173, 1217, 44],\n", - " [1167, '19:34', '19:55', 1174, 1195, 21],\n", - " [1168, '19:35', '20:14', 1175, 1214, 39],\n", - " [1169, '19:38', '20:05', 1178, 1205, 27],\n", - " [1170, '19:38', '20:45', 1178, 1245, 67],\n", - " [1171, '19:39', '20:12', 1179, 1212, 33],\n", - " [1172, '19:40', '19:50', 1180, 1190, 10],\n", - " [1173, '19:40', '19:56', 1180, 1196, 16],\n", - " [1174, '19:41', '20:27', 1181, 1227, 46],\n", - " [1175, '19:43', '19:55', 1183, 1195, 12],\n", - " [1176, '19:44', '20:05', 1184, 1205, 21],\n", - " [1177, '19:44', '20:33', 1184, 1233, 49],\n", - " [1178, '19:44', '21:00', 1184, 1260, 76],\n", - " [1179, '19:45', '20:24', 1185, 1224, 39],\n", - " [1180, '19:48', '20:37', 1188, 1237, 49],\n", - " [1181, '19:48', '21:09', 1188, 1269, 81],\n", - " [1182, '19:50', '20:00', 1190, 1200, 10],\n", - " [1183, '19:52', '20:29', 1192, 1229, 37],\n", - " [1184, '19:53', '20:08', 1193, 1208, 15],\n", - " [1185, '19:53', '21:02', 1193, 1262, 69],\n", - " [1186, '19:53', '20:20', 1193, 1220, 27],\n", - " [1187, '19:54', '20:19', 1194, 1219, 25],\n", - " [1188, '19:55', '20:34', 1195, 1234, 39],\n", - " [1189, '19:56', '20:34', 1196, 1234, 38],\n", - " [1190, '19:59', '20:48', 1199, 1248, 49],\n", - " [1191, '19:59', '21:20', 1199, 1280, 81],\n", - " [1192, '20:00', '20:16', 1200, 1216, 16],\n", - " [1193, '20:00', '20:10', 1200, 1210, 10],\n", - " [1194, '20:03', '20:42', 1203, 1242, 39],\n", - " [1195, '20:03', '21:24', 1203, 1284, 81],\n", - " [1196, '20:04', '20:29', 1204, 1229, 25],\n", - " [1197, '20:05', '20:48', 1205, 1248, 43],\n", - " [1198, '20:07', '20:44', 1207, 1244, 37],\n", - " [1199, '20:08', '20:40', 1208, 1240, 32],\n", - " [1200, '20:08', '20:35', 1208, 1235, 27],\n", - " [1201, '20:10', '20:20', 1210, 1220, 10],\n", - " [1202, '20:10', '20:22', 1210, 1222, 12],\n", - " [1203, '20:11', '20:47', 1211, 1247, 36],\n", - " [1204, '20:14', '21:04', 1214, 1264, 50],\n", - " [1205, '20:14', '21:03', 1214, 1263, 49],\n", - " [1206, '20:17', '21:03', 1217, 1263, 46],\n", - " [1207, '20:18', '21:39', 1218, 1299, 81],\n", - " [1208, '20:20', '20:30', 1220, 1230, 10],\n", - " [1209, '20:20', '20:57', 1220, 1257, 37],\n", - " [1210, '20:20', '20:36', 1220, 1236, 16],\n", - " [1211, '20:22', '20:59', 1222, 1259, 37],\n", - " [1212, '20:22', '20:42', 1222, 1242, 20],\n", - " [1213, '20:24', '20:49', 1224, 1249, 25],\n", - " [1214, '20:27', '21:22', 1227, 1282, 55],\n", - " [1215, '20:29', '21:18', 1229, 1278, 49],\n", - " [1216, '20:30', '21:07', 1230, 1267, 37],\n", - " [1217, '20:30', '20:40', 1230, 1240, 10],\n", - " [1218, '20:30', '20:40', 1230, 1240, 10],\n", - " [1219, '20:30', '21:40', 1230, 1300, 70],\n", - " [1220, '20:32', '21:18', 1232, 1278, 46],\n", - " [1221, '20:35', '21:54', 1235, 1314, 79],\n", - " [1222, '20:37', '21:14', 1237, 1274, 37],\n", - " [1223, '20:38', '21:08', 1238, 1268, 30],\n", - " [1224, '20:40', '20:50', 1240, 1250, 10],\n", - " [1225, '20:40', '21:17', 1240, 1277, 37],\n", - " [1226, '20:40', '20:56', 1240, 1256, 16],\n", - " [1227, '20:44', '21:33', 1244, 1293, 49],\n", - " [1228, '20:47', '21:33', 1247, 1293, 46],\n", - " [1229, '20:47', '21:42', 1247, 1302, 55],\n", - " [1230, '20:50', '21:00', 1250, 1260, 10],\n", - " [1231, '20:50', '22:00', 1250, 1320, 70],\n", - " [1232, '20:50', '22:09', 1250, 1329, 79],\n", - " [1233, '20:50', '21:27', 1250, 1287, 37],\n", - " [1234, '20:52', '21:29', 1252, 1289, 37],\n", - " [1235, '20:53', '21:20', 1253, 1280, 27],\n", - " [1236, '20:56', '21:11', 1256, 1271, 15],\n", - " [1237, '20:59', '21:48', 1259, 1308, 49],\n", - " [1238, '21:00', '21:10', 1260, 1270, 10],\n", - " [1239, '21:00', '21:37', 1260, 1297, 37],\n", - " [1240, '21:02', '21:48', 1262, 1308, 46],\n", - " [1241, '21:05', '22:24', 1265, 1344, 79],\n", - " [1242, '21:07', '21:44', 1267, 1304, 37],\n", - " [1243, '21:07', '22:02', 1267, 1322, 55],\n", - " [1244, '21:08', '21:38', 1268, 1298, 30],\n", - " [1245, '21:10', '22:25', 1270, 1345, 75],\n", - " [1246, '21:10', '21:20', 1270, 1280, 10],\n", - " [1247, '21:10', '21:47', 1270, 1307, 37],\n", - " [1248, '21:14', '22:03', 1274, 1323, 49],\n", - " [1249, '21:17', '22:03', 1277, 1323, 46],\n", - " [1250, '21:20', '22:18', 1280, 1338, 58],\n", - " [1251, '21:20', '21:57', 1280, 1317, 37],\n", - " [1252, '21:20', '21:30', 1280, 1290, 10],\n", - " [1253, '21:22', '21:59', 1282, 1319, 37],\n", - " [1254, '21:24', '21:49', 1284, 1309, 25],\n", - " [1255, '21:27', '22:21', 1287, 1341, 54],\n", - " [1256, '21:30', '22:07', 1290, 1327, 37],\n", - " [1257, '21:30', '22:20', 1290, 1340, 50],\n", - " [1258, '21:30', '21:40', 1290, 1300, 10],\n", - " [1259, '21:32', '22:18', 1292, 1338, 46],\n", - " [1260, '21:32', '22:01', 1292, 1321, 29],\n", - " [1261, '21:35', '22:54', 1295, 1374, 79],\n", - " [1262, '21:37', '22:14', 1297, 1334, 37],\n", - " [1263, '21:39', '21:55', 1299, 1315, 16],\n", - " [1264, '21:40', '22:17', 1300, 1337, 37],\n", - " [1265, '21:40', '21:50', 1300, 1310, 10],\n", - " [1266, '21:41', '22:08', 1301, 1328, 27],\n", - " [1267, '21:47', '22:16', 1307, 1336, 29],\n", - " [1268, '21:47', '22:51', 1307, 1371, 64],\n", - " [1269, '21:47', '22:33', 1307, 1353, 46],\n", - " [1270, '21:48', '22:03', 1308, 1323, 15],\n", - " [1271, '21:50', '22:55', 1310, 1375, 65],\n", - " [1272, '21:50', '22:27', 1310, 1347, 37],\n", - " [1273, '21:50', '22:00', 1310, 1320, 10],\n", - " [1274, '21:52', '22:29', 1312, 1349, 37],\n", - " [1275, '21:53', '22:19', 1313, 1339, 26],\n", - " [1276, '22:00', '22:38', 1320, 1358, 38],\n", - " [1277, '22:00', '22:10', 1320, 1330, 10],\n", - " [1278, '22:02', '22:12', 1322, 1332, 10],\n", - " [1279, '22:02', '22:48', 1322, 1368, 46],\n", - " [1280, '22:04', '22:31', 1324, 1351, 27],\n", - " [1281, '22:05', '23:24', 1325, 1404, 79],\n", - " [1282, '22:07', '22:44', 1327, 1364, 37],\n", - " [1283, '22:07', '22:39', 1327, 1359, 32],\n", - " [1284, '22:09', '22:25', 1329, 1345, 16],\n", - " [1285, '22:10', '23:25', 1330, 1405, 75],\n", - " [1286, '22:13', '22:38', 1333, 1358, 25],\n", - " [1287, '22:13', '22:53', 1333, 1373, 40],\n", - " [1288, '22:17', '22:27', 1337, 1347, 10],\n", - " [1289, '22:17', '23:03', 1337, 1383, 46],\n", - " [1290, '22:19', '22:46', 1339, 1366, 27],\n", - " [1291, '22:22', '22:59', 1342, 1379, 37],\n", - " [1292, '22:24', '22:48', 1344, 1368, 24],\n", - " [1293, '22:27', '22:52', 1347, 1372, 25],\n", - " [1294, '22:27', '23:21', 1347, 1401, 54],\n", - " [1295, '22:28', '23:08', 1348, 1388, 40],\n", - " [1296, '22:30', '23:17', 1350, 1397, 47],\n", - " [1297, '22:32', '22:42', 1352, 1362, 10],\n", - " [1298, '22:32', '23:11', 1352, 1391, 39],\n", - " [1299, '22:34', '23:01', 1354, 1381, 27],\n", - " [1300, '22:35', '23:54', 1355, 1434, 79],\n", - " [1301, '22:37', '23:14', 1357, 1394, 37],\n", - " [1302, '22:43', '23:23', 1363, 1403, 40],\n", - " [1303, '22:43', '23:08', 1363, 1388, 25],\n", - " [1304, '22:47', '23:33', 1367, 1413, 46],\n", - " [1305, '22:47', '22:57', 1367, 1377, 10],\n", - " [1306, '22:49', '23:16', 1369, 1396, 27],\n", - " [1307, '22:52', '23:29', 1372, 1409, 37],\n", - " [1308, '22:53', '23:15', 1373, 1395, 22],\n", - " [1309, '22:55', '23:55', 1375, 1435, 60],\n", - " [1310, '22:57', '23:51', 1377, 1431, 54],\n", - " [1311, '22:58', '23:38', 1378, 1418, 40],\n", - " [1312, '23:02', '23:41', 1382, 1421, 39],\n", - " [1313, '23:02', '23:12', 1382, 1392, 10],\n", - " [1314, '23:04', '23:31', 1384, 1411, 27],\n", - " [1315, '23:05', '00:24', 1385, 1464, 79],\n", - " [1316, '23:07', '23:44', 1387, 1424, 37],\n", - " [1317, '23:13', '23:53', 1393, 1433, 40],\n", - " [1318, '23:13', '23:38', 1393, 1418, 25],\n", - " [1319, '23:17', '00:03', 1397, 1443, 46],\n", - " [1320, '23:17', '23:27', 1397, 1407, 10],\n", - " [1321, '23:19', '23:46', 1399, 1426, 27],\n", - " [1322, '23:22', '23:59', 1402, 1439, 37],\n", - " [1323, '23:25', '00:25', 1405, 1465, 60],\n", - " [1324, '23:27', '00:21', 1407, 1461, 54],\n", - " [1325, '23:28', '00:08', 1408, 1448, 40],\n", - " [1326, '23:32', '23:42', 1412, 1422, 10],\n", - " [1327, '23:34', '00:01', 1414, 1441, 27],\n", - " [1328, '23:35', '01:05', 1415, 1505, 90],\n", - " [1329, '23:37', '00:09', 1417, 1449, 32],\n", - " [1330, '23:43', '00:23', 1423, 1463, 40],\n", - " [1331, '23:43', '00:08', 1423, 1448, 25],\n", - " [1332, '23:46', '00:01', 1426, 1441, 15],\n", - " [1333, '23:47', '23:57', 1427, 1437, 10],\n", - " [1334, '23:47', '00:33', 1427, 1473, 46],\n", - " [1335, '23:52', '00:24', 1432, 1464, 32],\n", - " [1336, '23:55', '00:49', 1435, 1489, 54],\n", - " [1337, '23:57', '00:57', 1437, 1497, 60],\n", - " [1338, '23:58', '00:38', 1438, 1478, 40],\n", - " [1339, '00:02', '00:12', 1442, 1452, 10],\n", - " [1340, '00:07', '00:39', 1447, 1479, 32],\n", - " [1341, '00:13', '00:38', 1453, 1478, 25],\n", - " [1342, '00:13', '00:51', 1453, 1491, 38],\n", - " [1343, '00:15', '01:14', 1455, 1514, 59],\n", - " [1344, '00:17', '01:23', 1457, 1523, 66],\n", - " [1345, '00:23', '00:33', 1463, 1473, 10],\n", - " [1346, '00:24', '00:40', 1464, 1480, 16],\n", - " [1347, '00:25', '01:12', 1465, 1512, 47],\n", - " [1348, '00:28', '01:07', 1468, 1507, 39],\n", - " [1349, '00:33', '01:05', 1473, 1505, 32],\n", - " [1350, '00:43', '01:21', 1483, 1521, 38],\n", - " [1351, '00:44', '00:54', 1484, 1494, 10],\n", - " [1352, '00:47', '01:09', 1487, 1509, 22],\n", - " [1353, '00:47', '01:26', 1487, 1526, 39],\n", - " [1354, '00:54', '01:04', 1494, 1504, 10],\n", - " [1355, '00:57', '01:07', 1497, 1507, 10]\n", + " [0, \"04:18\", \"05:00\", 258, 300, 42],\n", + " [1, \"04:27\", \"05:08\", 267, 308, 41],\n", + " [2, \"04:29\", \"05:26\", 269, 326, 57],\n", + " [3, \"04:29\", \"04:55\", 269, 295, 26],\n", + " [4, \"04:30\", \"04:53\", 270, 293, 23],\n", + " [5, \"04:30\", \"04:51\", 270, 291, 21],\n", + " [6, \"04:31\", \"04:53\", 271, 293, 22],\n", + " [7, \"04:33\", \"05:15\", 273, 315, 42],\n", + " [8, \"04:34\", \"04:44\", 274, 284, 10],\n", + " [9, \"04:34\", \"05:03\", 274, 303, 29],\n", + " [10, \"04:35\", \"04:50\", 275, 290, 15],\n", + " [11, \"04:36\", \"04:46\", 276, 286, 10],\n", + " [12, \"04:37\", \"05:18\", 277, 318, 41],\n", + " [13, \"04:41\", \"05:13\", 281, 313, 32],\n", + " [14, \"04:42\", \"05:23\", 282, 323, 41],\n", + " [15, \"04:43\", \"04:53\", 283, 293, 10],\n", + " [16, \"04:44\", \"05:45\", 284, 345, 61],\n", + " [17, \"04:45\", \"05:11\", 285, 311, 26],\n", + " [18, \"04:46\", \"05:01\", 286, 301, 15],\n", + " [19, \"04:46\", \"04:56\", 286, 296, 10],\n", + " [20, \"04:47\", \"05:14\", 287, 314, 27],\n", + " [21, \"04:48\", \"05:30\", 288, 330, 42],\n", + " [22, \"04:49\", \"05:41\", 289, 341, 52],\n", + " [23, \"04:49\", \"05:18\", 289, 318, 29],\n", + " [24, \"04:50\", \"05:33\", 290, 333, 43],\n", + " [25, \"04:52\", \"05:56\", 292, 356, 64],\n", + " [26, \"04:52\", \"05:07\", 292, 307, 15],\n", + " [27, \"04:53\", \"05:19\", 293, 319, 26],\n", + " [28, \"04:53\", \"05:23\", 293, 323, 30],\n", + " [29, \"04:55\", \"05:27\", 295, 327, 32],\n", + " [30, \"04:57\", \"05:38\", 297, 338, 41],\n", + " [31, \"05:00\", \"06:00\", 300, 360, 60],\n", + " [32, \"05:00\", \"05:54\", 300, 354, 54],\n", + " [33, \"05:01\", \"05:33\", 301, 333, 32],\n", + " [34, \"05:01\", \"05:26\", 301, 326, 25],\n", + " [35, \"05:02\", \"05:29\", 302, 329, 27],\n", + " [36, \"05:02\", \"05:12\", 302, 312, 10],\n", + " [37, \"05:03\", \"05:45\", 303, 345, 42],\n", + " [38, \"05:03\", \"05:18\", 303, 318, 15],\n", + " [39, \"05:03\", \"06:28\", 303, 388, 85],\n", + " [40, \"05:03\", \"05:13\", 303, 313, 10],\n", + " [41, \"05:04\", \"06:24\", 304, 384, 80],\n", + " [42, \"05:07\", \"05:44\", 307, 344, 37],\n", + " [43, \"05:08\", \"05:48\", 308, 348, 40],\n", + " [44, \"05:10\", \"06:06\", 310, 366, 56],\n", + " [45, \"05:11\", \"05:37\", 311, 337, 26],\n", + " [46, \"05:11\", \"05:53\", 311, 353, 42],\n", + " [47, \"05:13\", \"06:15\", 313, 375, 62],\n", + " [48, \"05:13\", \"05:38\", 313, 338, 25],\n", + " [49, \"05:16\", \"05:44\", 316, 344, 28],\n", + " [50, \"05:17\", \"05:27\", 317, 327, 10],\n", + " [51, \"05:18\", \"06:40\", 318, 400, 82],\n", + " [52, \"05:18\", \"06:03\", 318, 363, 45],\n", + " [53, \"05:18\", \"06:11\", 318, 371, 53],\n", + " [54, \"05:18\", \"06:00\", 318, 360, 42],\n", + " [55, \"05:19\", \"06:34\", 319, 394, 75],\n", + " [56, \"05:20\", \"06:17\", 320, 377, 57],\n", + " [57, \"05:22\", \"05:59\", 322, 359, 37],\n", + " [58, \"05:24\", \"05:48\", 324, 348, 24],\n", + " [59, \"05:25\", \"05:40\", 325, 340, 15],\n", + " [60, \"05:26\", \"06:08\", 326, 368, 42],\n", + " [61, \"05:27\", \"06:30\", 327, 390, 63],\n", + " [62, \"05:27\", \"05:54\", 327, 354, 27],\n", + " [63, \"05:28\", \"05:53\", 328, 353, 25],\n", + " [64, \"05:29\", \"05:44\", 329, 344, 15],\n", + " [65, \"05:30\", \"05:40\", 330, 340, 10],\n", + " [66, \"05:30\", \"05:40\", 330, 340, 10],\n", + " [67, \"05:30\", \"05:40\", 330, 340, 10],\n", + " [68, \"05:32\", \"06:53\", 332, 413, 81],\n", + " [69, \"05:33\", \"07:00\", 333, 420, 87],\n", + " [70, \"05:33\", \"06:15\", 333, 375, 42],\n", + " [71, \"05:33\", \"05:47\", 333, 347, 14],\n", + " [72, \"05:37\", \"06:13\", 337, 373, 36],\n", + " [73, \"05:37\", \"06:05\", 337, 365, 28],\n", + " [74, \"05:38\", \"06:33\", 338, 393, 55],\n", + " [75, \"05:38\", \"06:04\", 338, 364, 26],\n", + " [76, \"05:38\", \"06:18\", 338, 378, 40],\n", + " [77, \"05:39\", \"05:54\", 339, 354, 15],\n", + " [78, \"05:40\", \"05:56\", 340, 356, 16],\n", + " [79, \"05:40\", \"06:41\", 340, 401, 61],\n", + " [80, \"05:40\", \"05:50\", 340, 350, 10],\n", + " [81, \"05:41\", \"06:23\", 341, 383, 42],\n", + " [82, \"05:41\", \"06:01\", 341, 361, 20],\n", + " [83, \"05:43\", \"06:08\", 343, 368, 25],\n", + " [84, \"05:44\", \"07:10\", 344, 430, 86],\n", + " [85, \"05:44\", \"05:55\", 344, 355, 11],\n", + " [86, \"05:45\", \"06:44\", 345, 404, 59],\n", + " [87, \"05:47\", \"06:17\", 347, 377, 30],\n", + " [88, \"05:48\", \"07:08\", 348, 428, 80],\n", + " [89, \"05:48\", \"06:30\", 348, 390, 42],\n", + " [90, \"05:50\", \"06:50\", 350, 410, 60],\n", + " [91, \"05:50\", \"06:00\", 350, 360, 10],\n", + " [92, \"05:50\", \"06:00\", 350, 360, 10],\n", + " [93, \"05:50\", \"06:51\", 350, 411, 61],\n", + " [94, \"05:52\", \"06:33\", 352, 393, 41],\n", + " [95, \"05:52\", \"06:36\", 352, 396, 44],\n", + " [96, \"05:52\", \"06:23\", 352, 383, 31],\n", + " [97, \"05:54\", \"06:14\", 354, 374, 20],\n", + " [98, \"05:54\", \"07:20\", 354, 440, 86],\n", + " [99, \"05:55\", \"06:40\", 355, 400, 45],\n", + " [100, \"05:55\", \"06:27\", 355, 387, 32],\n", + " [101, \"05:56\", \"06:35\", 356, 395, 39],\n", + " [102, \"05:56\", \"06:06\", 356, 366, 10],\n", + " [103, \"05:57\", \"06:21\", 357, 381, 24],\n", + " [104, \"05:58\", \"07:23\", 358, 443, 85],\n", + " [105, \"05:58\", \"06:23\", 358, 383, 25],\n", + " [106, \"05:58\", \"06:08\", 358, 368, 10],\n", + " [107, \"05:58\", \"06:43\", 358, 403, 45],\n", + " [108, \"06:00\", \"06:10\", 360, 370, 10],\n", + " [109, \"06:00\", \"06:16\", 360, 376, 16],\n", + " [110, \"06:00\", \"07:01\", 360, 421, 61],\n", + " [111, \"06:01\", \"07:00\", 361, 420, 59],\n", + " [112, \"06:01\", \"06:13\", 361, 373, 12],\n", + " [113, \"06:01\", \"06:45\", 361, 405, 44],\n", + " [114, \"06:03\", \"06:50\", 363, 410, 47],\n", + " [115, \"06:04\", \"06:37\", 364, 397, 33],\n", + " [116, \"06:04\", \"07:30\", 364, 450, 86],\n", + " [117, \"06:05\", \"06:24\", 365, 384, 19],\n", + " [118, \"06:06\", \"06:51\", 366, 411, 45],\n", + " [119, \"06:07\", \"06:43\", 367, 403, 36],\n", + " [120, \"06:08\", \"07:30\", 368, 450, 82],\n", + " [121, \"06:10\", \"06:20\", 370, 380, 10],\n", + " [122, \"06:10\", \"07:17\", 370, 437, 67],\n", + " [123, \"06:11\", \"06:54\", 371, 414, 43],\n", + " [124, \"06:11\", \"06:21\", 371, 381, 10],\n", + " [125, \"06:13\", \"06:38\", 373, 398, 25],\n", + " [126, \"06:13\", \"06:58\", 373, 418, 45],\n", + " [127, \"06:13\", \"06:53\", 373, 413, 40],\n", + " [128, \"06:14\", \"07:03\", 374, 423, 49],\n", + " [129, \"06:14\", \"06:47\", 374, 407, 33],\n", + " [130, \"06:14\", \"07:40\", 374, 460, 86],\n", + " [131, \"06:15\", \"07:15\", 375, 435, 60],\n", + " [132, \"06:16\", \"06:28\", 376, 388, 12],\n", + " [133, \"06:16\", \"06:26\", 376, 386, 10],\n", + " [134, \"06:17\", \"06:34\", 377, 394, 17],\n", + " [135, \"06:18\", \"07:06\", 378, 426, 48],\n", + " [136, \"06:18\", \"07:38\", 378, 458, 80],\n", + " [137, \"06:18\", \"07:02\", 378, 422, 44],\n", + " [138, \"06:19\", \"06:53\", 379, 413, 34],\n", + " [139, \"06:20\", \"07:25\", 380, 445, 65],\n", + " [140, \"06:20\", \"06:36\", 380, 396, 16],\n", + " [141, \"06:20\", \"06:30\", 380, 390, 10],\n", + " [142, \"06:20\", \"06:30\", 380, 390, 10],\n", + " [143, \"06:21\", \"06:49\", 381, 409, 28],\n", + " [144, \"06:22\", \"07:06\", 382, 426, 44],\n", + " [145, \"06:24\", \"07:50\", 384, 470, 86],\n", + " [146, \"06:24\", \"06:57\", 384, 417, 33],\n", + " [147, \"06:26\", \"07:45\", 386, 465, 79],\n", + " [148, \"06:26\", \"07:10\", 386, 430, 44],\n", + " [149, \"06:27\", \"06:44\", 387, 404, 17],\n", + " [150, \"06:28\", \"06:53\", 388, 413, 25],\n", + " [151, \"06:28\", \"07:14\", 388, 434, 46],\n", + " [152, \"06:29\", \"07:03\", 389, 423, 34],\n", + " [153, \"06:30\", \"06:40\", 390, 400, 10],\n", + " [154, \"06:30\", \"07:37\", 390, 457, 67],\n", + " [155, \"06:31\", \"06:43\", 391, 403, 12],\n", + " [156, \"06:33\", \"07:14\", 393, 434, 41],\n", + " [157, \"06:33\", \"07:53\", 393, 473, 80],\n", + " [158, \"06:34\", \"08:16\", 394, 496, 102],\n", + " [159, \"06:34\", \"07:09\", 394, 429, 35],\n", + " [160, \"06:34\", \"07:07\", 394, 427, 33],\n", + " [161, \"06:36\", \"07:21\", 396, 441, 45],\n", + " [162, \"06:37\", \"07:22\", 397, 442, 45],\n", + " [163, \"06:37\", \"06:54\", 397, 414, 17],\n", + " [164, \"06:38\", \"07:30\", 398, 450, 52],\n", + " [165, \"06:38\", \"07:18\", 398, 438, 40],\n", + " [166, \"06:39\", \"07:33\", 399, 453, 54],\n", + " [167, \"06:40\", \"07:52\", 400, 472, 72],\n", + " [168, \"06:40\", \"06:50\", 400, 410, 10],\n", + " [169, \"06:40\", \"07:22\", 400, 442, 42],\n", + " [170, \"06:40\", \"06:56\", 400, 416, 16],\n", + " [171, \"06:41\", \"08:00\", 401, 480, 79],\n", + " [172, \"06:42\", \"07:26\", 402, 446, 44],\n", + " [173, \"06:42\", \"07:13\", 402, 433, 31],\n", + " [174, \"06:43\", \"07:08\", 403, 428, 25],\n", + " [175, \"06:43\", \"07:30\", 403, 450, 47],\n", + " [176, \"06:43\", \"07:23\", 403, 443, 40],\n", + " [177, \"06:44\", \"07:17\", 404, 437, 33],\n", + " [178, \"06:44\", \"08:13\", 404, 493, 89],\n", + " [179, \"06:46\", \"07:01\", 406, 421, 15],\n", + " [180, \"06:46\", \"06:58\", 406, 418, 12],\n", + " [181, \"06:47\", \"07:04\", 407, 424, 17],\n", + " [182, \"06:48\", \"08:15\", 408, 495, 87],\n", + " [183, \"06:48\", \"07:34\", 408, 454, 46],\n", + " [184, \"06:48\", \"07:37\", 408, 457, 49],\n", + " [185, \"06:49\", \"07:43\", 409, 463, 54],\n", + " [186, \"06:50\", \"08:00\", 410, 480, 70],\n", + " [187, \"06:50\", \"07:00\", 410, 420, 10],\n", + " [188, \"06:50\", \"07:05\", 410, 425, 15],\n", + " [189, \"06:51\", \"07:18\", 411, 438, 27],\n", + " [190, \"06:52\", \"07:36\", 412, 456, 44],\n", + " [191, \"06:53\", \"07:37\", 413, 457, 44],\n", + " [192, \"06:54\", \"08:20\", 414, 500, 86],\n", + " [193, \"06:54\", \"07:27\", 414, 447, 33],\n", + " [194, \"06:54\", \"07:20\", 414, 440, 26],\n", + " [195, \"06:56\", \"08:23\", 416, 503, 87],\n", + " [196, \"06:57\", \"07:12\", 417, 432, 15],\n", + " [197, \"06:57\", \"07:58\", 417, 478, 61],\n", + " [198, \"06:57\", \"07:45\", 417, 465, 48],\n", + " [199, \"06:57\", \"07:40\", 417, 460, 43],\n", + " [200, \"06:58\", \"07:23\", 418, 443, 25],\n", + " [201, \"06:59\", \"07:53\", 419, 473, 54],\n", + " [202, \"06:59\", \"08:07\", 419, 487, 68],\n", + " [203, \"07:00\", \"07:10\", 420, 430, 10],\n", + " [204, \"07:00\", \"07:16\", 420, 436, 16],\n", + " [205, \"07:01\", \"08:30\", 421, 510, 89],\n", + " [206, \"07:01\", \"07:13\", 421, 433, 12],\n", + " [207, \"07:01\", \"07:43\", 421, 463, 42],\n", + " [208, \"07:03\", \"08:30\", 423, 510, 87],\n", + " [209, \"07:04\", \"07:37\", 424, 457, 33],\n", + " [210, \"07:04\", \"07:44\", 424, 464, 40],\n", + " [211, \"07:05\", \"07:52\", 425, 472, 47],\n", + " [212, \"07:05\", \"08:05\", 425, 485, 60],\n", + " [213, \"07:05\", \"07:46\", 425, 466, 41],\n", + " [214, \"07:06\", \"07:51\", 426, 471, 45],\n", + " [215, \"07:07\", \"08:08\", 427, 488, 61],\n", + " [216, \"07:07\", \"07:52\", 427, 472, 45],\n", + " [217, \"07:07\", \"08:16\", 427, 496, 69],\n", + " [218, \"07:07\", \"07:27\", 427, 447, 20],\n", + " [219, \"07:09\", \"07:50\", 429, 470, 41],\n", + " [220, \"07:09\", \"08:40\", 429, 520, 91],\n", + " [221, \"07:09\", \"08:03\", 429, 483, 54],\n", + " [222, \"07:10\", \"07:20\", 430, 440, 10],\n", + " [223, \"07:11\", \"08:36\", 431, 516, 85],\n", + " [224, \"07:12\", \"08:00\", 432, 480, 48],\n", + " [225, \"07:12\", \"07:47\", 432, 467, 35],\n", + " [226, \"07:13\", \"07:54\", 433, 474, 41],\n", + " [227, \"07:13\", \"07:38\", 433, 458, 25],\n", + " [228, \"07:14\", \"07:59\", 434, 479, 45],\n", + " [229, \"07:16\", \"08:50\", 436, 530, 94],\n", + " [230, \"07:16\", \"07:28\", 436, 448, 12],\n", + " [231, \"07:17\", \"07:35\", 437, 455, 18],\n", + " [232, \"07:17\", \"07:58\", 437, 478, 41],\n", + " [233, \"07:18\", \"08:06\", 438, 486, 48],\n", + " [234, \"07:18\", \"08:44\", 438, 524, 86],\n", + " [235, \"07:19\", \"08:13\", 439, 493, 54],\n", + " [236, \"07:20\", \"08:02\", 440, 482, 42],\n", + " [237, \"07:20\", \"08:07\", 440, 487, 47],\n", + " [238, \"07:20\", \"07:30\", 440, 450, 10],\n", + " [239, \"07:20\", \"07:57\", 440, 477, 37],\n", + " [240, \"07:20\", \"07:36\", 440, 456, 16],\n", + " [241, \"07:21\", \"07:48\", 441, 468, 27],\n", + " [242, \"07:22\", \"08:06\", 442, 486, 44],\n", + " [243, \"07:22\", \"08:25\", 442, 505, 63],\n", + " [244, \"07:24\", \"08:27\", 444, 507, 63],\n", + " [245, \"07:24\", \"08:05\", 444, 485, 41],\n", + " [246, \"07:26\", \"08:23\", 446, 503, 57],\n", + " [247, \"07:26\", \"08:52\", 446, 532, 86],\n", + " [248, \"07:27\", \"08:07\", 447, 487, 40],\n", + " [249, \"07:27\", \"07:42\", 447, 462, 15],\n", + " [250, \"07:27\", \"08:15\", 447, 495, 48],\n", + " [251, \"07:28\", \"07:53\", 448, 473, 25],\n", + " [252, \"07:28\", \"08:09\", 448, 489, 41],\n", + " [253, \"07:28\", \"07:38\", 448, 458, 10],\n", + " [254, \"07:30\", \"08:35\", 450, 515, 65],\n", + " [255, \"07:31\", \"07:43\", 451, 463, 12],\n", + " [256, \"07:32\", \"08:13\", 452, 493, 41],\n", + " [257, \"07:34\", \"09:00\", 454, 540, 86],\n", + " [258, \"07:34\", \"08:33\", 454, 513, 59],\n", + " [259, \"07:34\", \"09:04\", 454, 544, 90],\n", + " [260, \"07:35\", \"08:22\", 455, 502, 47],\n", + " [261, \"07:35\", \"07:45\", 455, 465, 10],\n", + " [262, \"07:35\", \"08:16\", 455, 496, 41],\n", + " [263, \"07:36\", \"08:17\", 456, 497, 41],\n", + " [264, \"07:36\", \"08:36\", 456, 516, 60],\n", + " [265, \"07:37\", \"07:50\", 457, 470, 13],\n", + " [266, \"07:40\", \"07:56\", 460, 476, 16],\n", + " [267, \"07:40\", \"08:20\", 460, 500, 40],\n", + " [268, \"07:40\", \"08:45\", 460, 525, 65],\n", + " [269, \"07:41\", \"08:39\", 461, 519, 58],\n", + " [270, \"07:41\", \"07:51\", 461, 471, 10],\n", + " [271, \"07:42\", \"08:30\", 462, 510, 48],\n", + " [272, \"07:42\", \"08:21\", 462, 501, 39],\n", + " [273, \"07:43\", \"08:08\", 463, 488, 25],\n", + " [274, \"07:43\", \"08:24\", 463, 504, 41],\n", + " [275, \"07:44\", \"09:10\", 464, 550, 86],\n", + " [276, \"07:44\", \"08:43\", 464, 523, 59],\n", + " [277, \"07:46\", \"08:28\", 466, 508, 42],\n", + " [278, \"07:46\", \"07:58\", 466, 478, 12],\n", + " [279, \"07:47\", \"08:00\", 467, 480, 13],\n", + " [280, \"07:48\", \"09:14\", 468, 554, 86],\n", + " [281, \"07:49\", \"08:32\", 469, 512, 43],\n", + " [282, \"07:50\", \"08:55\", 470, 535, 65],\n", + " [283, \"07:50\", \"08:00\", 470, 480, 10],\n", + " [284, \"07:50\", \"08:37\", 470, 517, 47],\n", + " [285, \"07:50\", \"08:26\", 470, 506, 36],\n", + " [286, \"07:51\", \"08:18\", 471, 498, 27],\n", + " [287, \"07:52\", \"08:21\", 472, 501, 29],\n", + " [288, \"07:53\", \"08:35\", 473, 515, 42],\n", + " [289, \"07:54\", \"09:19\", 474, 559, 85],\n", + " [290, \"07:55\", \"08:53\", 475, 533, 58],\n", + " [291, \"07:56\", \"08:54\", 476, 534, 58],\n", + " [292, \"07:57\", \"08:39\", 477, 519, 42],\n", + " [293, \"07:57\", \"08:10\", 477, 490, 13],\n", + " [294, \"07:58\", \"08:45\", 478, 525, 47],\n", + " [295, \"07:58\", \"08:23\", 478, 503, 25],\n", + " [296, \"08:00\", \"08:10\", 480, 490, 10],\n", + " [297, \"08:00\", \"09:05\", 480, 545, 65],\n", + " [298, \"08:00\", \"08:16\", 480, 496, 16],\n", + " [299, \"08:00\", \"08:35\", 480, 515, 35],\n", + " [300, \"08:01\", \"08:13\", 481, 493, 12],\n", + " [301, \"08:01\", \"08:43\", 481, 523, 42],\n", + " [302, \"08:03\", \"09:26\", 483, 566, 83],\n", + " [303, \"08:04\", \"09:29\", 484, 569, 85],\n", + " [304, \"08:05\", \"08:21\", 485, 501, 16],\n", + " [305, \"08:05\", \"08:47\", 485, 527, 42],\n", + " [306, \"08:06\", \"08:51\", 486, 531, 45],\n", + " [307, \"08:06\", \"09:03\", 486, 543, 57],\n", + " [308, \"08:07\", \"08:20\", 487, 500, 13],\n", + " [309, \"08:08\", \"08:55\", 488, 535, 47],\n", + " [310, \"08:08\", \"08:50\", 488, 530, 42],\n", + " [311, \"08:10\", \"08:45\", 490, 525, 35],\n", + " [312, \"08:10\", \"09:15\", 490, 555, 65],\n", + " [313, \"08:10\", \"08:20\", 490, 500, 10],\n", + " [314, \"08:11\", \"09:41\", 491, 581, 90],\n", + " [315, \"08:12\", \"08:55\", 492, 535, 43],\n", + " [316, \"08:13\", \"08:38\", 493, 518, 25],\n", + " [317, \"08:14\", \"09:38\", 494, 578, 84],\n", + " [318, \"08:15\", \"08:30\", 495, 510, 15],\n", + " [319, \"08:16\", \"08:30\", 496, 510, 14],\n", + " [320, \"08:16\", \"08:28\", 496, 508, 12],\n", + " [321, \"08:16\", \"09:00\", 496, 540, 44],\n", + " [322, \"08:17\", \"09:13\", 497, 553, 56],\n", + " [323, \"08:18\", \"09:16\", 498, 556, 58],\n", + " [324, \"08:18\", \"09:05\", 498, 545, 47],\n", + " [325, \"08:20\", \"08:36\", 500, 516, 16],\n", + " [326, \"08:20\", \"08:55\", 500, 535, 35],\n", + " [327, \"08:20\", \"09:05\", 500, 545, 45],\n", + " [328, \"08:20\", \"08:30\", 500, 510, 10],\n", + " [329, \"08:20\", \"09:25\", 500, 565, 65],\n", + " [330, \"08:21\", \"08:38\", 501, 518, 17],\n", + " [331, \"08:21\", \"08:47\", 501, 527, 26],\n", + " [332, \"08:22\", \"08:45\", 502, 525, 23],\n", + " [333, \"08:23\", \"09:10\", 503, 550, 47],\n", + " [334, \"08:24\", \"09:48\", 504, 588, 84],\n", + " [335, \"08:26\", \"08:46\", 506, 526, 20],\n", + " [336, \"08:27\", \"09:07\", 507, 547, 40],\n", + " [337, \"08:28\", \"08:50\", 508, 530, 22],\n", + " [338, \"08:28\", \"09:56\", 508, 596, 88],\n", + " [339, \"08:28\", \"09:23\", 508, 563, 55],\n", + " [340, \"08:29\", \"09:20\", 509, 560, 51],\n", + " [341, \"08:30\", \"09:05\", 510, 545, 35],\n", + " [342, \"08:30\", \"08:45\", 510, 525, 15],\n", + " [343, \"08:30\", \"08:40\", 510, 520, 10],\n", + " [344, \"08:30\", \"09:35\", 510, 575, 65],\n", + " [345, \"08:31\", \"08:43\", 511, 523, 12],\n", + " [346, \"08:31\", \"09:13\", 511, 553, 42],\n", + " [347, \"08:34\", \"09:58\", 514, 598, 84],\n", + " [348, \"08:35\", \"08:55\", 515, 535, 20],\n", + " [349, \"08:35\", \"09:15\", 515, 555, 40],\n", + " [350, \"08:35\", \"08:45\", 515, 525, 10],\n", + " [351, \"08:36\", \"08:46\", 516, 526, 10],\n", + " [352, \"08:36\", \"09:00\", 516, 540, 24],\n", + " [353, \"08:38\", \"09:20\", 518, 560, 42],\n", + " [354, \"08:38\", \"09:35\", 518, 575, 57],\n", + " [355, \"08:38\", \"09:14\", 518, 554, 36],\n", + " [356, \"08:39\", \"09:33\", 519, 573, 54],\n", + " [357, \"08:40\", \"09:45\", 520, 585, 65],\n", + " [358, \"08:40\", \"08:50\", 520, 530, 10],\n", + " [359, \"08:40\", \"08:56\", 520, 536, 16],\n", + " [360, \"08:42\", \"09:25\", 522, 565, 43],\n", + " [361, \"08:43\", \"09:08\", 523, 548, 25],\n", + " [362, \"08:44\", \"09:35\", 524, 575, 51],\n", + " [363, \"08:45\", \"09:00\", 525, 540, 15],\n", + " [364, \"08:45\", \"09:05\", 525, 545, 20],\n", + " [365, \"08:46\", \"09:24\", 526, 564, 38],\n", + " [366, \"08:46\", \"08:58\", 526, 538, 12],\n", + " [367, \"08:46\", \"09:30\", 526, 570, 44],\n", + " [368, \"08:48\", \"10:11\", 528, 611, 83],\n", + " [369, \"08:48\", \"10:13\", 528, 613, 85],\n", + " [370, \"08:49\", \"09:43\", 529, 583, 54],\n", + " [371, \"08:50\", \"09:30\", 530, 570, 40],\n", + " [372, \"08:50\", \"10:00\", 530, 600, 70],\n", + " [373, \"08:50\", \"09:00\", 530, 540, 10],\n", + " [374, \"08:51\", \"09:17\", 531, 557, 26],\n", + " [375, \"08:53\", \"09:20\", 533, 560, 27],\n", + " [376, \"08:53\", \"09:35\", 533, 575, 42],\n", + " [377, \"08:55\", \"09:34\", 535, 574, 39],\n", + " [378, \"08:55\", \"09:15\", 535, 555, 20],\n", + " [379, \"08:58\", \"09:38\", 538, 578, 40],\n", + " [380, \"08:58\", \"10:26\", 538, 626, 88],\n", + " [381, \"08:59\", \"09:53\", 539, 593, 54],\n", + " [382, \"08:59\", \"09:50\", 539, 590, 51],\n", + " [383, \"09:00\", \"09:35\", 540, 575, 35],\n", + " [384, \"09:00\", \"09:16\", 540, 556, 16],\n", + " [385, \"09:00\", \"09:10\", 540, 550, 10],\n", + " [386, \"09:00\", \"09:16\", 540, 556, 16],\n", + " [387, \"09:01\", \"09:13\", 541, 553, 12],\n", + " [388, \"09:03\", \"09:45\", 543, 585, 42],\n", + " [389, \"09:03\", \"10:28\", 543, 628, 85],\n", + " [390, \"09:05\", \"09:44\", 545, 584, 39],\n", + " [391, \"09:05\", \"09:25\", 545, 565, 20],\n", + " [392, \"09:08\", \"09:53\", 548, 593, 45],\n", + " [393, \"09:08\", \"10:04\", 548, 604, 56],\n", + " [394, \"09:09\", \"10:03\", 549, 603, 54],\n", + " [395, \"09:10\", \"10:15\", 550, 615, 65],\n", + " [396, \"09:10\", \"09:20\", 550, 560, 10],\n", + " [397, \"09:11\", \"09:38\", 551, 578, 27],\n", + " [398, \"09:13\", \"10:00\", 553, 600, 47],\n", + " [399, \"09:14\", \"09:39\", 554, 579, 25],\n", + " [400, \"09:14\", \"10:05\", 554, 605, 51],\n", + " [401, \"09:15\", \"09:54\", 555, 594, 39],\n", + " [402, \"09:16\", \"09:28\", 556, 568, 12],\n", + " [403, \"09:18\", \"10:43\", 558, 643, 85],\n", + " [404, \"09:18\", \"10:41\", 558, 641, 83],\n", + " [405, \"09:18\", \"09:58\", 558, 598, 40],\n", + " [406, \"09:19\", \"10:13\", 559, 613, 54],\n", + " [407, \"09:20\", \"09:30\", 560, 570, 10],\n", + " [408, \"09:20\", \"09:36\", 560, 576, 16],\n", + " [409, \"09:21\", \"09:47\", 561, 587, 26],\n", + " [410, \"09:23\", \"10:30\", 563, 630, 67],\n", + " [411, \"09:23\", \"10:05\", 563, 605, 42],\n", + " [412, \"09:23\", \"09:49\", 563, 589, 26],\n", + " [413, \"09:24\", \"09:35\", 564, 575, 11],\n", + " [414, \"09:25\", \"09:35\", 565, 575, 10],\n", + " [415, \"09:25\", \"10:04\", 565, 604, 39],\n", + " [416, \"09:28\", \"10:08\", 568, 608, 40],\n", + " [417, \"09:29\", \"09:45\", 569, 585, 16],\n", + " [418, \"09:29\", \"10:20\", 569, 620, 51],\n", + " [419, \"09:29\", \"10:56\", 569, 656, 87],\n", + " [420, \"09:29\", \"10:23\", 569, 623, 54],\n", + " [421, \"09:30\", \"09:40\", 570, 580, 10],\n", + " [422, \"09:31\", \"09:43\", 571, 583, 12],\n", + " [423, \"09:33\", \"10:58\", 573, 658, 85],\n", + " [424, \"09:33\", \"10:15\", 573, 615, 42],\n", + " [425, \"09:34\", \"09:45\", 574, 585, 11],\n", + " [426, \"09:35\", \"10:14\", 575, 614, 39],\n", + " [427, \"09:38\", \"10:45\", 578, 645, 67],\n", + " [428, \"09:39\", \"10:33\", 579, 633, 54],\n", + " [429, \"09:40\", \"09:56\", 580, 596, 16],\n", + " [430, \"09:40\", \"09:50\", 580, 590, 10],\n", + " [431, \"09:41\", \"10:08\", 581, 608, 27],\n", + " [432, \"09:41\", \"10:23\", 581, 623, 42],\n", + " [433, \"09:44\", \"10:35\", 584, 635, 51],\n", + " [434, \"09:44\", \"11:11\", 584, 671, 87],\n", + " [435, \"09:44\", \"09:55\", 584, 595, 11],\n", + " [436, \"09:45\", \"10:24\", 585, 624, 39],\n", + " [437, \"09:46\", \"09:58\", 586, 598, 12],\n", + " [438, \"09:48\", \"10:30\", 588, 630, 42],\n", + " [439, \"09:48\", \"11:13\", 588, 673, 85],\n", + " [440, \"09:48\", \"10:04\", 588, 604, 16],\n", + " [441, \"09:49\", \"10:43\", 589, 643, 54],\n", + " [442, \"09:50\", \"10:00\", 590, 600, 10],\n", + " [443, \"09:51\", \"10:17\", 591, 617, 26],\n", + " [444, \"09:53\", \"10:49\", 593, 649, 56],\n", + " [445, \"09:53\", \"11:00\", 593, 660, 67],\n", + " [446, \"09:54\", \"10:05\", 594, 605, 11],\n", + " [447, \"09:55\", \"10:34\", 595, 634, 39],\n", + " [448, \"09:56\", \"10:38\", 596, 638, 42],\n", + " [449, \"09:57\", \"10:20\", 597, 620, 23],\n", + " [450, \"09:59\", \"11:26\", 599, 686, 87],\n", + " [451, \"09:59\", \"10:50\", 599, 650, 51],\n", + " [452, \"09:59\", \"10:53\", 599, 653, 54],\n", + " [453, \"10:00\", \"10:16\", 600, 616, 16],\n", + " [454, \"10:00\", \"10:10\", 600, 610, 10],\n", + " [455, \"10:01\", \"10:13\", 601, 613, 12],\n", + " [456, \"10:03\", \"11:28\", 603, 688, 85],\n", + " [457, \"10:03\", \"10:45\", 603, 645, 42],\n", + " [458, \"10:04\", \"10:15\", 604, 615, 11],\n", + " [459, \"10:05\", \"10:44\", 605, 644, 39],\n", + " [460, \"10:08\", \"11:15\", 608, 675, 67],\n", + " [461, \"10:09\", \"11:03\", 609, 663, 54],\n", + " [462, \"10:10\", \"10:20\", 610, 620, 10],\n", + " [463, \"10:11\", \"10:38\", 611, 638, 27],\n", + " [464, \"10:11\", \"10:53\", 611, 653, 42],\n", + " [465, \"10:14\", \"11:05\", 614, 665, 51],\n", + " [466, \"10:14\", \"11:41\", 614, 701, 87],\n", + " [467, \"10:14\", \"10:25\", 614, 625, 11],\n", + " [468, \"10:15\", \"10:54\", 615, 654, 39],\n", + " [469, \"10:16\", \"10:28\", 616, 628, 12],\n", + " [470, \"10:18\", \"11:43\", 618, 703, 85],\n", + " [471, \"10:18\", \"11:00\", 618, 660, 42],\n", + " [472, \"10:19\", \"11:13\", 619, 673, 54],\n", + " [473, \"10:20\", \"10:30\", 620, 630, 10],\n", + " [474, \"10:20\", \"10:36\", 620, 636, 16],\n", + " [475, \"10:21\", \"10:47\", 621, 647, 26],\n", + " [476, \"10:23\", \"11:30\", 623, 690, 67],\n", + " [477, \"10:23\", \"10:45\", 623, 645, 22],\n", + " [478, \"10:24\", \"10:35\", 624, 635, 11],\n", + " [479, \"10:25\", \"11:04\", 625, 664, 39],\n", + " [480, \"10:26\", \"11:08\", 626, 668, 42],\n", + " [481, \"10:29\", \"11:20\", 629, 680, 51],\n", + " [482, \"10:29\", \"11:23\", 629, 683, 54],\n", + " [483, \"10:29\", \"11:56\", 629, 716, 87],\n", + " [484, \"10:30\", \"10:40\", 630, 640, 10],\n", + " [485, \"10:31\", \"10:43\", 631, 643, 12],\n", + " [486, \"10:33\", \"11:15\", 633, 675, 42],\n", + " [487, \"10:33\", \"11:58\", 633, 718, 85],\n", + " [488, \"10:34\", \"10:45\", 634, 645, 11],\n", + " [489, \"10:35\", \"11:14\", 635, 674, 39],\n", + " [490, \"10:38\", \"11:45\", 638, 705, 67],\n", + " [491, \"10:39\", \"11:33\", 639, 693, 54],\n", + " [492, \"10:40\", \"10:50\", 640, 650, 10],\n", + " [493, \"10:40\", \"10:56\", 640, 656, 16],\n", + " [494, \"10:41\", \"11:23\", 641, 683, 42],\n", + " [495, \"10:41\", \"11:08\", 641, 668, 27],\n", + " [496, \"10:44\", \"12:11\", 644, 731, 87],\n", + " [497, \"10:44\", \"11:35\", 644, 695, 51],\n", + " [498, \"10:44\", \"10:55\", 644, 655, 11],\n", + " [499, \"10:45\", \"11:24\", 645, 684, 39],\n", + " [500, \"10:46\", \"10:58\", 646, 658, 12],\n", + " [501, \"10:48\", \"12:13\", 648, 733, 85],\n", + " [502, \"10:48\", \"11:30\", 648, 690, 42],\n", + " [503, \"10:49\", \"11:43\", 649, 703, 54],\n", + " [504, \"10:50\", \"11:00\", 650, 660, 10],\n", + " [505, \"10:51\", \"11:17\", 651, 677, 26],\n", + " [506, \"10:53\", \"12:00\", 653, 720, 67],\n", + " [507, \"10:53\", \"11:20\", 653, 680, 27],\n", + " [508, \"10:54\", \"11:05\", 654, 665, 11],\n", + " [509, \"10:55\", \"11:34\", 655, 694, 39],\n", + " [510, \"10:56\", \"11:38\", 656, 698, 42],\n", + " [511, \"10:59\", \"11:14\", 659, 674, 15],\n", + " [512, \"10:59\", \"12:26\", 659, 746, 87],\n", + " [513, \"10:59\", \"11:53\", 659, 713, 54],\n", + " [514, \"10:59\", \"11:50\", 659, 710, 51],\n", + " [515, \"11:00\", \"11:16\", 660, 676, 16],\n", + " [516, \"11:00\", \"11:10\", 660, 670, 10],\n", + " [517, \"11:01\", \"11:13\", 661, 673, 12],\n", + " [518, \"11:03\", \"11:45\", 663, 705, 42],\n", + " [519, \"11:03\", \"12:28\", 663, 748, 85],\n", + " [520, \"11:04\", \"11:15\", 664, 675, 11],\n", + " [521, \"11:05\", \"11:44\", 665, 704, 39],\n", + " [522, \"11:08\", \"12:15\", 668, 735, 67],\n", + " [523, \"11:09\", \"12:03\", 669, 723, 54],\n", + " [524, \"11:10\", \"11:20\", 670, 680, 10],\n", + " [525, \"11:11\", \"11:38\", 671, 698, 27],\n", + " [526, \"11:11\", \"11:53\", 671, 713, 42],\n", + " [527, \"11:14\", \"11:25\", 674, 685, 11],\n", + " [528, \"11:14\", \"12:05\", 674, 725, 51],\n", + " [529, \"11:14\", \"12:38\", 674, 758, 84],\n", + " [530, \"11:14\", \"12:41\", 674, 761, 87],\n", + " [531, \"11:15\", \"11:54\", 675, 714, 39],\n", + " [532, \"11:16\", \"11:28\", 676, 688, 12],\n", + " [533, \"11:18\", \"12:00\", 678, 720, 42],\n", + " [534, \"11:19\", \"12:13\", 679, 733, 54],\n", + " [535, \"11:20\", \"11:30\", 680, 690, 10],\n", + " [536, \"11:20\", \"11:36\", 680, 696, 16],\n", + " [537, \"11:21\", \"11:47\", 681, 707, 26],\n", + " [538, \"11:23\", \"12:30\", 683, 750, 67],\n", + " [539, \"11:23\", \"11:49\", 683, 709, 26],\n", + " [540, \"11:24\", \"12:48\", 684, 768, 84],\n", + " [541, \"11:24\", \"11:35\", 684, 695, 11],\n", + " [542, \"11:25\", \"12:04\", 685, 724, 39],\n", + " [543, \"11:26\", \"12:08\", 686, 728, 42],\n", + " [544, \"11:29\", \"11:44\", 689, 704, 15],\n", + " [545, \"11:29\", \"12:23\", 689, 743, 54],\n", + " [546, \"11:29\", \"12:20\", 689, 740, 51],\n", + " [547, \"11:29\", \"12:54\", 689, 774, 85],\n", + " [548, \"11:30\", \"11:40\", 690, 700, 10],\n", + " [549, \"11:31\", \"11:43\", 691, 703, 12],\n", + " [550, \"11:33\", \"12:15\", 693, 735, 42],\n", + " [551, \"11:34\", \"12:58\", 694, 778, 84],\n", + " [552, \"11:34\", \"11:45\", 694, 705, 11],\n", + " [553, \"11:35\", \"12:14\", 695, 734, 39],\n", + " [554, \"11:38\", \"12:45\", 698, 765, 67],\n", + " [555, \"11:39\", \"12:33\", 699, 753, 54],\n", + " [556, \"11:40\", \"11:56\", 700, 716, 16],\n", + " [557, \"11:40\", \"11:50\", 700, 710, 10],\n", + " [558, \"11:41\", \"12:08\", 701, 728, 27],\n", + " [559, \"11:41\", \"12:23\", 701, 743, 42],\n", + " [560, \"11:44\", \"11:55\", 704, 715, 11],\n", + " [561, \"11:44\", \"13:14\", 704, 794, 90],\n", + " [562, \"11:44\", \"13:08\", 704, 788, 84],\n", + " [563, \"11:44\", \"12:35\", 704, 755, 51],\n", + " [564, \"11:45\", \"12:24\", 705, 744, 39],\n", + " [565, \"11:46\", \"11:58\", 706, 718, 12],\n", + " [566, \"11:48\", \"12:30\", 708, 750, 42],\n", + " [567, \"11:49\", \"12:43\", 709, 763, 54],\n", + " [568, \"11:50\", \"12:00\", 710, 720, 10],\n", + " [569, \"11:51\", \"12:17\", 711, 737, 26],\n", + " [570, \"11:53\", \"12:49\", 713, 769, 56],\n", + " [571, \"11:53\", \"13:00\", 713, 780, 67],\n", + " [572, \"11:54\", \"13:18\", 714, 798, 84],\n", + " [573, \"11:54\", \"12:05\", 714, 725, 11],\n", + " [574, \"11:55\", \"12:40\", 715, 760, 45],\n", + " [575, \"11:55\", \"12:34\", 715, 754, 39],\n", + " [576, \"11:56\", \"12:35\", 716, 755, 39],\n", + " [577, \"11:57\", \"12:20\", 717, 740, 23],\n", + " [578, \"11:58\", \"12:29\", 718, 749, 31],\n", + " [579, \"11:59\", \"12:50\", 719, 770, 51],\n", + " [580, \"11:59\", \"12:53\", 719, 773, 54],\n", + " [581, \"11:59\", \"13:24\", 719, 804, 85],\n", + " [582, \"11:59\", \"12:14\", 719, 734, 15],\n", + " [583, \"12:00\", \"12:16\", 720, 736, 16],\n", + " [584, \"12:00\", \"12:10\", 720, 730, 10],\n", + " [585, \"12:01\", \"12:45\", 721, 765, 44],\n", + " [586, \"12:01\", \"12:13\", 721, 733, 12],\n", + " [587, \"12:03\", \"12:50\", 723, 770, 47],\n", + " [588, \"12:04\", \"12:15\", 724, 735, 11],\n", + " [589, \"12:04\", \"13:04\", 724, 784, 60],\n", + " [590, \"12:04\", \"13:28\", 724, 808, 84],\n", + " [591, \"12:05\", \"12:44\", 725, 764, 39],\n", + " [592, \"12:08\", \"13:11\", 728, 791, 63],\n", + " [593, \"12:08\", \"12:39\", 728, 759, 31],\n", + " [594, \"12:09\", \"13:03\", 729, 783, 54],\n", + " [595, \"12:10\", \"12:20\", 730, 740, 10],\n", + " [596, \"12:11\", \"12:55\", 731, 775, 44],\n", + " [597, \"12:11\", \"12:38\", 731, 758, 27],\n", + " [598, \"12:14\", \"13:05\", 734, 785, 51],\n", + " [599, \"12:14\", \"12:25\", 734, 745, 11],\n", + " [600, \"12:14\", \"13:44\", 734, 824, 90],\n", + " [601, \"12:14\", \"13:38\", 734, 818, 84],\n", + " [602, \"12:15\", \"12:54\", 735, 774, 39],\n", + " [603, \"12:16\", \"12:28\", 736, 748, 12],\n", + " [604, \"12:18\", \"13:00\", 738, 780, 42],\n", + " [605, \"12:19\", \"13:13\", 739, 793, 54],\n", + " [606, \"12:20\", \"12:30\", 740, 750, 10],\n", + " [607, \"12:20\", \"13:31\", 740, 811, 71],\n", + " [608, \"12:20\", \"12:30\", 740, 750, 10],\n", + " [609, \"12:20\", \"12:36\", 740, 756, 16],\n", + " [610, \"12:21\", \"12:47\", 741, 767, 26],\n", + " [611, \"12:23\", \"12:45\", 743, 765, 22],\n", + " [612, \"12:24\", \"12:35\", 744, 755, 11],\n", + " [613, \"12:24\", \"13:48\", 744, 828, 84],\n", + " [614, \"12:25\", \"13:10\", 745, 790, 45],\n", + " [615, \"12:25\", \"13:04\", 745, 784, 39],\n", + " [616, \"12:26\", \"13:05\", 746, 785, 39],\n", + " [617, \"12:28\", \"13:54\", 748, 834, 86],\n", + " [618, \"12:28\", \"12:38\", 748, 758, 10],\n", + " [619, \"12:28\", \"13:15\", 748, 795, 47],\n", + " [620, \"12:29\", \"13:23\", 749, 803, 54],\n", + " [621, \"12:30\", \"13:41\", 750, 821, 71],\n", + " [622, \"12:30\", \"12:40\", 750, 760, 10],\n", + " [623, \"12:31\", \"13:15\", 751, 795, 44],\n", + " [624, \"12:31\", \"12:43\", 751, 763, 12],\n", + " [625, \"12:33\", \"12:48\", 753, 768, 15],\n", + " [626, \"12:33\", \"13:20\", 753, 800, 47],\n", + " [627, \"12:34\", \"13:58\", 754, 838, 84],\n", + " [628, \"12:34\", \"13:34\", 754, 814, 60],\n", + " [629, \"12:34\", \"12:45\", 754, 765, 11],\n", + " [630, \"12:35\", \"13:14\", 755, 794, 39],\n", + " [631, \"12:38\", \"13:25\", 758, 805, 47],\n", + " [632, \"12:38\", \"13:25\", 758, 805, 47],\n", + " [633, \"12:38\", \"14:04\", 758, 844, 86],\n", + " [634, \"12:39\", \"13:33\", 759, 813, 54],\n", + " [635, \"12:40\", \"13:51\", 760, 831, 71],\n", + " [636, \"12:40\", \"12:50\", 760, 770, 10],\n", + " [637, \"12:40\", \"12:56\", 760, 776, 16],\n", + " [638, \"12:41\", \"13:08\", 761, 788, 27],\n", + " [639, \"12:43\", \"13:30\", 763, 810, 47],\n", + " [640, \"12:44\", \"12:55\", 764, 775, 11],\n", + " [641, \"12:44\", \"14:08\", 764, 848, 84],\n", + " [642, \"12:45\", \"13:24\", 765, 804, 39],\n", + " [643, \"12:46\", \"12:58\", 766, 778, 12],\n", + " [644, \"12:46\", \"13:21\", 766, 801, 35],\n", + " [645, \"12:48\", \"14:14\", 768, 854, 86],\n", + " [646, \"12:48\", \"13:35\", 768, 815, 47],\n", + " [647, \"12:48\", \"12:58\", 768, 778, 10],\n", + " [648, \"12:48\", \"13:35\", 768, 815, 47],\n", + " [649, \"12:49\", \"13:43\", 769, 823, 54],\n", + " [650, \"12:50\", \"14:01\", 770, 841, 71],\n", + " [651, \"12:50\", \"13:00\", 770, 780, 10],\n", + " [652, \"12:50\", \"13:00\", 770, 780, 10],\n", + " [653, \"12:51\", \"13:17\", 771, 797, 26],\n", + " [654, \"12:53\", \"13:20\", 773, 800, 27],\n", + " [655, \"12:53\", \"13:24\", 773, 804, 31],\n", + " [656, \"12:53\", \"13:40\", 773, 820, 47],\n", + " [657, \"12:54\", \"14:18\", 774, 858, 84],\n", + " [658, \"12:54\", \"13:05\", 774, 785, 11],\n", + " [659, \"12:55\", \"13:34\", 775, 814, 39],\n", + " [660, \"12:58\", \"14:24\", 778, 864, 86],\n", + " [661, \"12:58\", \"13:25\", 778, 805, 27],\n", + " [662, \"12:58\", \"13:45\", 778, 825, 47],\n", + " [663, \"12:58\", \"13:45\", 778, 825, 47],\n", + " [664, \"12:59\", \"13:53\", 779, 833, 54],\n", + " [665, \"13:00\", \"13:10\", 780, 790, 10],\n", + " [666, \"13:00\", \"13:16\", 780, 796, 16],\n", + " [667, \"13:00\", \"14:11\", 780, 851, 71],\n", + " [668, \"13:01\", \"13:13\", 781, 793, 12],\n", + " [669, \"13:03\", \"13:34\", 783, 814, 31],\n", + " [670, \"13:03\", \"13:50\", 783, 830, 47],\n", + " [671, \"13:04\", \"13:15\", 784, 795, 11],\n", + " [672, \"13:04\", \"14:28\", 784, 868, 84],\n", + " [673, \"13:05\", \"13:44\", 785, 824, 39],\n", + " [674, \"13:08\", \"13:55\", 788, 835, 47],\n", + " [675, \"13:08\", \"14:34\", 788, 874, 86],\n", + " [676, \"13:08\", \"13:55\", 788, 835, 47],\n", + " [677, \"13:09\", \"14:03\", 789, 843, 54],\n", + " [678, \"13:10\", \"13:20\", 790, 800, 10],\n", + " [679, \"13:10\", \"14:21\", 790, 861, 71],\n", + " [680, \"13:13\", \"14:00\", 793, 840, 47],\n", + " [681, \"13:13\", \"13:40\", 793, 820, 27],\n", + " [682, \"13:14\", \"14:38\", 794, 878, 84],\n", + " [683, \"13:14\", \"13:25\", 794, 805, 11],\n", + " [684, \"13:15\", \"13:54\", 795, 834, 39],\n", + " [685, \"13:16\", \"13:28\", 796, 808, 12],\n", + " [686, \"13:18\", \"14:05\", 798, 845, 47],\n", + " [687, \"13:18\", \"14:44\", 798, 884, 86],\n", + " [688, \"13:18\", \"14:05\", 798, 845, 47],\n", + " [689, \"13:19\", \"14:13\", 799, 853, 54],\n", + " [690, \"13:20\", \"13:36\", 800, 816, 16],\n", + " [691, \"13:20\", \"14:31\", 800, 871, 71],\n", + " [692, \"13:20\", \"13:30\", 800, 810, 10],\n", + " [693, \"13:21\", \"13:47\", 801, 827, 26],\n", + " [694, \"13:23\", \"14:10\", 803, 850, 47],\n", + " [695, \"13:23\", \"13:49\", 803, 829, 26],\n", + " [696, \"13:24\", \"14:48\", 804, 888, 84],\n", + " [697, \"13:24\", \"13:35\", 804, 815, 11],\n", + " [698, \"13:25\", \"14:04\", 805, 844, 39],\n", + " [699, \"13:28\", \"14:15\", 808, 855, 47],\n", + " [700, \"13:28\", \"14:54\", 808, 894, 86],\n", + " [701, \"13:28\", \"13:55\", 808, 835, 27],\n", + " [702, \"13:28\", \"14:15\", 808, 855, 47],\n", + " [703, \"13:29\", \"14:23\", 809, 863, 54],\n", + " [704, \"13:30\", \"13:40\", 810, 820, 10],\n", + " [705, \"13:30\", \"14:41\", 810, 881, 71],\n", + " [706, \"13:31\", \"13:43\", 811, 823, 12],\n", + " [707, \"13:33\", \"14:20\", 813, 860, 47],\n", + " [708, \"13:34\", \"14:58\", 814, 898, 84],\n", + " [709, \"13:34\", \"13:45\", 814, 825, 11],\n", + " [710, \"13:35\", \"14:14\", 815, 854, 39],\n", + " [711, \"13:38\", \"14:25\", 818, 865, 47],\n", + " [712, \"13:38\", \"14:25\", 818, 865, 47],\n", + " [713, \"13:38\", \"15:04\", 818, 904, 86],\n", + " [714, \"13:39\", \"14:33\", 819, 873, 54],\n", + " [715, \"13:40\", \"13:50\", 820, 830, 10],\n", + " [716, \"13:40\", \"13:56\", 820, 836, 16],\n", + " [717, \"13:40\", \"14:51\", 820, 891, 71],\n", + " [718, \"13:43\", \"14:30\", 823, 870, 47],\n", + " [719, \"13:43\", \"14:10\", 823, 850, 27],\n", + " [720, \"13:44\", \"15:09\", 824, 909, 85],\n", + " [721, \"13:44\", \"13:55\", 824, 835, 11],\n", + " [722, \"13:45\", \"14:24\", 825, 864, 39],\n", + " [723, \"13:46\", \"13:58\", 826, 838, 12],\n", + " [724, \"13:48\", \"14:35\", 828, 875, 47],\n", + " [725, \"13:48\", \"15:14\", 828, 914, 86],\n", + " [726, \"13:48\", \"14:35\", 828, 875, 47],\n", + " [727, \"13:49\", \"14:43\", 829, 883, 54],\n", + " [728, \"13:50\", \"14:00\", 830, 840, 10],\n", + " [729, \"13:50\", \"15:01\", 830, 901, 71],\n", + " [730, \"13:51\", \"14:17\", 831, 857, 26],\n", + " [731, \"13:53\", \"14:40\", 833, 880, 47],\n", + " [732, \"13:53\", \"14:49\", 833, 889, 56],\n", + " [733, \"13:54\", \"14:05\", 834, 845, 11],\n", + " [734, \"13:54\", \"15:19\", 834, 919, 85],\n", + " [735, \"13:55\", \"14:34\", 835, 874, 39],\n", + " [736, \"13:57\", \"14:20\", 837, 860, 23],\n", + " [737, \"13:58\", \"15:24\", 838, 924, 86],\n", + " [738, \"13:58\", \"14:45\", 838, 885, 47],\n", + " [739, \"13:58\", \"14:45\", 838, 885, 47],\n", + " [740, \"13:58\", \"14:25\", 838, 865, 27],\n", + " [741, \"13:59\", \"14:53\", 839, 893, 54],\n", + " [742, \"14:00\", \"14:16\", 840, 856, 16],\n", + " [743, \"14:00\", \"14:10\", 840, 850, 10],\n", + " [744, \"14:00\", \"15:11\", 840, 911, 71],\n", + " [745, \"14:01\", \"14:13\", 841, 853, 12],\n", + " [746, \"14:03\", \"14:50\", 843, 890, 47],\n", + " [747, \"14:04\", \"14:15\", 844, 855, 11],\n", + " [748, \"14:04\", \"15:29\", 844, 929, 85],\n", + " [749, \"14:05\", \"14:44\", 845, 884, 39],\n", + " [750, \"14:08\", \"14:55\", 848, 895, 47],\n", + " [751, \"14:08\", \"14:55\", 848, 895, 47],\n", + " [752, \"14:08\", \"15:34\", 848, 934, 86],\n", + " [753, \"14:09\", \"15:03\", 849, 903, 54],\n", + " [754, \"14:10\", \"15:21\", 850, 921, 71],\n", + " [755, \"14:10\", \"14:20\", 850, 860, 10],\n", + " [756, \"14:13\", \"15:00\", 853, 900, 47],\n", + " [757, \"14:13\", \"14:40\", 853, 880, 27],\n", + " [758, \"14:14\", \"15:40\", 854, 940, 86],\n", + " [759, \"14:14\", \"14:25\", 854, 865, 11],\n", + " [760, \"14:15\", \"14:54\", 855, 894, 39],\n", + " [761, \"14:16\", \"14:28\", 856, 868, 12],\n", + " [762, \"14:18\", \"15:05\", 858, 905, 47],\n", + " [763, \"14:18\", \"15:44\", 858, 944, 86],\n", + " [764, \"14:18\", \"15:05\", 858, 905, 47],\n", + " [765, \"14:19\", \"15:13\", 859, 913, 54],\n", + " [766, \"14:20\", \"15:31\", 860, 931, 71],\n", + " [767, \"14:20\", \"14:30\", 860, 870, 10],\n", + " [768, \"14:20\", \"14:36\", 860, 876, 16],\n", + " [769, \"14:21\", \"14:47\", 861, 887, 26],\n", + " [770, \"14:23\", \"15:10\", 863, 910, 47],\n", + " [771, \"14:23\", \"14:45\", 863, 885, 22],\n", + " [772, \"14:24\", \"15:50\", 864, 950, 86],\n", + " [773, \"14:24\", \"14:35\", 864, 875, 11],\n", + " [774, \"14:25\", \"15:02\", 865, 902, 37],\n", + " [775, \"14:26\", \"14:52\", 866, 892, 26],\n", + " [776, \"14:28\", \"15:15\", 868, 915, 47],\n", + " [777, \"14:28\", \"14:55\", 868, 895, 27],\n", + " [778, \"14:28\", \"15:54\", 868, 954, 86],\n", + " [779, \"14:28\", \"15:15\", 868, 915, 47],\n", + " [780, \"14:29\", \"15:23\", 869, 923, 54],\n", + " [781, \"14:30\", \"15:41\", 870, 941, 71],\n", + " [782, \"14:30\", \"14:40\", 870, 880, 10],\n", + " [783, \"14:31\", \"14:43\", 871, 883, 12],\n", + " [784, \"14:33\", \"15:20\", 873, 920, 47],\n", + " [785, \"14:34\", \"16:00\", 874, 960, 86],\n", + " [786, \"14:34\", \"14:45\", 874, 885, 11],\n", + " [787, \"14:35\", \"15:11\", 875, 911, 36],\n", + " [788, \"14:38\", \"15:25\", 878, 925, 47],\n", + " [789, \"14:38\", \"15:25\", 878, 925, 47],\n", + " [790, \"14:38\", \"16:04\", 878, 964, 86],\n", + " [791, \"14:39\", \"15:33\", 879, 933, 54],\n", + " [792, \"14:40\", \"14:50\", 880, 890, 10],\n", + " [793, \"14:40\", \"15:51\", 880, 951, 71],\n", + " [794, \"14:40\", \"14:56\", 880, 896, 16],\n", + " [795, \"14:43\", \"15:30\", 883, 930, 47],\n", + " [796, \"14:43\", \"15:10\", 883, 910, 27],\n", + " [797, \"14:44\", \"15:00\", 884, 900, 16],\n", + " [798, \"14:44\", \"16:10\", 884, 970, 86],\n", + " [799, \"14:45\", \"15:19\", 885, 919, 34],\n", + " [800, \"14:46\", \"14:58\", 886, 898, 12],\n", + " [801, \"14:48\", \"15:35\", 888, 935, 47],\n", + " [802, \"14:48\", \"15:35\", 888, 935, 47],\n", + " [803, \"14:48\", \"17:04\", 888, 1024, 136],\n", + " [804, \"14:49\", \"15:43\", 889, 943, 54],\n", + " [805, \"14:50\", \"16:01\", 890, 961, 71],\n", + " [806, \"14:50\", \"15:00\", 890, 900, 10],\n", + " [807, \"14:51\", \"15:17\", 891, 917, 26],\n", + " [808, \"14:52\", \"15:27\", 892, 927, 35],\n", + " [809, \"14:52\", \"15:21\", 892, 921, 29],\n", + " [810, \"14:53\", \"15:40\", 893, 940, 47],\n", + " [811, \"14:54\", \"15:08\", 894, 908, 14],\n", + " [812, \"14:54\", \"16:20\", 894, 980, 86],\n", + " [813, \"14:58\", \"16:24\", 898, 984, 86],\n", + " [814, \"14:58\", \"15:45\", 898, 945, 47],\n", + " [815, \"14:58\", \"15:25\", 898, 925, 27],\n", + " [816, \"14:58\", \"15:45\", 898, 945, 47],\n", + " [817, \"14:59\", \"15:53\", 899, 953, 54],\n", + " [818, \"15:00\", \"15:10\", 900, 910, 10],\n", + " [819, \"15:00\", \"15:35\", 900, 935, 35],\n", + " [820, \"15:00\", \"16:11\", 900, 971, 71],\n", + " [821, \"15:00\", \"15:16\", 900, 916, 16],\n", + " [822, \"15:01\", \"15:13\", 901, 913, 12],\n", + " [823, \"15:02\", \"15:16\", 902, 916, 14],\n", + " [824, \"15:03\", \"15:50\", 903, 950, 47],\n", + " [825, \"15:04\", \"16:30\", 904, 990, 86],\n", + " [826, \"15:08\", \"16:34\", 908, 994, 86],\n", + " [827, \"15:08\", \"15:55\", 908, 955, 47],\n", + " [828, \"15:08\", \"15:55\", 908, 955, 47],\n", + " [829, \"15:08\", \"15:45\", 908, 945, 37],\n", + " [830, \"15:09\", \"16:14\", 909, 974, 65],\n", + " [831, \"15:09\", \"16:03\", 909, 963, 54],\n", + " [832, \"15:10\", \"16:21\", 910, 981, 71],\n", + " [833, \"15:10\", \"15:20\", 910, 920, 10],\n", + " [834, \"15:11\", \"15:24\", 911, 924, 13],\n", + " [835, \"15:12\", \"15:36\", 912, 936, 24],\n", + " [836, \"15:13\", \"16:00\", 913, 960, 47],\n", + " [837, \"15:13\", \"15:40\", 913, 940, 27],\n", + " [838, \"15:14\", \"16:40\", 914, 1000, 86],\n", + " [839, \"15:16\", \"15:28\", 916, 928, 12],\n", + " [840, \"15:16\", \"15:55\", 916, 955, 39],\n", + " [841, \"15:18\", \"16:05\", 918, 965, 47],\n", + " [842, \"15:18\", \"16:44\", 918, 1004, 86],\n", + " [843, \"15:18\", \"16:05\", 918, 965, 47],\n", + " [844, \"15:19\", \"16:13\", 919, 973, 54],\n", + " [845, \"15:19\", \"15:34\", 919, 934, 15],\n", + " [846, \"15:20\", \"15:30\", 920, 930, 10],\n", + " [847, \"15:20\", \"16:31\", 920, 991, 71],\n", + " [848, \"15:20\", \"15:36\", 920, 936, 16],\n", + " [849, \"15:21\", \"15:47\", 921, 947, 26],\n", + " [850, \"15:21\", \"16:06\", 921, 966, 45],\n", + " [851, \"15:23\", \"16:10\", 923, 970, 47],\n", + " [852, \"15:24\", \"16:50\", 924, 1010, 86],\n", + " [853, \"15:24\", \"16:05\", 924, 965, 41],\n", + " [854, \"15:27\", \"15:51\", 927, 951, 24],\n", + " [855, \"15:27\", \"15:44\", 927, 944, 17],\n", + " [856, \"15:28\", \"16:15\", 928, 975, 47],\n", + " [857, \"15:28\", \"16:54\", 928, 1014, 86],\n", + " [858, \"15:28\", \"16:15\", 928, 975, 47],\n", + " [859, \"15:28\", \"15:55\", 928, 955, 27],\n", + " [860, \"15:29\", \"16:23\", 929, 983, 54],\n", + " [861, \"15:30\", \"16:41\", 930, 1001, 71],\n", + " [862, \"15:30\", \"15:40\", 930, 940, 10],\n", + " [863, \"15:31\", \"15:43\", 931, 943, 12],\n", + " [864, \"15:33\", \"16:20\", 933, 980, 47],\n", + " [865, \"15:34\", \"17:00\", 934, 1020, 86],\n", + " [866, \"15:34\", \"16:15\", 934, 975, 41],\n", + " [867, \"15:35\", \"15:54\", 935, 954, 19],\n", + " [868, \"15:36\", \"16:21\", 936, 981, 45],\n", + " [869, \"15:38\", \"16:25\", 938, 985, 47],\n", + " [870, \"15:38\", \"16:25\", 938, 985, 47],\n", + " [871, \"15:38\", \"16:39\", 938, 999, 61],\n", + " [872, \"15:39\", \"16:33\", 939, 993, 54],\n", + " [873, \"15:40\", \"15:50\", 940, 950, 10],\n", + " [874, \"15:40\", \"16:51\", 940, 1011, 71],\n", + " [875, \"15:40\", \"15:56\", 940, 956, 16],\n", + " [876, \"15:43\", \"16:10\", 943, 970, 27],\n", + " [877, \"15:43\", \"16:30\", 943, 990, 47],\n", + " [878, \"15:44\", \"17:10\", 944, 1030, 86],\n", + " [879, \"15:44\", \"16:25\", 944, 985, 41],\n", + " [880, \"15:45\", \"16:04\", 945, 964, 19],\n", + " [881, \"15:46\", \"15:58\", 946, 958, 12],\n", + " [882, \"15:48\", \"16:35\", 948, 995, 47],\n", + " [883, \"15:48\", \"16:35\", 948, 995, 47],\n", + " [884, \"15:48\", \"17:14\", 948, 1034, 86],\n", + " [885, \"15:49\", \"16:43\", 949, 1003, 54],\n", + " [886, \"15:50\", \"16:00\", 950, 960, 10],\n", + " [887, \"15:50\", \"17:01\", 950, 1021, 71],\n", + " [888, \"15:51\", \"16:18\", 951, 978, 27],\n", + " [889, \"15:52\", \"16:36\", 952, 996, 44],\n", + " [890, \"15:53\", \"16:40\", 953, 1000, 47],\n", + " [891, \"15:54\", \"17:20\", 954, 1040, 86],\n", + " [892, \"15:54\", \"16:35\", 954, 995, 41],\n", + " [893, \"15:55\", \"16:14\", 955, 974, 19],\n", + " [894, \"15:58\", \"16:25\", 958, 985, 27],\n", + " [895, \"15:58\", \"16:45\", 958, 1005, 47],\n", + " [896, \"15:58\", \"16:45\", 958, 1005, 47],\n", + " [897, \"15:58\", \"17:24\", 958, 1044, 86],\n", + " [898, \"15:59\", \"17:11\", 959, 1031, 72],\n", + " [899, \"15:59\", \"16:53\", 959, 1013, 54],\n", + " [900, \"16:00\", \"16:10\", 960, 970, 10],\n", + " [901, \"16:00\", \"16:16\", 960, 976, 16],\n", + " [902, \"16:01\", \"16:13\", 961, 973, 12],\n", + " [903, \"16:03\", \"16:50\", 963, 1010, 47],\n", + " [904, \"16:04\", \"17:30\", 964, 1050, 86],\n", + " [905, \"16:04\", \"16:45\", 964, 1005, 41],\n", + " [906, \"16:05\", \"16:24\", 965, 984, 19],\n", + " [907, \"16:06\", \"16:51\", 966, 1011, 45],\n", + " [908, \"16:08\", \"16:55\", 968, 1015, 47],\n", + " [909, \"16:08\", \"17:34\", 968, 1054, 86],\n", + " [910, \"16:08\", \"16:55\", 968, 1015, 47],\n", + " [911, \"16:09\", \"17:03\", 969, 1023, 54],\n", + " [912, \"16:09\", \"17:21\", 969, 1041, 72],\n", + " [913, \"16:10\", \"16:20\", 970, 980, 10],\n", + " [914, \"16:13\", \"16:40\", 973, 1000, 27],\n", + " [915, \"16:13\", \"17:00\", 973, 1020, 47],\n", + " [916, \"16:14\", \"16:55\", 974, 1015, 41],\n", + " [917, \"16:14\", \"17:40\", 974, 1060, 86],\n", + " [918, \"16:15\", \"16:34\", 975, 994, 19],\n", + " [919, \"16:16\", \"16:28\", 976, 988, 12],\n", + " [920, \"16:18\", \"17:05\", 978, 1025, 47],\n", + " [921, \"16:18\", \"17:05\", 978, 1025, 47],\n", + " [922, \"16:18\", \"17:44\", 978, 1064, 86],\n", + " [923, \"16:19\", \"17:31\", 979, 1051, 72],\n", + " [924, \"16:19\", \"17:13\", 979, 1033, 54],\n", + " [925, \"16:20\", \"16:30\", 980, 990, 10],\n", + " [926, \"16:20\", \"16:36\", 980, 996, 16],\n", + " [927, \"16:21\", \"16:48\", 981, 1008, 27],\n", + " [928, \"16:22\", \"17:06\", 982, 1026, 44],\n", + " [929, \"16:23\", \"17:10\", 983, 1030, 47],\n", + " [930, \"16:24\", \"17:05\", 984, 1025, 41],\n", + " [931, \"16:24\", \"17:50\", 984, 1070, 86],\n", + " [932, \"16:25\", \"16:44\", 985, 1004, 19],\n", + " [933, \"16:28\", \"17:15\", 988, 1035, 47],\n", + " [934, \"16:28\", \"17:15\", 988, 1035, 47],\n", + " [935, \"16:28\", \"16:55\", 988, 1015, 27],\n", + " [936, \"16:28\", \"17:54\", 988, 1074, 86],\n", + " [937, \"16:29\", \"17:23\", 989, 1043, 54],\n", + " [938, \"16:29\", \"17:41\", 989, 1061, 72],\n", + " [939, \"16:30\", \"16:40\", 990, 1000, 10],\n", + " [940, \"16:31\", \"16:43\", 991, 1003, 12],\n", + " [941, \"16:33\", \"17:20\", 993, 1040, 47],\n", + " [942, \"16:34\", \"17:15\", 994, 1035, 41],\n", + " [943, \"16:34\", \"18:00\", 994, 1080, 86],\n", + " [944, \"16:35\", \"16:54\", 995, 1014, 19],\n", + " [945, \"16:36\", \"17:21\", 996, 1041, 45],\n", + " [946, \"16:38\", \"17:25\", 998, 1045, 47],\n", + " [947, \"16:38\", \"17:25\", 998, 1045, 47],\n", + " [948, \"16:38\", \"18:04\", 998, 1084, 86],\n", + " [949, \"16:39\", \"17:33\", 999, 1053, 54],\n", + " [950, \"16:39\", \"17:51\", 999, 1071, 72],\n", + " [951, \"16:40\", \"16:56\", 1000, 1016, 16],\n", + " [952, \"16:40\", \"16:50\", 1000, 1010, 10],\n", + " [953, \"16:43\", \"17:10\", 1003, 1030, 27],\n", + " [954, \"16:43\", \"17:30\", 1003, 1050, 47],\n", + " [955, \"16:44\", \"17:25\", 1004, 1045, 41],\n", + " [956, \"16:44\", \"18:10\", 1004, 1090, 86],\n", + " [957, \"16:45\", \"17:04\", 1005, 1024, 19],\n", + " [958, \"16:46\", \"16:58\", 1006, 1018, 12],\n", + " [959, \"16:48\", \"18:14\", 1008, 1094, 86],\n", + " [960, \"16:48\", \"17:35\", 1008, 1055, 47],\n", + " [961, \"16:48\", \"17:35\", 1008, 1055, 47],\n", + " [962, \"16:49\", \"18:01\", 1009, 1081, 72],\n", + " [963, \"16:49\", \"17:43\", 1009, 1063, 54],\n", + " [964, \"16:50\", \"17:00\", 1010, 1020, 10],\n", + " [965, \"16:51\", \"17:18\", 1011, 1038, 27],\n", + " [966, \"16:52\", \"17:36\", 1012, 1056, 44],\n", + " [967, \"16:53\", \"17:40\", 1013, 1060, 47],\n", + " [968, \"16:54\", \"18:20\", 1014, 1100, 86],\n", + " [969, \"16:54\", \"17:35\", 1014, 1055, 41],\n", + " [970, \"16:55\", \"17:14\", 1015, 1034, 19],\n", + " [971, \"16:58\", \"17:25\", 1018, 1045, 27],\n", + " [972, \"16:58\", \"17:45\", 1018, 1065, 47],\n", + " [973, \"16:58\", \"17:45\", 1018, 1065, 47],\n", + " [974, \"16:58\", \"18:24\", 1018, 1104, 86],\n", + " [975, \"16:59\", \"18:11\", 1019, 1091, 72],\n", + " [976, \"16:59\", \"17:53\", 1019, 1073, 54],\n", + " [977, \"17:00\", \"17:16\", 1020, 1036, 16],\n", + " [978, \"17:00\", \"17:10\", 1020, 1030, 10],\n", + " [979, \"17:01\", \"17:13\", 1021, 1033, 12],\n", + " [980, \"17:03\", \"17:50\", 1023, 1070, 47],\n", + " [981, \"17:04\", \"18:30\", 1024, 1110, 86],\n", + " [982, \"17:04\", \"17:45\", 1024, 1065, 41],\n", + " [983, \"17:05\", \"17:24\", 1025, 1044, 19],\n", + " [984, \"17:06\", \"17:51\", 1026, 1071, 45],\n", + " [985, \"17:08\", \"17:55\", 1028, 1075, 47],\n", + " [986, \"17:08\", \"17:55\", 1028, 1075, 47],\n", + " [987, \"17:08\", \"18:34\", 1028, 1114, 86],\n", + " [988, \"17:09\", \"18:03\", 1029, 1083, 54],\n", + " [989, \"17:09\", \"18:21\", 1029, 1101, 72],\n", + " [990, \"17:10\", \"17:20\", 1030, 1040, 10],\n", + " [991, \"17:13\", \"17:40\", 1033, 1060, 27],\n", + " [992, \"17:13\", \"18:00\", 1033, 1080, 47],\n", + " [993, \"17:14\", \"17:55\", 1034, 1075, 41],\n", + " [994, \"17:14\", \"18:40\", 1034, 1120, 86],\n", + " [995, \"17:15\", \"17:34\", 1035, 1054, 19],\n", + " [996, \"17:16\", \"17:28\", 1036, 1048, 12],\n", + " [997, \"17:18\", \"18:05\", 1038, 1085, 47],\n", + " [998, \"17:18\", \"18:05\", 1038, 1085, 47],\n", + " [999, \"17:18\", \"18:44\", 1038, 1124, 86],\n", + " [1000, \"17:19\", \"18:31\", 1039, 1111, 72],\n", + " [1001, \"17:19\", \"18:13\", 1039, 1093, 54],\n", + " [1002, \"17:20\", \"17:36\", 1040, 1056, 16],\n", + " [1003, \"17:20\", \"17:30\", 1040, 1050, 10],\n", + " [1004, \"17:21\", \"17:47\", 1041, 1067, 26],\n", + " [1005, \"17:22\", \"18:06\", 1042, 1086, 44],\n", + " [1006, \"17:23\", \"18:10\", 1043, 1090, 47],\n", + " [1007, \"17:24\", \"18:50\", 1044, 1130, 86],\n", + " [1008, \"17:24\", \"18:05\", 1044, 1085, 41],\n", + " [1009, \"17:25\", \"17:44\", 1045, 1064, 19],\n", + " [1010, \"17:28\", \"17:55\", 1048, 1075, 27],\n", + " [1011, \"17:28\", \"18:15\", 1048, 1095, 47],\n", + " [1012, \"17:28\", \"18:15\", 1048, 1095, 47],\n", + " [1013, \"17:28\", \"18:54\", 1048, 1134, 86],\n", + " [1014, \"17:29\", \"18:41\", 1049, 1121, 72],\n", + " [1015, \"17:29\", \"18:23\", 1049, 1103, 54],\n", + " [1016, \"17:30\", \"17:40\", 1050, 1060, 10],\n", + " [1017, \"17:31\", \"17:43\", 1051, 1063, 12],\n", + " [1018, \"17:33\", \"18:20\", 1053, 1100, 47],\n", + " [1019, \"17:34\", \"18:15\", 1054, 1095, 41],\n", + " [1020, \"17:34\", \"19:00\", 1054, 1140, 86],\n", + " [1021, \"17:35\", \"17:54\", 1055, 1074, 19],\n", + " [1022, \"17:36\", \"18:21\", 1056, 1101, 45],\n", + " [1023, \"17:38\", \"18:25\", 1058, 1105, 47],\n", + " [1024, \"17:38\", \"19:04\", 1058, 1144, 86],\n", + " [1025, \"17:38\", \"18:25\", 1058, 1105, 47],\n", + " [1026, \"17:39\", \"18:51\", 1059, 1131, 72],\n", + " [1027, \"17:39\", \"18:33\", 1059, 1113, 54],\n", + " [1028, \"17:40\", \"17:56\", 1060, 1076, 16],\n", + " [1029, \"17:40\", \"17:50\", 1060, 1070, 10],\n", + " [1030, \"17:43\", \"18:10\", 1063, 1090, 27],\n", + " [1031, \"17:43\", \"18:30\", 1063, 1110, 47],\n", + " [1032, \"17:44\", \"18:25\", 1064, 1105, 41],\n", + " [1033, \"17:44\", \"19:14\", 1064, 1154, 90],\n", + " [1034, \"17:45\", \"18:04\", 1065, 1084, 19],\n", + " [1035, \"17:46\", \"17:58\", 1066, 1078, 12],\n", + " [1036, \"17:48\", \"18:35\", 1068, 1115, 47],\n", + " [1037, \"17:48\", \"18:35\", 1068, 1115, 47],\n", + " [1038, \"17:48\", \"19:14\", 1068, 1154, 86],\n", + " [1039, \"17:49\", \"19:01\", 1069, 1141, 72],\n", + " [1040, \"17:49\", \"18:43\", 1069, 1123, 54],\n", + " [1041, \"17:50\", \"18:00\", 1070, 1080, 10],\n", + " [1042, \"17:51\", \"18:17\", 1071, 1097, 26],\n", + " [1043, \"17:52\", \"18:36\", 1072, 1116, 44],\n", + " [1044, \"17:53\", \"18:40\", 1073, 1120, 47],\n", + " [1045, \"17:54\", \"18:35\", 1074, 1115, 41],\n", + " [1046, \"17:54\", \"18:57\", 1074, 1137, 63],\n", + " [1047, \"17:55\", \"18:14\", 1075, 1094, 19],\n", + " [1048, \"17:58\", \"18:45\", 1078, 1125, 47],\n", + " [1049, \"17:58\", \"18:45\", 1078, 1125, 47],\n", + " [1050, \"17:58\", \"18:25\", 1078, 1105, 27],\n", + " [1051, \"17:58\", \"19:26\", 1078, 1166, 88],\n", + " [1052, \"17:59\", \"18:53\", 1079, 1133, 54],\n", + " [1053, \"18:00\", \"19:11\", 1080, 1151, 71],\n", + " [1054, \"18:00\", \"18:10\", 1080, 1090, 10],\n", + " [1055, \"18:00\", \"18:16\", 1080, 1096, 16],\n", + " [1056, \"18:01\", \"18:13\", 1081, 1093, 12],\n", + " [1057, \"18:03\", \"18:50\", 1083, 1130, 47],\n", + " [1058, \"18:04\", \"18:45\", 1084, 1125, 41],\n", + " [1059, \"18:04\", \"19:29\", 1084, 1169, 85],\n", + " [1060, \"18:05\", \"18:24\", 1085, 1104, 19],\n", + " [1061, \"18:06\", \"18:51\", 1086, 1131, 45],\n", + " [1062, \"18:08\", \"18:55\", 1088, 1135, 47],\n", + " [1063, \"18:08\", \"19:06\", 1088, 1146, 58],\n", + " [1064, \"18:08\", \"18:55\", 1088, 1135, 47],\n", + " [1065, \"18:09\", \"19:03\", 1089, 1143, 54],\n", + " [1066, \"18:10\", \"18:20\", 1090, 1100, 10],\n", + " [1067, \"18:10\", \"19:21\", 1090, 1161, 71],\n", + " [1068, \"18:13\", \"19:00\", 1093, 1140, 47],\n", + " [1069, \"18:13\", \"18:40\", 1093, 1120, 27],\n", + " [1070, \"18:14\", \"19:43\", 1094, 1183, 89],\n", + " [1071, \"18:14\", \"18:55\", 1094, 1135, 41],\n", + " [1072, \"18:15\", \"18:34\", 1095, 1114, 19],\n", + " [1073, \"18:16\", \"18:28\", 1096, 1108, 12],\n", + " [1074, \"18:17\", \"18:27\", 1097, 1107, 10],\n", + " [1075, \"18:18\", \"19:41\", 1098, 1181, 83],\n", + " [1076, \"18:18\", \"18:58\", 1098, 1138, 40],\n", + " [1077, \"18:18\", \"19:05\", 1098, 1145, 47],\n", + " [1078, \"18:19\", \"19:13\", 1099, 1153, 54],\n", + " [1079, \"18:20\", \"19:31\", 1100, 1171, 71],\n", + " [1080, \"18:20\", \"18:36\", 1100, 1116, 16],\n", + " [1081, \"18:20\", \"18:30\", 1100, 1110, 10],\n", + " [1082, \"18:22\", \"19:05\", 1102, 1145, 43],\n", + " [1083, \"18:23\", \"19:05\", 1103, 1145, 42],\n", + " [1084, \"18:24\", \"19:27\", 1104, 1167, 63],\n", + " [1085, \"18:24\", \"19:05\", 1104, 1145, 41],\n", + " [1086, \"18:25\", \"18:44\", 1105, 1124, 19],\n", + " [1087, \"18:28\", \"19:25\", 1108, 1165, 57],\n", + " [1088, \"18:28\", \"18:55\", 1108, 1135, 27],\n", + " [1089, \"18:28\", \"19:08\", 1108, 1148, 40],\n", + " [1090, \"18:28\", \"19:15\", 1108, 1155, 47],\n", + " [1091, \"18:29\", \"19:23\", 1109, 1163, 54],\n", + " [1092, \"18:30\", \"19:05\", 1110, 1145, 35],\n", + " [1093, \"18:30\", \"18:40\", 1110, 1120, 10],\n", + " [1094, \"18:31\", \"18:43\", 1111, 1123, 12],\n", + " [1095, \"18:33\", \"19:15\", 1113, 1155, 42],\n", + " [1096, \"18:34\", \"19:58\", 1114, 1198, 84],\n", + " [1097, \"18:34\", \"19:14\", 1114, 1154, 40],\n", + " [1098, \"18:35\", \"18:55\", 1115, 1135, 20],\n", + " [1099, \"18:36\", \"19:20\", 1116, 1160, 44],\n", + " [1100, \"18:38\", \"19:25\", 1118, 1165, 47],\n", + " [1101, \"18:38\", \"19:23\", 1118, 1163, 45],\n", + " [1102, \"18:38\", \"19:56\", 1118, 1196, 78],\n", + " [1103, \"18:39\", \"19:33\", 1119, 1173, 54],\n", + " [1104, \"18:40\", \"18:50\", 1120, 1130, 10],\n", + " [1105, \"18:40\", \"19:45\", 1120, 1185, 65],\n", + " [1106, \"18:40\", \"18:56\", 1120, 1136, 16],\n", + " [1107, \"18:43\", \"19:10\", 1123, 1150, 27],\n", + " [1108, \"18:43\", \"19:30\", 1123, 1170, 47],\n", + " [1109, \"18:44\", \"19:24\", 1124, 1164, 40],\n", + " [1110, \"18:45\", \"19:05\", 1125, 1145, 20],\n", + " [1111, \"18:46\", \"18:58\", 1126, 1138, 12],\n", + " [1112, \"18:48\", \"19:35\", 1128, 1175, 47],\n", + " [1113, \"18:48\", \"20:12\", 1128, 1212, 84],\n", + " [1114, \"18:48\", \"20:11\", 1128, 1211, 83],\n", + " [1115, \"18:48\", \"19:28\", 1128, 1168, 40],\n", + " [1116, \"18:49\", \"19:43\", 1129, 1183, 54],\n", + " [1117, \"18:50\", \"19:00\", 1130, 1140, 10],\n", + " [1118, \"18:51\", \"19:01\", 1131, 1141, 10],\n", + " [1119, \"18:53\", \"19:35\", 1133, 1175, 42],\n", + " [1120, \"18:53\", \"19:15\", 1133, 1155, 22],\n", + " [1121, \"18:53\", \"20:00\", 1133, 1200, 67],\n", + " [1122, \"18:55\", \"19:15\", 1135, 1155, 20],\n", + " [1123, \"18:55\", \"19:34\", 1135, 1174, 39],\n", + " [1124, \"18:58\", \"19:38\", 1138, 1178, 40],\n", + " [1125, \"18:59\", \"19:53\", 1139, 1193, 54],\n", + " [1126, \"18:59\", \"19:50\", 1139, 1190, 51],\n", + " [1127, \"18:59\", \"19:53\", 1139, 1193, 54],\n", + " [1128, \"19:00\", \"19:16\", 1140, 1156, 16],\n", + " [1129, \"19:00\", \"19:10\", 1140, 1150, 10],\n", + " [1130, \"19:00\", \"19:16\", 1140, 1156, 16],\n", + " [1131, \"19:01\", \"19:13\", 1141, 1153, 12],\n", + " [1132, \"19:03\", \"20:26\", 1143, 1226, 83],\n", + " [1133, \"19:03\", \"19:45\", 1143, 1185, 42],\n", + " [1134, \"19:05\", \"19:44\", 1145, 1184, 39],\n", + " [1135, \"19:05\", \"19:25\", 1145, 1165, 20],\n", + " [1136, \"19:08\", \"20:15\", 1148, 1215, 67],\n", + " [1137, \"19:08\", \"19:35\", 1148, 1175, 27],\n", + " [1138, \"19:09\", \"19:49\", 1149, 1189, 40],\n", + " [1139, \"19:09\", \"20:03\", 1149, 1203, 54],\n", + " [1140, \"19:10\", \"19:20\", 1150, 1160, 10],\n", + " [1141, \"19:10\", \"19:20\", 1150, 1160, 10],\n", + " [1142, \"19:11\", \"19:53\", 1151, 1193, 42],\n", + " [1143, \"19:14\", \"20:26\", 1154, 1226, 72],\n", + " [1144, \"19:14\", \"19:35\", 1154, 1175, 21],\n", + " [1145, \"19:14\", \"19:24\", 1154, 1164, 10],\n", + " [1146, \"19:14\", \"20:05\", 1154, 1205, 51],\n", + " [1147, \"19:15\", \"19:30\", 1155, 1170, 15],\n", + " [1148, \"19:15\", \"19:54\", 1155, 1194, 39],\n", + " [1149, \"19:18\", \"20:39\", 1158, 1239, 81],\n", + " [1150, \"19:18\", \"20:00\", 1158, 1200, 42],\n", + " [1151, \"19:19\", \"20:14\", 1159, 1214, 55],\n", + " [1152, \"19:20\", \"19:30\", 1160, 1170, 10],\n", + " [1153, \"19:20\", \"19:36\", 1160, 1176, 16],\n", + " [1154, \"19:21\", \"19:31\", 1161, 1171, 10],\n", + " [1155, \"19:23\", \"20:30\", 1163, 1230, 67],\n", + " [1156, \"19:23\", \"19:35\", 1163, 1175, 12],\n", + " [1157, \"19:24\", \"19:45\", 1164, 1185, 21],\n", + " [1158, \"19:24\", \"19:45\", 1164, 1185, 21],\n", + " [1159, \"19:25\", \"20:04\", 1165, 1204, 39],\n", + " [1160, \"19:26\", \"20:08\", 1166, 1208, 42],\n", + " [1161, \"19:29\", \"20:02\", 1169, 1202, 33],\n", + " [1162, \"19:29\", \"20:18\", 1169, 1218, 49],\n", + " [1163, \"19:29\", \"20:41\", 1169, 1241, 72],\n", + " [1164, \"19:30\", \"19:40\", 1170, 1180, 10],\n", + " [1165, \"19:33\", \"20:54\", 1173, 1254, 81],\n", + " [1166, \"19:33\", \"20:17\", 1173, 1217, 44],\n", + " [1167, \"19:34\", \"19:55\", 1174, 1195, 21],\n", + " [1168, \"19:35\", \"20:14\", 1175, 1214, 39],\n", + " [1169, \"19:38\", \"20:05\", 1178, 1205, 27],\n", + " [1170, \"19:38\", \"20:45\", 1178, 1245, 67],\n", + " [1171, \"19:39\", \"20:12\", 1179, 1212, 33],\n", + " [1172, \"19:40\", \"19:50\", 1180, 1190, 10],\n", + " [1173, \"19:40\", \"19:56\", 1180, 1196, 16],\n", + " [1174, \"19:41\", \"20:27\", 1181, 1227, 46],\n", + " [1175, \"19:43\", \"19:55\", 1183, 1195, 12],\n", + " [1176, \"19:44\", \"20:05\", 1184, 1205, 21],\n", + " [1177, \"19:44\", \"20:33\", 1184, 1233, 49],\n", + " [1178, \"19:44\", \"21:00\", 1184, 1260, 76],\n", + " [1179, \"19:45\", \"20:24\", 1185, 1224, 39],\n", + " [1180, \"19:48\", \"20:37\", 1188, 1237, 49],\n", + " [1181, \"19:48\", \"21:09\", 1188, 1269, 81],\n", + " [1182, \"19:50\", \"20:00\", 1190, 1200, 10],\n", + " [1183, \"19:52\", \"20:29\", 1192, 1229, 37],\n", + " [1184, \"19:53\", \"20:08\", 1193, 1208, 15],\n", + " [1185, \"19:53\", \"21:02\", 1193, 1262, 69],\n", + " [1186, \"19:53\", \"20:20\", 1193, 1220, 27],\n", + " [1187, \"19:54\", \"20:19\", 1194, 1219, 25],\n", + " [1188, \"19:55\", \"20:34\", 1195, 1234, 39],\n", + " [1189, \"19:56\", \"20:34\", 1196, 1234, 38],\n", + " [1190, \"19:59\", \"20:48\", 1199, 1248, 49],\n", + " [1191, \"19:59\", \"21:20\", 1199, 1280, 81],\n", + " [1192, \"20:00\", \"20:16\", 1200, 1216, 16],\n", + " [1193, \"20:00\", \"20:10\", 1200, 1210, 10],\n", + " [1194, \"20:03\", \"20:42\", 1203, 1242, 39],\n", + " [1195, \"20:03\", \"21:24\", 1203, 1284, 81],\n", + " [1196, \"20:04\", \"20:29\", 1204, 1229, 25],\n", + " [1197, \"20:05\", \"20:48\", 1205, 1248, 43],\n", + " [1198, \"20:07\", \"20:44\", 1207, 1244, 37],\n", + " [1199, \"20:08\", \"20:40\", 1208, 1240, 32],\n", + " [1200, \"20:08\", \"20:35\", 1208, 1235, 27],\n", + " [1201, \"20:10\", \"20:20\", 1210, 1220, 10],\n", + " [1202, \"20:10\", \"20:22\", 1210, 1222, 12],\n", + " [1203, \"20:11\", \"20:47\", 1211, 1247, 36],\n", + " [1204, \"20:14\", \"21:04\", 1214, 1264, 50],\n", + " [1205, \"20:14\", \"21:03\", 1214, 1263, 49],\n", + " [1206, \"20:17\", \"21:03\", 1217, 1263, 46],\n", + " [1207, \"20:18\", \"21:39\", 1218, 1299, 81],\n", + " [1208, \"20:20\", \"20:30\", 1220, 1230, 10],\n", + " [1209, \"20:20\", \"20:57\", 1220, 1257, 37],\n", + " [1210, \"20:20\", \"20:36\", 1220, 1236, 16],\n", + " [1211, \"20:22\", \"20:59\", 1222, 1259, 37],\n", + " [1212, \"20:22\", \"20:42\", 1222, 1242, 20],\n", + " [1213, \"20:24\", \"20:49\", 1224, 1249, 25],\n", + " [1214, \"20:27\", \"21:22\", 1227, 1282, 55],\n", + " [1215, \"20:29\", \"21:18\", 1229, 1278, 49],\n", + " [1216, \"20:30\", \"21:07\", 1230, 1267, 37],\n", + " [1217, \"20:30\", \"20:40\", 1230, 1240, 10],\n", + " [1218, \"20:30\", \"20:40\", 1230, 1240, 10],\n", + " [1219, \"20:30\", \"21:40\", 1230, 1300, 70],\n", + " [1220, \"20:32\", \"21:18\", 1232, 1278, 46],\n", + " [1221, \"20:35\", \"21:54\", 1235, 1314, 79],\n", + " [1222, \"20:37\", \"21:14\", 1237, 1274, 37],\n", + " [1223, \"20:38\", \"21:08\", 1238, 1268, 30],\n", + " [1224, \"20:40\", \"20:50\", 1240, 1250, 10],\n", + " [1225, \"20:40\", \"21:17\", 1240, 1277, 37],\n", + " [1226, \"20:40\", \"20:56\", 1240, 1256, 16],\n", + " [1227, \"20:44\", \"21:33\", 1244, 1293, 49],\n", + " [1228, \"20:47\", \"21:33\", 1247, 1293, 46],\n", + " [1229, \"20:47\", \"21:42\", 1247, 1302, 55],\n", + " [1230, \"20:50\", \"21:00\", 1250, 1260, 10],\n", + " [1231, \"20:50\", \"22:00\", 1250, 1320, 70],\n", + " [1232, \"20:50\", \"22:09\", 1250, 1329, 79],\n", + " [1233, \"20:50\", \"21:27\", 1250, 1287, 37],\n", + " [1234, \"20:52\", \"21:29\", 1252, 1289, 37],\n", + " [1235, \"20:53\", \"21:20\", 1253, 1280, 27],\n", + " [1236, \"20:56\", \"21:11\", 1256, 1271, 15],\n", + " [1237, \"20:59\", \"21:48\", 1259, 1308, 49],\n", + " [1238, \"21:00\", \"21:10\", 1260, 1270, 10],\n", + " [1239, \"21:00\", \"21:37\", 1260, 1297, 37],\n", + " [1240, \"21:02\", \"21:48\", 1262, 1308, 46],\n", + " [1241, \"21:05\", \"22:24\", 1265, 1344, 79],\n", + " [1242, \"21:07\", \"21:44\", 1267, 1304, 37],\n", + " [1243, \"21:07\", \"22:02\", 1267, 1322, 55],\n", + " [1244, \"21:08\", \"21:38\", 1268, 1298, 30],\n", + " [1245, \"21:10\", \"22:25\", 1270, 1345, 75],\n", + " [1246, \"21:10\", \"21:20\", 1270, 1280, 10],\n", + " [1247, \"21:10\", \"21:47\", 1270, 1307, 37],\n", + " [1248, \"21:14\", \"22:03\", 1274, 1323, 49],\n", + " [1249, \"21:17\", \"22:03\", 1277, 1323, 46],\n", + " [1250, \"21:20\", \"22:18\", 1280, 1338, 58],\n", + " [1251, \"21:20\", \"21:57\", 1280, 1317, 37],\n", + " [1252, \"21:20\", \"21:30\", 1280, 1290, 10],\n", + " [1253, \"21:22\", \"21:59\", 1282, 1319, 37],\n", + " [1254, \"21:24\", \"21:49\", 1284, 1309, 25],\n", + " [1255, \"21:27\", \"22:21\", 1287, 1341, 54],\n", + " [1256, \"21:30\", \"22:07\", 1290, 1327, 37],\n", + " [1257, \"21:30\", \"22:20\", 1290, 1340, 50],\n", + " [1258, \"21:30\", \"21:40\", 1290, 1300, 10],\n", + " [1259, \"21:32\", \"22:18\", 1292, 1338, 46],\n", + " [1260, \"21:32\", \"22:01\", 1292, 1321, 29],\n", + " [1261, \"21:35\", \"22:54\", 1295, 1374, 79],\n", + " [1262, \"21:37\", \"22:14\", 1297, 1334, 37],\n", + " [1263, \"21:39\", \"21:55\", 1299, 1315, 16],\n", + " [1264, \"21:40\", \"22:17\", 1300, 1337, 37],\n", + " [1265, \"21:40\", \"21:50\", 1300, 1310, 10],\n", + " [1266, \"21:41\", \"22:08\", 1301, 1328, 27],\n", + " [1267, \"21:47\", \"22:16\", 1307, 1336, 29],\n", + " [1268, \"21:47\", \"22:51\", 1307, 1371, 64],\n", + " [1269, \"21:47\", \"22:33\", 1307, 1353, 46],\n", + " [1270, \"21:48\", \"22:03\", 1308, 1323, 15],\n", + " [1271, \"21:50\", \"22:55\", 1310, 1375, 65],\n", + " [1272, \"21:50\", \"22:27\", 1310, 1347, 37],\n", + " [1273, \"21:50\", \"22:00\", 1310, 1320, 10],\n", + " [1274, \"21:52\", \"22:29\", 1312, 1349, 37],\n", + " [1275, \"21:53\", \"22:19\", 1313, 1339, 26],\n", + " [1276, \"22:00\", \"22:38\", 1320, 1358, 38],\n", + " [1277, \"22:00\", \"22:10\", 1320, 1330, 10],\n", + " [1278, \"22:02\", \"22:12\", 1322, 1332, 10],\n", + " [1279, \"22:02\", \"22:48\", 1322, 1368, 46],\n", + " [1280, \"22:04\", \"22:31\", 1324, 1351, 27],\n", + " [1281, \"22:05\", \"23:24\", 1325, 1404, 79],\n", + " [1282, \"22:07\", \"22:44\", 1327, 1364, 37],\n", + " [1283, \"22:07\", \"22:39\", 1327, 1359, 32],\n", + " [1284, \"22:09\", \"22:25\", 1329, 1345, 16],\n", + " [1285, \"22:10\", \"23:25\", 1330, 1405, 75],\n", + " [1286, \"22:13\", \"22:38\", 1333, 1358, 25],\n", + " [1287, \"22:13\", \"22:53\", 1333, 1373, 40],\n", + " [1288, \"22:17\", \"22:27\", 1337, 1347, 10],\n", + " [1289, \"22:17\", \"23:03\", 1337, 1383, 46],\n", + " [1290, \"22:19\", \"22:46\", 1339, 1366, 27],\n", + " [1291, \"22:22\", \"22:59\", 1342, 1379, 37],\n", + " [1292, \"22:24\", \"22:48\", 1344, 1368, 24],\n", + " [1293, \"22:27\", \"22:52\", 1347, 1372, 25],\n", + " [1294, \"22:27\", \"23:21\", 1347, 1401, 54],\n", + " [1295, \"22:28\", \"23:08\", 1348, 1388, 40],\n", + " [1296, \"22:30\", \"23:17\", 1350, 1397, 47],\n", + " [1297, \"22:32\", \"22:42\", 1352, 1362, 10],\n", + " [1298, \"22:32\", \"23:11\", 1352, 1391, 39],\n", + " [1299, \"22:34\", \"23:01\", 1354, 1381, 27],\n", + " [1300, \"22:35\", \"23:54\", 1355, 1434, 79],\n", + " [1301, \"22:37\", \"23:14\", 1357, 1394, 37],\n", + " [1302, \"22:43\", \"23:23\", 1363, 1403, 40],\n", + " [1303, \"22:43\", \"23:08\", 1363, 1388, 25],\n", + " [1304, \"22:47\", \"23:33\", 1367, 1413, 46],\n", + " [1305, \"22:47\", \"22:57\", 1367, 1377, 10],\n", + " [1306, \"22:49\", \"23:16\", 1369, 1396, 27],\n", + " [1307, \"22:52\", \"23:29\", 1372, 1409, 37],\n", + " [1308, \"22:53\", \"23:15\", 1373, 1395, 22],\n", + " [1309, \"22:55\", \"23:55\", 1375, 1435, 60],\n", + " [1310, \"22:57\", \"23:51\", 1377, 1431, 54],\n", + " [1311, \"22:58\", \"23:38\", 1378, 1418, 40],\n", + " [1312, \"23:02\", \"23:41\", 1382, 1421, 39],\n", + " [1313, \"23:02\", \"23:12\", 1382, 1392, 10],\n", + " [1314, \"23:04\", \"23:31\", 1384, 1411, 27],\n", + " [1315, \"23:05\", \"00:24\", 1385, 1464, 79],\n", + " [1316, \"23:07\", \"23:44\", 1387, 1424, 37],\n", + " [1317, \"23:13\", \"23:53\", 1393, 1433, 40],\n", + " [1318, \"23:13\", \"23:38\", 1393, 1418, 25],\n", + " [1319, \"23:17\", \"00:03\", 1397, 1443, 46],\n", + " [1320, \"23:17\", \"23:27\", 1397, 1407, 10],\n", + " [1321, \"23:19\", \"23:46\", 1399, 1426, 27],\n", + " [1322, \"23:22\", \"23:59\", 1402, 1439, 37],\n", + " [1323, \"23:25\", \"00:25\", 1405, 1465, 60],\n", + " [1324, \"23:27\", \"00:21\", 1407, 1461, 54],\n", + " [1325, \"23:28\", \"00:08\", 1408, 1448, 40],\n", + " [1326, \"23:32\", \"23:42\", 1412, 1422, 10],\n", + " [1327, \"23:34\", \"00:01\", 1414, 1441, 27],\n", + " [1328, \"23:35\", \"01:05\", 1415, 1505, 90],\n", + " [1329, \"23:37\", \"00:09\", 1417, 1449, 32],\n", + " [1330, \"23:43\", \"00:23\", 1423, 1463, 40],\n", + " [1331, \"23:43\", \"00:08\", 1423, 1448, 25],\n", + " [1332, \"23:46\", \"00:01\", 1426, 1441, 15],\n", + " [1333, \"23:47\", \"23:57\", 1427, 1437, 10],\n", + " [1334, \"23:47\", \"00:33\", 1427, 1473, 46],\n", + " [1335, \"23:52\", \"00:24\", 1432, 1464, 32],\n", + " [1336, \"23:55\", \"00:49\", 1435, 1489, 54],\n", + " [1337, \"23:57\", \"00:57\", 1437, 1497, 60],\n", + " [1338, \"23:58\", \"00:38\", 1438, 1478, 40],\n", + " [1339, \"00:02\", \"00:12\", 1442, 1452, 10],\n", + " [1340, \"00:07\", \"00:39\", 1447, 1479, 32],\n", + " [1341, \"00:13\", \"00:38\", 1453, 1478, 25],\n", + " [1342, \"00:13\", \"00:51\", 1453, 1491, 38],\n", + " [1343, \"00:15\", \"01:14\", 1455, 1514, 59],\n", + " [1344, \"00:17\", \"01:23\", 1457, 1523, 66],\n", + " [1345, \"00:23\", \"00:33\", 1463, 1473, 10],\n", + " [1346, \"00:24\", \"00:40\", 1464, 1480, 16],\n", + " [1347, \"00:25\", \"01:12\", 1465, 1512, 47],\n", + " [1348, \"00:28\", \"01:07\", 1468, 1507, 39],\n", + " [1349, \"00:33\", \"01:05\", 1473, 1505, 32],\n", + " [1350, \"00:43\", \"01:21\", 1483, 1521, 38],\n", + " [1351, \"00:44\", \"00:54\", 1484, 1494, 10],\n", + " [1352, \"00:47\", \"01:09\", 1487, 1509, 22],\n", + " [1353, \"00:47\", \"01:26\", 1487, 1526, 39],\n", + " [1354, \"00:54\", \"01:04\", 1494, 1504, 10],\n", + " [1355, \"00:57\", \"01:07\", 1497, 1507, 10],\n", "] # yapf:disable\n", "\n", "\n", @@ -1750,17 +1749,16 @@ "\n", " # Computed data.\n", " total_driving_time = sum(shift[5] for shift in shifts)\n", - " min_num_drivers = int(\n", - " math.ceil(total_driving_time * 1.0 / max_driving_time))\n", + " min_num_drivers = int(math.ceil(total_driving_time * 1.0 / max_driving_time))\n", " min_start_time = min(shift[3] for shift in shifts)\n", " max_end_time = max(shift[4] for shift in shifts)\n", "\n", - " print('Bus driver scheduling')\n", - " print(' num shifts =', num_shifts)\n", - " print(' total driving time =', total_driving_time, 'minutes')\n", - " print(' min num drivers =', min_num_drivers)\n", - " print(' min start time =', min_start_time)\n", - " print(' max end time =', max_end_time)\n", + " print(\"Bus driver scheduling\")\n", + " print(\" num shifts =\", num_shifts)\n", + " print(\" total driving time =\", total_driving_time, \"minutes\")\n", + " print(\" min num drivers =\", min_num_drivers)\n", + " print(\" min start time =\", min_start_time)\n", + " print(\" max end time =\", max_end_time)\n", "\n", " # We are going to build a flow from a the start of the day to the end\n", " # of the day.\n", @@ -1785,11 +1783,11 @@ " # Create all the shift variables before iterating on the transitions\n", " # between these shifts.\n", " for shift in range(num_shifts):\n", - " driving_time[shift] = model.NewIntVar(0, max_driving_time, 'dt_%i' % shift)\n", + " driving_time[shift] = model.NewIntVar(0, max_driving_time, \"dt_%i\" % shift)\n", " no_break_driving_time[shift] = model.NewIntVar(\n", - " 0, max_driving_time_without_pauses, 'nbdt_%i' % shift)\n", - " working_time[shift] = model.NewIntVar(\n", - " 0, max_working_time, 'wt_%i' % shift)\n", + " 0, max_driving_time_without_pauses, \"nbdt_%i\" % shift\n", + " )\n", + " working_time[shift] = model.NewIntVar(0, max_working_time, \"wt_%i\" % shift)\n", "\n", " for shift in range(num_shifts):\n", " duration = shifts[shift][5]\n", @@ -1797,19 +1795,19 @@ " # Arc from source to shift.\n", " # - set the working time of the driver\n", " # - increase driving time and driving time since the last break\n", - " source_lit = model.NewBoolVar('from source to %i' % shift)\n", + " source_lit = model.NewBoolVar(\"from source to %i\" % shift)\n", " all_literals.append(source_lit)\n", " outgoing_source_literals.append(source_lit)\n", " incoming_literals[shift].append(source_lit)\n", " model.Add(driving_time[shift] == duration).OnlyEnforceIf(source_lit)\n", - " model.Add(no_break_driving_time[shift] == duration).OnlyEnforceIf(\n", - " source_lit)\n", + " model.Add(no_break_driving_time[shift] == duration).OnlyEnforceIf(source_lit)\n", " model.Add(working_time[shift] == duration + extra_time).OnlyEnforceIf(\n", - " source_lit)\n", + " source_lit\n", + " )\n", "\n", " # Arc from shift to sink\n", " # - checks that working time is greater than min_working_time\n", - " sink_lit = model.NewBoolVar('from %i to sink' % shift)\n", + " sink_lit = model.NewBoolVar(\"from %i to sink\" % shift)\n", " all_literals.append(sink_lit)\n", " outgoing_literals[shift].append(sink_lit)\n", " incoming_sink_literals.append(sink_lit)\n", @@ -1822,25 +1820,29 @@ " if delay > max_break:\n", " break # Assumes start times are sorted.\n", " other_duration = shifts[other][5]\n", - " lit = model.NewBoolVar('from %i to %i' % (shift, other))\n", + " lit = model.NewBoolVar(\"from %i to %i\" % (shift, other))\n", " all_literals.append(lit)\n", "\n", " # Increase driving time\n", - " model.Add(driving_time[other] ==\n", - " driving_time[shift] + other_duration).OnlyEnforceIf(lit)\n", + " model.Add(\n", + " driving_time[other] == driving_time[shift] + other_duration\n", + " ).OnlyEnforceIf(lit)\n", "\n", " # Increase no_break_driving or reset it to 0 depending on the delay\n", " if delay >= min_pause_after_4h:\n", - " model.Add(no_break_driving_time[other] ==\n", - " other_duration).OnlyEnforceIf(lit)\n", + " model.Add(no_break_driving_time[other] == other_duration).OnlyEnforceIf(\n", + " lit\n", + " )\n", " else:\n", " model.Add(\n", - " no_break_driving_time[other] ==\n", - " no_break_driving_time[shift] + other_duration).OnlyEnforceIf(lit)\n", + " no_break_driving_time[other]\n", + " == no_break_driving_time[shift] + other_duration\n", + " ).OnlyEnforceIf(lit)\n", "\n", " # Increase working time\n", - " model.Add(working_time[other] == working_time[shift] + delay +\n", - " other_duration).OnlyEnforceIf(lit)\n", + " model.Add(\n", + " working_time[other] == working_time[shift] + delay + other_duration\n", + " ).OnlyEnforceIf(lit)\n", "\n", " # Add arc\n", " outgoing_literals[shift].append(lit)\n", @@ -1852,16 +1854,16 @@ " model.Add(sum(incoming_literals[shift]) == 1)\n", "\n", " # Num drivers\n", - " num_drivers = model.NewIntVar(min_num_drivers, min_num_drivers * 3, 'num_drivers')\n", + " num_drivers = model.NewIntVar(min_num_drivers, min_num_drivers * 3, \"num_drivers\")\n", " model.Add(sum(incoming_sink_literals) == num_drivers)\n", " model.Add(sum(outgoing_source_literals) == num_drivers)\n", "\n", - " model.Minimize(num_drivers) \n", + " model.Minimize(num_drivers)\n", "\n", " # Solve model.\n", " solver = cp_model.CpSolver()\n", " solver.parameters.log_search_progress = True\n", - " #solver.parameters.num_search_workers = 16\n", + " # solver.parameters.num_search_workers = 16\n", " # solver.parameters.boolean_encoding_level = 0\n", " # solver.parameters.lns_focus_on_decision_variables = True\n", " status = solver.Solve(model)\n", @@ -1871,13 +1873,13 @@ "\n", " # Display solution\n", " optimal_num_drivers = int(solver.ObjectiveValue())\n", - " print('minimal number of drivers =', optimal_num_drivers)\n", + " print(\"minimal number of drivers =\", optimal_num_drivers)\n", " return optimal_num_drivers\n", "\n", "\n", "def main(args):\n", " \"\"\"Optimize the bus driver allocation in two passes.\"\"\"\n", - " print('----------- first pass: minimize the number of drivers')\n", + " print(\"----------- first pass: minimize the number of drivers\")\n", " shifts = []\n", " if args.instance == 1:\n", " shifts = SAMPLE_SHIFTS_SMALL\n", @@ -1887,8 +1889,8 @@ " shifts = SAMPLE_SHIFTS_LARGE\n", " num_drivers = find_minimum_number_of_drivers(shifts, args.params)\n", "\n", - " print('----------- second pass: minimize the sum of working times')\n", - " #bus_driver_scheduling(False, num_drivers)\n", + " print(\"----------- second pass: minimize the sum of working times\")\n", + " # bus_driver_scheduling(False, num_drivers)\n", "\n", "\n", "main(PARSER.parse_args())\n", diff --git a/examples/notebook/examples/bus_driver_scheduling_sat.ipynb b/examples/notebook/examples/bus_driver_scheduling_sat.ipynb index 8031e8d40e6..bb04ad260cb 100644 --- a/examples/notebook/examples/bus_driver_scheduling_sat.ipynb +++ b/examples/notebook/examples/bus_driver_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/chemical_balance_lp.ipynb b/examples/notebook/examples/chemical_balance_lp.ipynb index 61cf55e97d6..2e0538ecbe9 100644 --- a/examples/notebook/examples/chemical_balance_lp.ipynb +++ b/examples/notebook/examples/chemical_balance_lp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/chemical_balance_sat.ipynb b/examples/notebook/examples/chemical_balance_sat.ipynb index f456b3b679b..4cc19e2a37e 100644 --- a/examples/notebook/examples/chemical_balance_sat.ipynb +++ b/examples/notebook/examples/chemical_balance_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -157,24 +157,24 @@ " # Creates a solver and solves.\n", " solver = cp_model.CpSolver()\n", " status = solver.solve(model)\n", - " print(f\"Status = {solver.status_name(status)}\")\n", - " # The objective value of the solution.\n", - " print(f\"Optimal objective value = {solver.objective_value / 10000.0}\")\n", - "\n", - " for s in all_sets:\n", - " print(\n", - " f\" {chemical_set[s][0]} = {solver.value(set_vars[s]) / 1000.0}\",\n", - " end=\" \",\n", - " )\n", - " print()\n", - " for p in all_products:\n", - " name = max_quantities[p][0]\n", - " max_quantity = max_quantities[p][1]\n", - " quantity = sum(\n", - " solver.value(set_vars[s]) / 1000.0 * chemical_set[s][p + 1]\n", - " for s in all_sets\n", - " )\n", - " print(f\"{name}: {quantity} out of {max_quantity}\")\n", + " if status == cp_model.OPTIMAL:\n", + " # The objective value of the solution.\n", + " print(f\"Optimal objective value = {solver.objective_value / 10000.0}\")\n", + "\n", + " for s in all_sets:\n", + " print(\n", + " f\" {chemical_set[s][0]} = {solver.value(set_vars[s]) / 1000.0}\",\n", + " end=\" \",\n", + " )\n", + " print()\n", + " for p in all_products:\n", + " name = max_quantities[p][0]\n", + " max_quantity = max_quantities[p][1]\n", + " quantity = sum(\n", + " solver.value(set_vars[s]) / 1000.0 * chemical_set[s][p + 1]\n", + " for s in all_sets\n", + " )\n", + " print(f\"{name}: {quantity:.3f} out of {max_quantity}\")\n", "\n", "\n", "def main(argv: Sequence[str]) -> None:\n", diff --git a/examples/notebook/examples/clustering_sat.ipynb b/examples/notebook/examples/clustering_sat.ipynb index d355ed13878..18636b3f4fd 100644 --- a/examples/notebook/examples/clustering_sat.ipynb +++ b/examples/notebook/examples/clustering_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -133,7 +133,7 @@ "]\n", "\n", "\n", - "def clustering_sat():\n", + "def clustering_sat() -> None:\n", " \"\"\"Entry point of the program.\"\"\"\n", " num_nodes = len(distance_matrix)\n", " print(\"Num nodes =\", num_nodes)\n", diff --git a/examples/notebook/examples/cover_rectangle_sat.ipynb b/examples/notebook/examples/cover_rectangle_sat.ipynb index 2893b2a8668..e98d7890b5a 100644 --- a/examples/notebook/examples/cover_rectangle_sat.ipynb +++ b/examples/notebook/examples/cover_rectangle_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/cryptarithm_sat.ipynb b/examples/notebook/examples/cryptarithm_sat.ipynb index c217a0c4106..a4488987670 100644 --- a/examples/notebook/examples/cryptarithm_sat.ipynb +++ b/examples/notebook/examples/cryptarithm_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -87,7 +87,7 @@ "from ortools.sat.python import cp_model\n", "\n", "\n", - "def send_more_money():\n", + "def send_more_money() -> None:\n", " \"\"\"solve the cryptarithmic puzzle SEND+MORE=MONEY.\"\"\"\n", " model = cp_model.CpModel()\n", "\n", @@ -142,7 +142,7 @@ " print(\"y:\", solver.value(y))\n", "\n", "\n", - "def main(_):\n", + "def main(_) -> None:\n", " send_more_money()\n", "\n", "\n", diff --git a/examples/notebook/examples/cvrptw_plot.ipynb b/examples/notebook/examples/cvrptw_plot.ipynb index efeb0b0eb82..e234ce229cf 100644 --- a/examples/notebook/examples/cvrptw_plot.ipynb +++ b/examples/notebook/examples/cvrptw_plot.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/flexible_job_shop_sat.ipynb b/examples/notebook/examples/flexible_job_shop_sat.ipynb index 5388b1facb2..d76f41b56e4 100644 --- a/examples/notebook/examples/flexible_job_shop_sat.ipynb +++ b/examples/notebook/examples/flexible_job_shop_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -101,20 +101,20 @@ "class SolutionPrinter(cp_model.CpSolverSolutionCallback):\n", " \"\"\"Print intermediate solutions.\"\"\"\n", "\n", - " def __init__(self):\n", + " def __init__(self) -> None:\n", " cp_model.CpSolverSolutionCallback.__init__(self)\n", " self.__solution_count = 0\n", "\n", - " def on_solution_callback(self):\n", + " def on_solution_callback(self) -> None:\n", " \"\"\"Called at each new solution.\"\"\"\n", " print(\n", - " \"Solution %i, time = %f s, objective = %i\"\n", - " % (self.__solution_count, self.wall_time, self.objective_value)\n", + " f\"Solution {self.__solution_count}, time = {self.wall_time} s,\"\n", + " f\" objective = {self.objective_value}\"\n", " )\n", " self.__solution_count += 1\n", "\n", "\n", - "def flexible_jobshop():\n", + "def flexible_jobshop() -> None:\n", " \"\"\"solve a small flexible jobshop problem.\"\"\"\n", " # Data part.\n", " jobs = [ # task = (processing_time, machine_id)\n", @@ -152,13 +152,13 @@ " max_task_duration = max(max_task_duration, alternative[0])\n", " horizon += max_task_duration\n", "\n", - " print(\"Horizon = %i\" % horizon)\n", + " print(f\"Horizon = {horizon}\")\n", "\n", " # Global storage of variables.\n", " intervals_per_resources = collections.defaultdict(list)\n", " starts = {} # indexed by (job_id, task_id).\n", " presences = {} # indexed by (job_id, task_id, alt_id).\n", - " job_ends = []\n", + " job_ends: list[cp_model.IntVar] = []\n", "\n", " # Scan the jobs and create the relevant variables and intervals.\n", " for job_id in all_jobs:\n", @@ -180,7 +180,7 @@ " max_duration = max(max_duration, alt_duration)\n", "\n", " # Create main interval for the task.\n", - " suffix_name = \"_j%i_t%i\" % (job_id, task_id)\n", + " suffix_name = f\"_j{job_id}_t{task_id}\"\n", " start = model.new_int_var(0, horizon, \"start\" + suffix_name)\n", " duration = model.new_int_var(\n", " min_duration, max_duration, \"duration\" + suffix_name\n", @@ -202,7 +202,7 @@ " if num_alternatives > 1:\n", " l_presences = []\n", " for alt_id in all_alternatives:\n", - " alt_suffix = \"_j%i_t%i_a%i\" % (job_id, task_id, alt_id)\n", + " alt_suffix = f\"_j{job_id}_t{task_id}_a{alt_id}\"\n", " l_presence = model.new_bool_var(\"presence\" + alt_suffix)\n", " l_start = model.new_int_var(0, horizon, \"start\" + alt_suffix)\n", " l_duration = task[alt_id][0]\n", @@ -229,7 +229,8 @@ " intervals_per_resources[task[0][1]].append(interval)\n", " presences[(job_id, task_id, 0)] = model.new_constant(1)\n", "\n", - " job_ends.append(previous_end)\n", + " if previous_end is not None:\n", + " job_ends.append(previous_end)\n", "\n", " # Create machines constraints.\n", " for machine_id in all_machines:\n", @@ -248,29 +249,25 @@ " status = solver.solve(model, solution_printer)\n", "\n", " # Print final solution.\n", - " for job_id in all_jobs:\n", - " print(\"Job %i:\" % job_id)\n", - " for task_id in range(len(jobs[job_id])):\n", - " start_value = solver.value(starts[(job_id, task_id)])\n", - " machine = -1\n", - " duration = -1\n", - " selected = -1\n", - " for alt_id in range(len(jobs[job_id][task_id])):\n", - " if solver.value(presences[(job_id, task_id, alt_id)]):\n", - " duration = jobs[job_id][task_id][alt_id][0]\n", - " machine = jobs[job_id][task_id][alt_id][1]\n", - " selected = alt_id\n", - " print(\n", - " \" task_%i_%i starts at %i (alt %i, machine %i, duration %i)\"\n", - " % (job_id, task_id, start_value, selected, machine, duration)\n", - " )\n", - "\n", - " print(\"solve status: %s\" % solver.status_name(status))\n", - " print(\"Optimal objective value: %i\" % solver.objective_value)\n", - " print(\"Statistics\")\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", + " print(f\"Optimal objective value: {solver.objective_value}\")\n", + " for job_id in all_jobs:\n", + " print(f\"Job {job_id}\")\n", + " for task_id, task in enumerate(jobs[job_id]):\n", + " start_value = solver.value(starts[(job_id, task_id)])\n", + " machine: int = -1\n", + " task_duration: int = -1\n", + " selected: int = -1\n", + " for alt_id, alt in enumerate(task):\n", + " if solver.boolean_value(presences[(job_id, task_id, alt_id)]):\n", + " task_duration, machine = alt\n", + " selected = alt_id\n", + " print(\n", + " f\" task_{job_id}_{task_id} starts at {start_value} (alt\"\n", + " f\" {selected}, machine {machine}, duration {task_duration})\"\n", + " )\n", + "\n", + " print(solver.response_stats())\n", "\n", "\n", "flexible_jobshop()\n", diff --git a/examples/notebook/examples/gate_scheduling_sat.ipynb b/examples/notebook/examples/gate_scheduling_sat.ipynb index 44792eab750..985b8f9c6ba 100644 --- a/examples/notebook/examples/gate_scheduling_sat.ipynb +++ b/examples/notebook/examples/gate_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -96,7 +96,7 @@ "from ortools.sat.python import cp_model\n", "\n", "\n", - "def main(_):\n", + "def main(_) -> None:\n", " \"\"\"Solves the gate scheduling problem.\"\"\"\n", " model = cp_model.CpModel()\n", "\n", @@ -134,34 +134,34 @@ "\n", " for i in all_jobs:\n", " # Create main interval.\n", - " start = model.new_int_var(0, horizon, \"start_%i\" % i)\n", + " start = model.new_int_var(0, horizon, f\"start_{i}\")\n", " duration = jobs[i][0]\n", - " end = model.new_int_var(0, horizon, \"end_%i\" % i)\n", - " interval = model.new_interval_var(start, duration, end, \"interval_%i\" % i)\n", + " end = model.new_int_var(0, horizon, f\"end_{i}\")\n", + " interval = model.new_interval_var(start, duration, end, f\"interval_{i}\")\n", " starts.append(start)\n", " intervals.append(interval)\n", " ends.append(end)\n", " demands.append(jobs[i][1])\n", "\n", " # Create an optional copy of interval to be executed on machine 0.\n", - " performed_on_m0 = model.new_bool_var(\"perform_%i_on_m0\" % i)\n", + " performed_on_m0 = model.new_bool_var(f\"perform_{i}_on_m0\")\n", " performed.append(performed_on_m0)\n", - " start0 = model.new_int_var(0, horizon, \"start_%i_on_m0\" % i)\n", - " end0 = model.new_int_var(0, horizon, \"end_%i_on_m0\" % i)\n", + " start0 = model.new_int_var(0, horizon, f\"start_{i}_on_m0\")\n", + " end0 = model.new_int_var(0, horizon, f\"end_{i}_on_m0\")\n", " interval0 = model.new_optional_interval_var(\n", - " start0, duration, end0, performed_on_m0, \"interval_%i_on_m0\" % i\n", + " start0, duration, end0, performed_on_m0, f\"interval_{i}_on_m0\"\n", " )\n", " intervals0.append(interval0)\n", "\n", " # Create an optional copy of interval to be executed on machine 1.\n", - " start1 = model.new_int_var(0, horizon, \"start_%i_on_m1\" % i)\n", - " end1 = model.new_int_var(0, horizon, \"end_%i_on_m1\" % i)\n", + " start1 = model.new_int_var(0, horizon, f\"start_{i}_on_m1\")\n", + " end1 = model.new_int_var(0, horizon, f\"end_{i}_on_m1\")\n", " interval1 = model.new_optional_interval_var(\n", " start1,\n", " duration,\n", " end1,\n", " ~performed_on_m0,\n", - " \"interval_%i_on_m1\" % i,\n", + " f\"interval_{i}_on_m1\",\n", " )\n", " intervals1.append(interval1)\n", "\n", @@ -191,18 +191,24 @@ " # Output solution.\n", " if visualization.RunFromIPython():\n", " output = visualization.SvgWrapper(solver.objective_value, max_width, 40.0)\n", - " output.AddTitle(\"Makespan = %i\" % solver.objective_value)\n", + " output.AddTitle(f\"Makespan = {solver.objective_value}\")\n", " color_manager = visualization.ColorManager()\n", " color_manager.SeedRandomColor(0)\n", "\n", " for i in all_jobs:\n", " performed_machine = 1 - solver.value(performed[i])\n", - " start = solver.value(starts[i])\n", + " start_of_task = solver.value(starts[i])\n", " d_x = jobs[i][0]\n", " d_y = jobs[i][1]\n", " s_y = performed_machine * (max_width - d_y)\n", " output.AddRectangle(\n", - " start, s_y, d_x, d_y, color_manager.RandomColor(), \"black\", \"j%i\" % i\n", + " start_of_task,\n", + " s_y,\n", + " d_x,\n", + " d_y,\n", + " color_manager.RandomColor(),\n", + " \"black\",\n", + " f\"j{i}\",\n", " )\n", "\n", " output.AddXScale()\n", @@ -210,17 +216,15 @@ " output.Display()\n", " else:\n", " print(\"Solution\")\n", - " print(\" - makespan = %i\" % solver.objective_value)\n", + " print(f\" - makespan = {solver.objective_value}\")\n", " for i in all_jobs:\n", " performed_machine = 1 - solver.value(performed[i])\n", - " start = solver.value(starts[i])\n", + " start_of_task = solver.value(starts[i])\n", " print(\n", - " \" - Job %i starts at %i on machine %i\" % (i, start, performed_machine)\n", + " f\" - Job {i} starts at {start_of_task} on machine\"\n", + " f\" {performed_machine}\"\n", " )\n", - " print(\"Statistics\")\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " print(solver.response_stats())\n", "\n", "\n", "main()\n", diff --git a/examples/notebook/examples/golomb8.ipynb b/examples/notebook/examples/golomb8.ipynb index 6d05000633f..0414ee36c55 100644 --- a/examples/notebook/examples/golomb8.ipynb +++ b/examples/notebook/examples/golomb8.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -91,17 +91,14 @@ "metadata": {}, "outputs": [], "source": [ - "from ortools.sat.colab import flags\n", "from ortools.constraint_solver import pywrapcp\n", "\n", - "FLAGS = flags.FLAGS\n", - "\n", "# We disable the following warning because it is a false positive on constraints\n", "# like: solver.Add(x == 0)\n", "# pylint: disable=g-explicit-bool-comparison\n", "\n", "\n", - "def main(_):\n", + "def main(_) -> None:\n", " # Create the solver.\n", " solver = pywrapcp.Solver(\"golomb ruler\")\n", "\n", diff --git a/examples/notebook/examples/golomb_sat.ipynb b/examples/notebook/examples/golomb_sat.ipynb index d9d6c36f6c6..cdd120961fa 100644 --- a/examples/notebook/examples/golomb_sat.ipynb +++ b/examples/notebook/examples/golomb_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -105,7 +105,7 @@ ")\n", "\n", "\n", - "def solve_golomb_ruler(order: int, params: str):\n", + "def solve_golomb_ruler(order: int, params: str) -> None:\n", " \"\"\"Solve the Golomb ruler problem.\"\"\"\n", " # Create the model.\n", " model = cp_model.CpModel()\n", @@ -143,18 +143,13 @@ " status = solver.solve(model, solution_printer)\n", "\n", " # Print solution.\n", - " print(f\"status: {solver.status_name(status)}\")\n", " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", " for idx, var in enumerate(marks):\n", " print(f\"mark[{idx}]: {solver.value(var)}\")\n", " intervals = [solver.value(diff) for diff in diffs]\n", " intervals.sort()\n", " print(f\"intervals: {intervals}\")\n", - "\n", - " print(\"Statistics:\")\n", - " print(f\"- conflicts: {solver.num_conflicts}\")\n", - " print(f\"- branches : {solver.num_branches}\")\n", - " print(f\"- wall time: {solver.wall_time}s\\n\")\n", + " print(solver.response_stats())\n", "\n", "\n", "def main(argv: Sequence[str]) -> None:\n", diff --git a/examples/notebook/examples/hidato_sat.ipynb b/examples/notebook/examples/hidato_sat.ipynb index 70434bacb58..49640e66c3c 100644 --- a/examples/notebook/examples/hidato_sat.ipynb +++ b/examples/notebook/examples/hidato_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -142,7 +142,7 @@ " if game[i][j] == 0:\n", " line += \" .\"\n", " else:\n", - " line += \"% 3s\" % game[i][j]\n", + " line += f\"{game[i][j]:3}\"\n", " print(line)\n", "\n", "\n", @@ -170,7 +170,7 @@ "\n", " elif problem == 3:\n", " # Problems from the book:\n", - " # Gyora Bededek: \"Hidato: 2000 Pure Logic Puzzles\"\n", + " # Gyora Bededek: 'Hidato: 2000 Pure Logic Puzzles'\n", " # Problem 1 (Practice)\n", " puzzle = [\n", " [0, 0, 20, 0, 0],\n", @@ -215,7 +215,7 @@ " return puzzle\n", "\n", "\n", - "def solve_hidato(puzzle: list[list[int]], index: int):\n", + "def solve_hidato(puzzle: list[list[int]], index: int) -> None:\n", " \"\"\"solve the given hidato table.\"\"\"\n", " # Create the model.\n", " model = cp_model.CpModel()\n", @@ -224,15 +224,15 @@ " c = len(puzzle[0])\n", " if not visualization.RunFromIPython():\n", " print(\"\")\n", - " print(\"----- Solving problem %i -----\" % index)\n", + " print(f\"----- Solving problem {index} -----\")\n", " print(\"\")\n", - " print((\"Initial game (%i x %i)\" % (r, c)))\n", + " print(f\"Initial game ({r} x {c})\")\n", " print_matrix(puzzle)\n", "\n", " #\n", " # Declare variables.\n", " #\n", - " positions = [model.new_int_var(0, r * c - 1, \"p[%i]\" % i) for i in range(r * c)]\n", + " positions = [model.new_int_var(0, r * c - 1, f\"p[{i}]\") for i in range(r * c)]\n", "\n", " #\n", " # Constraints.\n", @@ -270,7 +270,7 @@ " color = \"white\" if puzzle[y][x] == 0 else \"lightgreen\"\n", " output.AddRectangle(x, r - y - 1, 1, 1, color, \"black\", str(i + 1))\n", "\n", - " output.AddTitle(\"Puzzle %i solved in %f s\" % (index, solver.wall_time))\n", + " output.AddTitle(f\"Puzzle {index} solved in {solver.wall_time:.2f} s\")\n", " output.Display()\n", " else:\n", " print_solution(\n", @@ -278,10 +278,7 @@ " r,\n", " c,\n", " )\n", - " print(\"Statistics\")\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " print(solver.response_stats())\n", "\n", "\n", "def main(_):\n", diff --git a/examples/notebook/examples/integer_programming.ipynb b/examples/notebook/examples/integer_programming.ipynb index c8519d2a824..3dd97a4aae9 100644 --- a/examples/notebook/examples/integer_programming.ipynb +++ b/examples/notebook/examples/integer_programming.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/jobshop_ft06_distance_sat.ipynb b/examples/notebook/examples/jobshop_ft06_distance_sat.ipynb index 277650be7f8..dc5ee45b5b8 100644 --- a/examples/notebook/examples/jobshop_ft06_distance_sat.ipynb +++ b/examples/notebook/examples/jobshop_ft06_distance_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -105,7 +105,7 @@ " return abs(x - y)\n", "\n", "\n", - "def jobshop_ft06_distance():\n", + "def jobshop_ft06_distance() -> None:\n", " \"\"\"Solves the ft06 jobshop with distances between tasks.\"\"\"\n", " # Creates the model.\n", " model = cp_model.CpModel()\n", @@ -142,11 +142,11 @@ " all_tasks = {}\n", " for i in all_jobs:\n", " for j in all_machines:\n", - " start_var = model.new_int_var(0, horizon, \"start_%i_%i\" % (i, j))\n", + " start_var = model.new_int_var(0, horizon, f\"start_{i}_{j}\")\n", " duration = durations[i][j]\n", - " end_var = model.new_int_var(0, horizon, \"end_%i_%i\" % (i, j))\n", + " end_var = model.new_int_var(0, horizon, f\"end_{i}_{j}\")\n", " interval_var = model.new_interval_var(\n", - " start_var, duration, end_var, \"interval_%i_%i\" % (i, j)\n", + " start_var, duration, end_var, f\"interval_{i}_{j}\"\n", " )\n", " all_tasks[(i, j)] = task_type(\n", " start=start_var, end=end_var, interval=interval_var\n", @@ -170,16 +170,16 @@ " arcs = []\n", " for j1 in range(len(job_intervals)):\n", " # Initial arc from the dummy node (0) to a task.\n", - " start_lit = model.new_bool_var(\"%i is first job\" % j1)\n", + " start_lit = model.new_bool_var(f\"{j1} is first job\")\n", " arcs.append((0, j1 + 1, start_lit))\n", " # Final arc from an arc to the dummy node.\n", - " arcs.append((j1 + 1, 0, model.new_bool_var(\"%i is last job\" % j1)))\n", + " arcs.append((j1 + 1, 0, model.new_bool_var(f\"{j1} is last job\")))\n", "\n", " for j2 in range(len(job_intervals)):\n", " if j1 == j2:\n", " continue\n", "\n", - " lit = model.new_bool_var(\"%i follows %i\" % (j2, j1))\n", + " lit = model.new_bool_var(f\"{j2} follows {j1}\")\n", " arcs.append((j1 + 1, j2 + 1, lit))\n", "\n", " # We add the reified precedence to link the literal with the\n", @@ -209,7 +209,8 @@ "\n", " # Output solution.\n", " if status == cp_model.OPTIMAL:\n", - " print(\"Optimal makespan: %i\" % solver.objective_value)\n", + " print(f\"Optimal makespan: {solver.objective_value}\")\n", + " print(solver.response_stats())\n", "\n", "\n", "jobshop_ft06_distance()\n", diff --git a/examples/notebook/examples/jobshop_ft06_sat.ipynb b/examples/notebook/examples/jobshop_ft06_sat.ipynb index ae6fc8fa35a..792fd66ff5f 100644 --- a/examples/notebook/examples/jobshop_ft06_sat.ipynb +++ b/examples/notebook/examples/jobshop_ft06_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -135,11 +135,11 @@ " all_tasks = {}\n", " for i in all_jobs:\n", " for j in all_machines:\n", - " start_var = model.new_int_var(0, horizon, \"start_%i_%i\" % (i, j))\n", + " start_var = model.new_int_var(0, horizon, f\"start_{i}_{j}\")\n", " duration = durations[i][j]\n", - " end_var = model.new_int_var(0, horizon, \"end_%i_%i\" % (i, j))\n", + " end_var = model.new_int_var(0, horizon, f\"end_{i}_{j}\")\n", " interval_var = model.new_interval_var(\n", - " start_var, duration, end_var, \"interval_%i_%i\" % (i, j)\n", + " start_var, duration, end_var, f\"interval_{i}_{j}\"\n", " )\n", " all_tasks[(i, j)] = task_type(\n", " start=start_var, end=end_var, interval=interval_var\n", @@ -182,7 +182,7 @@ " ]\n", " visualization.DisplayJobshop(starts, durations, machines, \"FT06\")\n", " else:\n", - " print(\"Optimal makespan: %i\" % solver.objective_value)\n", + " print(f\"Optimal makespan: {solver.objective_value}\")\n", "\n", "\n", "jobshop_ft06()\n", diff --git a/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb b/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb index 7410dc9bbf3..a40167ca522 100644 --- a/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb +++ b/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -91,20 +91,20 @@ "class SolutionPrinter(cp_model.CpSolverSolutionCallback):\n", " \"\"\"Print intermediate solutions.\"\"\"\n", "\n", - " def __init__(self):\n", + " def __init__(self) -> None:\n", " cp_model.CpSolverSolutionCallback.__init__(self)\n", " self.__solution_count = 0\n", "\n", - " def on_solution_callback(self):\n", + " def on_solution_callback(self) -> None:\n", " \"\"\"Called at each new solution.\"\"\"\n", " print(\n", - " \"Solution %i, time = %f s, objective = %i\"\n", - " % (self.__solution_count, self.wall_time, self.objective_value)\n", + " f\"Solution {self.__solution_count}, time = {self.wall_time} s,\"\n", + " f\" objective = {self.objective_value}\"\n", " )\n", " self.__solution_count += 1\n", "\n", "\n", - "def jobshop_with_maintenance():\n", + "def jobshop_with_maintenance() -> None:\n", " \"\"\"Solves a jobshop with maintenance on one machine.\"\"\"\n", " # Create the model.\n", " model = cp_model.CpModel()\n", @@ -122,7 +122,7 @@ " horizon = sum(task[1] for job in jobs_data for task in job)\n", "\n", " # Named tuple to store information about created variables.\n", - " task_type = collections.namedtuple(\"Task\", \"start end interval\")\n", + " task_type = collections.namedtuple(\"task_type\", \"start end interval\")\n", " # Named tuple to manipulate solution information.\n", " assigned_task_type = collections.namedtuple(\n", " \"assigned_task_type\", \"start job index duration\"\n", @@ -135,9 +135,8 @@ " for job_id, job in enumerate(jobs_data):\n", " for entry in enumerate(job):\n", " task_id, task = entry\n", - " machine = task[0]\n", - " duration = task[1]\n", - " suffix = \"_%i_%i\" % (job_id, task_id)\n", + " machine, duration = task\n", + " suffix = f\"_{job_id}_{task_id}\"\n", " start_var = model.new_int_var(0, horizon, \"start\" + suffix)\n", " end_var = model.new_int_var(0, horizon, \"end\" + suffix)\n", " interval_var = model.new_interval_var(\n", @@ -200,15 +199,15 @@ " sol_line = \" \"\n", "\n", " for assigned_task in assigned_jobs[machine]:\n", - " name = \"job_%i_%i\" % (assigned_task.job, assigned_task.index)\n", + " name = f\"job_{assigned_task.job}_{assigned_task.index}\"\n", " # add spaces to output to align columns.\n", - " sol_line_tasks += \"%-10s\" % name\n", + " sol_line_tasks += f\"{name:>10}\"\n", " start = assigned_task.start\n", " duration = assigned_task.duration\n", "\n", - " sol_tmp = \"[%i,%i]\" % (start, start + duration)\n", + " sol_tmp = f\"[{start}, {start + duration}]\"\n", " # add spaces to output to align columns.\n", - " sol_line += \"%-10s\" % sol_tmp\n", + " sol_line += f\"{sol_tmp:>10}\"\n", "\n", " sol_line += \"\\n\"\n", " sol_line_tasks += \"\\n\"\n", @@ -216,12 +215,9 @@ " output += sol_line\n", "\n", " # Finally print the solution found.\n", - " print(\"Optimal Schedule Length: %i\" % solver.objective_value)\n", + " print(f\"Optimal Schedule Length: {solver.objective_value}\")\n", " print(output)\n", - " print(\"Statistics\")\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " print(solver.response_stats())\n", "\n", "\n", "def main(argv: Sequence[str]) -> None:\n", diff --git a/examples/notebook/examples/knapsack_2d_sat.ipynb b/examples/notebook/examples/knapsack_2d_sat.ipynb index b05a748ef8e..09efe314988 100644 --- a/examples/notebook/examples/knapsack_2d_sat.ipynb +++ b/examples/notebook/examples/knapsack_2d_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -139,7 +139,9 @@ " return (data, max_height, max_width)\n", "\n", "\n", - "def solve_with_duplicate_items(data: pd.Series, max_height: int, max_width: int):\n", + "def solve_with_duplicate_items(\n", + " data: pd.Series, max_height: int, max_width: int\n", + ") -> None:\n", " \"\"\"solve the problem by building 2 items (rotated or not) for each item.\"\"\"\n", " # Derived data (expanded to individual items).\n", " data_widths = data[\"width\"].to_numpy()\n", @@ -230,7 +232,7 @@ " status = solver.solve(model)\n", "\n", " # Report solution.\n", - " if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n", + " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", " used = {i for i in range(num_items) if solver.boolean_value(is_used[i])}\n", " data = pd.DataFrame(\n", " {\n", @@ -332,7 +334,7 @@ " status = solver.solve(model)\n", "\n", " # Report solution.\n", - " if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n", + " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", " used = {i for i in range(num_items) if solver.boolean_value(is_used[i])}\n", " data = pd.DataFrame(\n", " {\n", @@ -453,7 +455,7 @@ " status = solver.solve(model)\n", "\n", " # Report solution.\n", - " if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n", + " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", " used = {i for i in range(num_items) if solver.boolean_value(is_used[i])}\n", " data = pd.DataFrame(\n", " {\n", diff --git a/examples/notebook/examples/line_balancing_sat.ipynb b/examples/notebook/examples/line_balancing_sat.ipynb index 07adcde4c2a..07c2c82ab93 100644 --- a/examples/notebook/examples/line_balancing_sat.ipynb +++ b/examples/notebook/examples/line_balancing_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -98,7 +98,7 @@ "source": [ "import collections\n", "import re\n", - "from typing import Sequence\n", + "from typing import Dict, Sequence\n", "\n", "from ortools.sat.colab import flags\n", "from google.protobuf import text_format\n", @@ -115,8 +115,8 @@ ")\n", "\n", "\n", - "class SectionInfo(object):\n", - " \"\"\"Store model information for each section of the input file.\"\"\"\n", + "class SectionInfo:\n", + " \"\"\"Store problem information for each section of the input file.\"\"\"\n", "\n", " def __init__(self):\n", " self.value = None\n", @@ -134,44 +134,43 @@ " return \"SectionInfo()\"\n", "\n", "\n", - "def read_model(filename):\n", - " \"\"\"Reads a .alb file and returns the model.\"\"\"\n", + "def read_problem(filename: str) -> Dict[str, SectionInfo]:\n", + " \"\"\"Reads a .alb file and returns the problem.\"\"\"\n", "\n", " current_info = SectionInfo()\n", "\n", - " model = {}\n", + " problem: Dict[str, SectionInfo] = {}\n", " with open(filename, \"r\") as input_file:\n", - " print(f\"Reading model from '{filename}'\")\n", - " section_name = \"\"\n", + " print(f\"Reading problem from '{filename}'\")\n", "\n", " for line in input_file:\n", " stripped_line = line.strip()\n", " if not stripped_line:\n", " continue\n", "\n", - " match_section_def = re.match(r\"<([\\w\\s]+)>\", stripped_line)\n", + " match_section_def = re.fullmatch(r\"<([\\w\\s]+)>\", stripped_line)\n", " if match_section_def:\n", " section_name = match_section_def.group(1)\n", " if section_name == \"end\":\n", " continue\n", "\n", " current_info = SectionInfo()\n", - " model[section_name] = current_info\n", + " problem[section_name] = current_info\n", " continue\n", "\n", - " match_single_number = re.match(r\"^([0-9]+)$\", stripped_line)\n", + " match_single_number = re.fullmatch(r\"^([0-9]+)$\", stripped_line)\n", " if match_single_number:\n", " current_info.value = int(match_single_number.group(1))\n", " continue\n", "\n", - " match_key_value = re.match(r\"^([0-9]+)\\s+([0-9]+)$\", stripped_line)\n", + " match_key_value = re.fullmatch(r\"^([0-9]+)\\s+([0-9]+)$\", stripped_line)\n", " if match_key_value:\n", " key = int(match_key_value.group(1))\n", " value = int(match_key_value.group(2))\n", " current_info.index_map[key] = value\n", " continue\n", "\n", - " match_pair = re.match(r\"^([0-9]+),([0-9]+)$\", stripped_line)\n", + " match_pair = re.fullmatch(r\"^([0-9]+),([0-9]+)$\", stripped_line)\n", " if match_pair:\n", " left = int(match_pair.group(1))\n", " right = int(match_pair.group(2))\n", @@ -180,24 +179,26 @@ "\n", " print(f\"Unrecognized line '{stripped_line}'\")\n", "\n", - " return model\n", + " return problem\n", "\n", "\n", - "def print_stats(model):\n", - " print(\"Model Statistics\")\n", - " for key, value in model.items():\n", + "def print_stats(problem: Dict[str, SectionInfo]) -> None:\n", + " print(\"Problem Statistics\")\n", + " for key, value in problem.items():\n", " print(f\" - {key}: {value}\")\n", "\n", "\n", - "def solve_model_greedily(model):\n", + "def solve_problem_greedily(problem: Dict[str, SectionInfo]) -> Dict[int, int]:\n", " \"\"\"Compute a greedy solution.\"\"\"\n", " print(\"Solving using a Greedy heuristics\")\n", "\n", - " num_tasks = model[\"number of tasks\"].value\n", + " num_tasks = problem[\"number of tasks\"].value\n", + " if num_tasks is None:\n", + " return {}\n", " all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the data.\n", - " precedences = model[\"precedence relations\"].set_of_pairs\n", - " durations = model[\"task times\"].index_map\n", - " cycle_time = model[\"cycle time\"].value\n", + " precedences = problem[\"precedence relations\"].set_of_pairs\n", + " durations = problem[\"task times\"].index_map\n", + " cycle_time = problem[\"cycle time\"].value\n", "\n", " weights = collections.defaultdict(int)\n", " successors = collections.defaultdict(list)\n", @@ -210,7 +211,7 @@ " if after in candidates:\n", " candidates.remove(after)\n", "\n", - " assignment = {}\n", + " assignment: Dict[int, int] = {}\n", " current_pod = 0\n", " residual_capacity = cycle_time\n", "\n", @@ -251,16 +252,20 @@ " return assignment\n", "\n", "\n", - "def solve_boolean_model(model, hint):\n", - " \"\"\"solve the given model.\"\"\"\n", + "def solve_problem_with_boolean_model(\n", + " problem: Dict[str, SectionInfo], hint: Dict[int, int]\n", + ") -> None:\n", + " \"\"\"solve the given problem.\"\"\"\n", "\n", " print(\"Solving using the Boolean model\")\n", - " # Model data\n", - " num_tasks = model[\"number of tasks\"].value\n", - " all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the model.\n", - " durations = model[\"task times\"].index_map\n", - " precedences = model[\"precedence relations\"].set_of_pairs\n", - " cycle_time = model[\"cycle time\"].value\n", + " # problem data\n", + " num_tasks = problem[\"number of tasks\"].value\n", + " if num_tasks is None:\n", + " return\n", + " all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the problem.\n", + " durations = problem[\"task times\"].index_map\n", + " precedences = problem[\"precedence relations\"].set_of_pairs\n", + " cycle_time = problem[\"cycle time\"].value\n", "\n", " num_pods = max(p for _, p in hint.items()) + 1 if hint else num_tasks - 1\n", " all_pods = range(num_pods)\n", @@ -340,16 +345,20 @@ " solver.solve(model)\n", "\n", "\n", - "def solve_scheduling_model(model, hint):\n", - " \"\"\"solve the given model using a cumutive model.\"\"\"\n", + "def solve_problem_with_scheduling_model(\n", + " problem: Dict[str, SectionInfo], hint: Dict[int, int]\n", + ") -> None:\n", + " \"\"\"solve the given problem using a cumulative model.\"\"\"\n", "\n", " print(\"Solving using the scheduling model\")\n", - " # Model data\n", - " num_tasks = model[\"number of tasks\"].value\n", + " # Problem data\n", + " num_tasks = problem[\"number of tasks\"].value\n", + " if num_tasks is None:\n", + " return\n", " all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the data.\n", - " durations = model[\"task times\"].index_map\n", - " precedences = model[\"precedence relations\"].set_of_pairs\n", - " cycle_time = model[\"cycle time\"].value\n", + " durations = problem[\"task times\"].index_map\n", + " precedences = problem[\"precedence relations\"].set_of_pairs\n", + " cycle_time = problem[\"cycle time\"].value\n", "\n", " num_pods = max(p for _, p in hint.items()) + 1 if hint else num_tasks\n", "\n", @@ -407,14 +416,14 @@ " if len(argv) > 1:\n", " raise app.UsageError(\"Too many command-line arguments.\")\n", "\n", - " model = read_model(_INPUT.value)\n", - " print_stats(model)\n", - " greedy_solution = solve_model_greedily(model)\n", + " problem = read_problem(_INPUT.value)\n", + " print_stats(problem)\n", + " greedy_solution = solve_problem_greedily(problem)\n", "\n", " if _MODEL.value == \"boolean\":\n", - " solve_boolean_model(model, greedy_solution)\n", + " solve_problem_with_boolean_model(problem, greedy_solution)\n", " elif _MODEL.value == \"scheduling\":\n", - " solve_scheduling_model(model, greedy_solution)\n", + " solve_problem_with_scheduling_model(problem, greedy_solution)\n", "\n", "\n", "main()\n", diff --git a/examples/notebook/examples/linear_assignment_api.ipynb b/examples/notebook/examples/linear_assignment_api.ipynb index 55c1d39baa1..093c87cf6fc 100644 --- a/examples/notebook/examples/linear_assignment_api.ipynb +++ b/examples/notebook/examples/linear_assignment_api.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/linear_programming.ipynb b/examples/notebook/examples/linear_programming.ipynb index 553507ec07b..ea83ff3c899 100644 --- a/examples/notebook/examples/linear_programming.ipynb +++ b/examples/notebook/examples/linear_programming.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/magic_sequence_distribute.ipynb b/examples/notebook/examples/magic_sequence_distribute.ipynb index ed8cd1d9098..d3ec65460f9 100644 --- a/examples/notebook/examples/magic_sequence_distribute.ipynb +++ b/examples/notebook/examples/magic_sequence_distribute.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/maximize_combinations_sat.ipynb b/examples/notebook/examples/maximize_combinations_sat.ipynb new file mode 100644 index 00000000000..a30e3d7166f --- /dev/null +++ b/examples/notebook/examples/maximize_combinations_sat.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "google", + "metadata": {}, + "source": [ + "##### Copyright 2024 Google LLC." + ] + }, + { + "cell_type": "markdown", + "id": "apache", + "metadata": {}, + "source": [ + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License.\n" + ] + }, + { + "cell_type": "markdown", + "id": "basename", + "metadata": {}, + "source": [ + "# maximize_combinations_sat" + ] + }, + { + "cell_type": "markdown", + "id": "link", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "doc", + "metadata": {}, + "source": [ + "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install ortools" + ] + }, + { + "cell_type": "markdown", + "id": "description", + "metadata": {}, + "source": [ + "\n", + "Maximize the number of valid combinations of Boolean variables.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "code", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Sequence\n", + "from ortools.sat.python import cp_model\n", + "\n", + "\n", + "def maximize_combinations_sat() -> None:\n", + " \"\"\"Maximize the number of valid combinations of Boolean variables.\"\"\"\n", + " model = cp_model.CpModel()\n", + " cards: list[cp_model.IntVar] = [\n", + " model.new_bool_var(\"card1\"),\n", + " model.new_bool_var(\"card2\"),\n", + " model.new_bool_var(\"card3\"),\n", + " model.new_bool_var(\"card4\"),\n", + " ]\n", + "\n", + " combos: list[list[cp_model.IntVar]] = [\n", + " [cards[0], cards[1]],\n", + " [cards[0], cards[2]],\n", + " [cards[1], cards[3]],\n", + " [cards[0], cards[2], cards[3]],\n", + " ]\n", + "\n", + " deck_size: int = 3\n", + " model.add(sum(cards) == deck_size)\n", + "\n", + " valid_combos: list[cp_model.IntVar] = []\n", + " for combination in combos:\n", + " is_valid = model.new_bool_var(\"\")\n", + "\n", + " # All true implies is_valid.\n", + " model.add_bool_and(is_valid).only_enforce_if(combination)\n", + "\n", + " # is_valid implies all true.\n", + " for literal in combination:\n", + " model.add_implication(is_valid, literal)\n", + " valid_combos.append(is_valid)\n", + "\n", + " model.maximize(sum(valid_combos))\n", + "\n", + " solver = cp_model.CpSolver()\n", + " solver.parameters.log_search_progress = True\n", + " status = solver.solve(model)\n", + "\n", + " if status == cp_model.OPTIMAL:\n", + " print(\n", + " \"chosen cards:\",\n", + " [card.name for card in cards if solver.boolean_value(card)],\n", + " )\n", + "\n", + "\n", + "def main(argv: Sequence[str]) -> None:\n", + " if len(argv) > 1:\n", + " raise app.UsageError(\"Too many command-line arguments.\")\n", + " maximize_combinations_sat()\n", + "\n", + "\n", + "main()\n", + "\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebook/examples/maze_escape_sat.ipynb b/examples/notebook/examples/maze_escape_sat.ipynb index cebacf631b8..2ff03b9f05b 100644 --- a/examples/notebook/examples/maze_escape_sat.ipynb +++ b/examples/notebook/examples/maze_escape_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -89,7 +89,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Sequence\n", + "from typing import Dict, Sequence, Tuple\n", "\n", "from ortools.sat.colab import flags\n", "from google.protobuf import text_format\n", @@ -99,11 +99,25 @@ " \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n", ")\n", "_PARAMS = flags.define_string(\n", - " \"params\", \"num_search_workers:8,log_search_progress:true\", \"Sat solver parameters.\"\n", + " \"params\",\n", + " \"num_search_workers:8,log_search_progress:true\",\n", + " \"Sat solver parameters.\",\n", ")\n", "\n", "\n", - "def add_neighbor(size, x, y, z, dx, dy, dz, model, index_map, position_to_rank, arcs):\n", + "def add_neighbor(\n", + " size: int,\n", + " x: int,\n", + " y: int,\n", + " z: int,\n", + " dx: int,\n", + " dy: int,\n", + " dz: int,\n", + " model: cp_model.CpModel,\n", + " index_map: Dict[Tuple[int, int, int], int],\n", + " position_to_rank: Dict[Tuple[int, int, int], cp_model.IntVar],\n", + " arcs: list[Tuple[int, int, cp_model.LiteralT]],\n", + ") -> None:\n", " \"\"\"Checks if the neighbor is valid, and adds it to the model.\"\"\"\n", " if (\n", " x + dx < 0\n", @@ -123,7 +137,7 @@ " arcs.append((before_index, after_index, move_literal))\n", "\n", "\n", - "def escape_the_maze(params, output_proto):\n", + "def escape_the_maze(params: str, output_proto: str) -> None:\n", " \"\"\"Escapes the maze.\"\"\"\n", " size = 4\n", " boxes = [(0, 1, 0), (2, 0, 1), (1, 3, 1), (3, 1, 3)]\n", @@ -157,7 +171,7 @@ "\n", " # Circuit constraint: visit all blocks exactly once, and maintains the rank\n", " # of each block.\n", - " arcs = []\n", + " arcs: list[Tuple[int, int, cp_model.LiteralT]] = []\n", " for x in range(size):\n", " for y in range(size):\n", " for z in range(size):\n", @@ -211,8 +225,8 @@ " elif position == end:\n", " msg += \" [end]\"\n", " else:\n", - " for b in range(len(boxes)):\n", - " if position == boxes[b]:\n", + " for b, box in enumerate(boxes):\n", + " if position == box:\n", " msg += f\" [boxes {b}]\"\n", " path[rank] = msg\n", " print(path)\n", diff --git a/examples/notebook/examples/memory_layout_and_infeasibility_sat.ipynb b/examples/notebook/examples/memory_layout_and_infeasibility_sat.ipynb index 8819fc42906..5512d91f34e 100644 --- a/examples/notebook/examples/memory_layout_and_infeasibility_sat.ipynb +++ b/examples/notebook/examples/memory_layout_and_infeasibility_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -121,8 +121,10 @@ " y_starts: List[cp_model.IntVar] = []\n", " y_intervals: List[cp_model.IntervalVar] = []\n", "\n", - " for start, end, demand, unused_alignment in DEMANDS:\n", - " x_interval = model.new_fixed_size_interval_var(start, end - start + 1, \"\")\n", + " for start_time, end_time, demand, _ in DEMANDS:\n", + " x_interval = model.new_fixed_size_interval_var(\n", + " start_time, end_time - start_time + 1, \"\"\n", + " )\n", " y_start = model.new_int_var(0, CAPACITY - demand, \"\")\n", " y_interval = model.new_fixed_size_interval_var(y_start, demand, \"\")\n", "\n", @@ -141,9 +143,9 @@ " status = solver.solve(model)\n", " print(solver.response_stats())\n", "\n", - " if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:\n", - " for index, start in enumerate(y_starts):\n", - " print(f\"task {index} buffer starts at {solver.value(start)}\")\n", + " if status in (cp_model.FEASIBLE, cp_model.OPTIMAL):\n", + " for index, start_var in enumerate(y_starts):\n", + " print(f\"task {index} buffer starts at {solver.value(start_var)}\")\n", "\n", " return status != cp_model.INFEASIBLE\n", "\n", diff --git a/examples/notebook/examples/no_wait_baking_scheduling_sat.ipynb b/examples/notebook/examples/no_wait_baking_scheduling_sat.ipynb index 442eb1f8e14..e82657ba4a1 100644 --- a/examples/notebook/examples/no_wait_baking_scheduling_sat.ipynb +++ b/examples/notebook/examples/no_wait_baking_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -90,16 +90,16 @@ "outputs": [], "source": [ "import collections\n", - "from typing import Sequence\n", + "from typing import List, Sequence, Tuple\n", + "\n", "from ortools.sat.colab import flags\n", "from google.protobuf import text_format\n", "from ortools.sat.python import cp_model\n", "\n", "_PARAMS = flags.define_string(\n", - " \"params\", \"num_search_workers:16, max_time_in_seconds:30\", \"Sat solver parameters.\"\n", - ")\n", - "_PROTO_FILE = flags.define_string(\n", - " \"proto_file\", \"\", \"If not empty, output the proto to this file.\"\n", + " \"params\",\n", + " \"num_search_workers:16, max_time_in_seconds:30\",\n", + " \"Sat solver parameters.\",\n", ")\n", "\n", "# Recipes\n", @@ -117,7 +117,7 @@ "DISPLAY = \"display\"\n", "\n", "\n", - "class Task(object):\n", + "class Task:\n", " \"\"\"A unit baking task.\n", "\n", " - Simple baking tasks have a fixed duration. They are performed by workers.\n", @@ -131,7 +131,7 @@ " self.max_duration = max_duration\n", "\n", "\n", - "class Skill(object):\n", + "class Skill:\n", " \"\"\"The skill of a worker or the capability of a machine.\"\"\"\n", "\n", " def __init__(self, name, efficiency):\n", @@ -140,19 +140,21 @@ " self.efficiency = efficiency\n", "\n", "\n", - "class Recipe(object):\n", + "class Recipe:\n", " \"\"\"A recipe is a sequence of cooking tasks.\"\"\"\n", "\n", " def __init__(self, name):\n", " self.name = name\n", " self.tasks = []\n", "\n", - " def add_task(self, resource_name, min_duration, max_duration):\n", + " def add_task(\n", + " self, resource_name: str, min_duration: int, max_duration: int\n", + " ) -> \"Recipe\":\n", " self.tasks.append(Task(resource_name, min_duration, max_duration))\n", " return self\n", "\n", "\n", - "class Resource(object):\n", + "class Resource:\n", " \"\"\"A resource is a worker, a machine, or just some space for cakes to rest.\n", "\n", " - Workers have a capacity of 1 and can have variable efficiency.\n", @@ -168,12 +170,12 @@ " self.capacity = capacity\n", " self.skills = []\n", "\n", - " def add_skill(self, skill_name, efficiency):\n", + " def add_skill(self, skill_name: str, efficiency: float) -> \"Resource\":\n", " self.skills.append(Skill(skill_name, efficiency))\n", " return self\n", "\n", "\n", - "class Order(object):\n", + "class Order:\n", " \"\"\"An order is a recipe that should be delivered at a given due date.\"\"\"\n", "\n", " def __init__(self, unique_id, recipe_name, due_date, quantity):\n", @@ -191,7 +193,7 @@ " self.quantity = quantity\n", "\n", "\n", - "def set_up_data():\n", + "def set_up_data() -> Tuple[List[Recipe], List[Resource], List[Order]]:\n", " \"\"\"Set up the bakery problem data.\"\"\"\n", "\n", " # Recipes.\n", @@ -260,7 +262,9 @@ " return recipes, resources, orders\n", "\n", "\n", - "def solve_with_cp_sat(recipes, resources, orders):\n", + "def solve_with_cp_sat(\n", + " recipes: List[Recipe], resources: List[Resource], orders: List[Order]\n", + ") -> None:\n", " \"\"\"Build the optimization model, and solve the problem.\"\"\"\n", "\n", " model = cp_model.CpModel()\n", @@ -297,7 +301,6 @@ " skill_name = task.name\n", " suffix = f\"_{order.unique_id}_batch{batch}_{skill_name}\"\n", "\n", - " start = None\n", " if previous_end is None:\n", " start = model.new_int_var(start_work, horizon, f\"start{suffix}\")\n", " orders_sequence_of_events[order_id].append(\n", @@ -309,7 +312,6 @@ " size = model.new_int_var(\n", " task.min_duration, task.max_duration, f\"size{suffix}\"\n", " )\n", - " end = None\n", " if task == recipe.tasks[-1]:\n", " # The order must end after the due_date. Ideally, exactly at the\n", " # due_date.\n", diff --git a/examples/notebook/examples/nqueens_sat.ipynb b/examples/notebook/examples/nqueens_sat.ipynb index c235565da3e..391be2abc69 100644 --- a/examples/notebook/examples/nqueens_sat.ipynb +++ b/examples/notebook/examples/nqueens_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -96,26 +96,26 @@ "\n", " def __init__(self, queens: list[cp_model.IntVar]):\n", " cp_model.CpSolverSolutionCallback.__init__(self)\n", - " self.__queens = queens\n", - " self.__solution_count = 0\n", - " self.__start_time = time.time()\n", + " self._queens = queens\n", + " self._solution_count = 0\n", + " self._start_time = time.time()\n", "\n", " @property\n", " def solution_count(self) -> int:\n", - " return self.__solution_count\n", + " return self._solution_count\n", "\n", " def on_solution_callback(self) -> None:\n", " current_time = time.time()\n", " print(\n", - " \"Solution %i, time = %f s\"\n", - " % (self.__solution_count, current_time - self.__start_time)\n", + " f\"Solution{self._solution_count}, time =\"\n", + " f\" {current_time - self._start_time} s\"\n", " )\n", - " self.__solution_count += 1\n", + " self._solution_count += 1\n", "\n", - " all_queens = range(len(self.__queens))\n", + " all_queens = range(len(self._queens))\n", " for i in all_queens:\n", " for j in all_queens:\n", - " if self.value(self.__queens[j]) == i:\n", + " if self.value(self._queens[j]) == i:\n", " # There is a queen in column j, row i.\n", " print(\"Q\", end=\" \")\n", " else:\n", diff --git a/examples/notebook/examples/pell_equation_sat.ipynb b/examples/notebook/examples/pell_equation_sat.ipynb new file mode 100644 index 00000000000..d136340121a --- /dev/null +++ b/examples/notebook/examples/pell_equation_sat.ipynb @@ -0,0 +1,141 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "google", + "metadata": {}, + "source": [ + "##### Copyright 2024 Google LLC." + ] + }, + { + "cell_type": "markdown", + "id": "apache", + "metadata": {}, + "source": [ + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License.\n" + ] + }, + { + "cell_type": "markdown", + "id": "basename", + "metadata": {}, + "source": [ + "# pell_equation_sat" + ] + }, + { + "cell_type": "markdown", + "id": "link", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "doc", + "metadata": {}, + "source": [ + "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install ortools" + ] + }, + { + "cell_type": "markdown", + "id": "description", + "metadata": {}, + "source": [ + "\n", + "Solves Pell's equation x^2 - coeff * y^2 = 1.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "code", + "metadata": {}, + "outputs": [], + "source": [ + "from collections.abc import Sequence\n", + "\n", + "from ortools.sat.colab import flags\n", + "from ortools.sat.python import cp_model\n", + "\n", + "\n", + "_COEFF = flags.define_integer(\"coeff\", 1, \"The Pell equation coefficient.\")\n", + "_MAX_VALUE = flags.define_integer(\"max_value\", 5000_000, \"The maximum value.\")\n", + "\n", + "\n", + "def solve_pell(coeff: int, max_value: int) -> None:\n", + " \"\"\"Solves Pell's equation x^2 - coeff * y^2 = 1.\"\"\"\n", + " model = cp_model.CpModel()\n", + "\n", + " x = model.new_int_var(1, max_value, \"x\")\n", + " y = model.new_int_var(1, max_value, \"y\")\n", + "\n", + " # Pell's equation:\n", + " x_square = model.new_int_var(1, max_value * max_value, \"x_square\")\n", + " y_square = model.new_int_var(1, max_value * max_value, \"y_square\")\n", + " model.add_multiplication_equality(x_square, x, x)\n", + " model.add_multiplication_equality(y_square, y, y)\n", + " model.add(x_square - coeff * y_square == 1)\n", + "\n", + " model.add_decision_strategy(\n", + " [x, y], cp_model.CHOOSE_MIN_DOMAIN_SIZE, cp_model.SELECT_MIN_VALUE\n", + " )\n", + "\n", + " solver = cp_model.CpSolver()\n", + " solver.parameters.num_workers = 12\n", + " solver.parameters.log_search_progress = True\n", + " solver.parameters.cp_model_presolve = True\n", + " solver.parameters.cp_model_probing_level = 0\n", + "\n", + " result = solver.solve(model)\n", + " if result == cp_model.OPTIMAL:\n", + " print(f\"x={solver.value(x)} y={solver.value(y)} coeff={coeff}\")\n", + " if solver.value(x) ** 2 - coeff * (solver.value(y) ** 2) != 1:\n", + " raise ValueError(\"Pell equation not satisfied.\")\n", + "\n", + "\n", + "def main(argv: Sequence[str]) -> None:\n", + " if len(argv) > 1:\n", + " raise app.UsageError(\"Too many command-line arguments.\")\n", + " solve_pell(_COEFF.value, _MAX_VALUE.value)\n", + "\n", + "\n", + "main()\n", + "\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebook/examples/pentominoes_sat.ipynb b/examples/notebook/examples/pentominoes_sat.ipynb index e3d2a01a180..8f47a9623c7 100644 --- a/examples/notebook/examples/pentominoes_sat.ipynb +++ b/examples/notebook/examples/pentominoes_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -81,6 +81,9 @@ "\n", "This problem comes from the game Katamino:\n", "http://boardgamegeek.com/boardgame/6931/katamino\n", + "\n", + "This example also includes suggestions from\n", + "https://web.ma.utexas.edu/users/smmg/archive/1997/radin.html\n", "\n" ] }, @@ -109,6 +112,8 @@ " \"pieces\", \"FILNPTUVWXYZ\", \"The subset of pieces to consider.\"\n", ")\n", "\n", + "_HEIGHT = flags.define_integer(\"height\", 5, \"The height of the box.\")\n", + "\n", "\n", "def is_one(mask: List[List[int]], x: int, y: int, orientation: int) -> bool:\n", " \"\"\"Returns true if the oriented piece is 1 at position [i][j].\n", @@ -171,8 +176,9 @@ "\n", "def generate_and_solve_problem(pieces: Dict[str, List[List[int]]]) -> None:\n", " \"\"\"Solves the pentominoes problem.\"\"\"\n", - " box_width = len(pieces)\n", - " box_height = 5\n", + " box_height = _HEIGHT.value\n", + " box_width = 5 * len(pieces) // box_height\n", + " print(f\"Box has dimension {box_height} * {box_width}\")\n", "\n", " model = cp_model.CpModel()\n", " position_to_variables: List[List[List[cp_model.IntVar]]] = [\n", @@ -209,8 +215,8 @@ " status = solver.solve(model)\n", "\n", " print(\n", - " f\"Problem {_PIECES.value} solved in {solver.wall_time}s with status\"\n", - " f\" {solver.status_name(status)}\"\n", + " f\"Problem {_PIECES.value} box {box_height}*{box_width} solved in\"\n", + " f\" {solver.wall_time}s with status {solver.status_name(status)}\"\n", " )\n", "\n", " # Print the solution.\n", @@ -250,6 +256,16 @@ " print(f\"Piece {p} not found in the list of pieces\")\n", " return\n", " selected_pieces[p] = pieces[p]\n", + " if (len(selected_pieces) * 5) % _HEIGHT.value != 0:\n", + " print(\n", + " f\"The height {_HEIGHT.value} does not divide the total area\"\n", + " f\" {5 * len(selected_pieces)}\"\n", + " )\n", + " return\n", + " if _HEIGHT.value < 3 or 5 * len(selected_pieces) // _HEIGHT.value < 3:\n", + " print(f\"The height {_HEIGHT.value} is not compatible with the pieces.\")\n", + " return\n", + "\n", " generate_and_solve_problem(selected_pieces)\n", "\n", "\n", diff --git a/examples/notebook/examples/prize_collecting_tsp.ipynb b/examples/notebook/examples/prize_collecting_tsp.ipynb index d9ac369f160..3c8cbb9eb4b 100644 --- a/examples/notebook/examples/prize_collecting_tsp.ipynb +++ b/examples/notebook/examples/prize_collecting_tsp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -83,7 +83,7 @@ "outputs": [], "source": [ "from ortools.routing import enums_pb2\n", - "from ortools.constraint_solver import pywrapcp\n", + "from ortools.routing import pywraprouting\n", "\n", "DISTANCE_MATRIX = [\n", " [0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],\n", @@ -175,13 +175,13 @@ " all_nodes = range(num_nodes)\n", "\n", " # Create the routing index manager.\n", - " manager = pywrapcp.RoutingIndexManager(\n", + " manager = pywraprouting.RoutingIndexManager(\n", " num_nodes,\n", " num_vehicles,\n", " depot)\n", "\n", " # Create routing model.\n", - " routing = pywrapcp.RoutingModel(manager)\n", + " routing = pywraprouting.RoutingModel(manager)\n", "\n", " # Create and register a transit callback.\n", " def distance_callback(from_index, to_index):\n", @@ -214,7 +214,7 @@ " VISIT_VALUES[node])\n", "\n", " # Setting first solution heuristic.\n", - " search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n", + " search_parameters = pywraprouting.DefaultRoutingSearchParameters()\n", " search_parameters.first_solution_strategy = (\n", " enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n", " search_parameters.local_search_metaheuristic = (\n", diff --git a/examples/notebook/examples/prize_collecting_tsp_sat.ipynb b/examples/notebook/examples/prize_collecting_tsp_sat.ipynb index 953b755e2ad..df2d25e7c15 100644 --- a/examples/notebook/examples/prize_collecting_tsp_sat.ipynb +++ b/examples/notebook/examples/prize_collecting_tsp_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/prize_collecting_vrp.ipynb b/examples/notebook/examples/prize_collecting_vrp.ipynb index 53eb2400e14..cf7d6c2cf4f 100644 --- a/examples/notebook/examples/prize_collecting_vrp.ipynb +++ b/examples/notebook/examples/prize_collecting_vrp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -83,7 +83,7 @@ "outputs": [], "source": [ "from ortools.routing import enums_pb2\n", - "from ortools.constraint_solver import pywrapcp\n", + "from ortools.routing import pywraprouting\n", "\n", "DISTANCE_MATRIX = [\n", " [0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],\n", @@ -181,13 +181,13 @@ " all_nodes = range(num_nodes)\n", "\n", " # Create the routing index manager.\n", - " manager = pywrapcp.RoutingIndexManager(\n", + " manager = pywraprouting.RoutingIndexManager(\n", " num_nodes,\n", " num_vehicles,\n", " depot)\n", "\n", " # Create routing model.\n", - " routing = pywrapcp.RoutingModel(manager)\n", + " routing = pywraprouting.RoutingModel(manager)\n", "\n", " # Create and register a transit callback.\n", " def distance_callback(from_index, to_index):\n", @@ -220,7 +220,7 @@ " VISIT_VALUES[node])\n", "\n", " # Setting first solution heuristic.\n", - " search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n", + " search_parameters = pywraprouting.DefaultRoutingSearchParameters()\n", " search_parameters.first_solution_strategy = (\n", " enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n", " search_parameters.local_search_metaheuristic = (\n", diff --git a/examples/notebook/examples/prize_collecting_vrp_sat.ipynb b/examples/notebook/examples/prize_collecting_vrp_sat.ipynb index ece91f2ba5e..e0565da8394 100644 --- a/examples/notebook/examples/prize_collecting_vrp_sat.ipynb +++ b/examples/notebook/examples/prize_collecting_vrp_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/proto_solve.ipynb b/examples/notebook/examples/proto_solve.ipynb index 58708fa2ede..8ef8b466e28 100644 --- a/examples/notebook/examples/proto_solve.ipynb +++ b/examples/notebook/examples/proto_solve.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/pyflow_example.ipynb b/examples/notebook/examples/pyflow_example.ipynb index 920b6a6e6e7..d90c8fc9926 100644 --- a/examples/notebook/examples/pyflow_example.ipynb +++ b/examples/notebook/examples/pyflow_example.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/qubo_sat.ipynb b/examples/notebook/examples/qubo_sat.ipynb index d3273c3bb46..23720414661 100644 --- a/examples/notebook/examples/qubo_sat.ipynb +++ b/examples/notebook/examples/qubo_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/random_tsp.ipynb b/examples/notebook/examples/random_tsp.ipynb index 93011ad3981..6edf0c6ba80 100644 --- a/examples/notebook/examples/random_tsp.ipynb +++ b/examples/notebook/examples/random_tsp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -97,7 +97,7 @@ "import random\n", "\n", "from ortools.routing import enums_pb2\n", - "from ortools.constraint_solver import pywrapcp\n", + "from ortools.routing import pywraprouting\n", "\n", "parser = argparse.ArgumentParser()\n", "\n", @@ -161,9 +161,9 @@ " # Second argument = 1 to build a single tour (it's a TSP).\n", " # Nodes are indexed from 0 to args_tsp_size - 1, by default the start of\n", " # the route is node 0.\n", - " manager = pywrapcp.RoutingIndexManager(args.tsp_size, 1, 0)\n", - " routing = pywrapcp.RoutingModel(manager)\n", - " search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n", + " manager = pywraprouting.RoutingIndexManager(args.tsp_size, 1, 0)\n", + " routing = pywraprouting.RoutingModel(manager)\n", + " search_parameters = pywraprouting.DefaultRoutingSearchParameters()\n", " # Setting first solution heuristic (cheapest addition).\n", " search_parameters.first_solution_strategy = (\n", " enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n", diff --git a/examples/notebook/examples/rcpsp_sat.ipynb b/examples/notebook/examples/rcpsp_sat.ipynb index da67fe4fc53..ee96bdd88b3 100644 --- a/examples/notebook/examples/rcpsp_sat.ipynb +++ b/examples/notebook/examples/rcpsp_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/reallocate_sat.ipynb b/examples/notebook/examples/reallocate_sat.ipynb index cf54de82091..3a1aa56091d 100644 --- a/examples/notebook/examples/reallocate_sat.ipynb +++ b/examples/notebook/examples/reallocate_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -89,7 +89,6 @@ "\n", "\n", "def main():\n", - "\n", " # Data\n", " data_0 = [\n", " [107, 107, 107, 0, 0], # pr1\n", @@ -105,7 +104,7 @@ " [298836792, 0, 0, 0],\n", " [3713428, 4118530, 4107277, 3072018],\n", " [6477273, 7183884, 5358471, 0],\n", - " [1485371, 1647412, 1642911, 1228807]\n", + " [1485371, 1647412, 1642911, 1228807],\n", " ]\n", "\n", " data_2 = [\n", @@ -115,7 +114,7 @@ " [2988367, 0, 0, 0],\n", " [37134, 41185, 41072, 30720],\n", " [64772, 71838, 53584, 0],\n", - " [14853, 16474, 16429, 12288]\n", + " [14853, 16474, 16429, 12288],\n", " ]\n", "\n", " pr = data_0\n", @@ -129,7 +128,7 @@ " model = cp_model.CpModel()\n", "\n", " # Variables\n", - " delta = model.NewIntVar(0, total, 'delta')\n", + " delta = model.NewIntVar(0, total, \"delta\")\n", "\n", " contributions_per_years = collections.defaultdict(list)\n", " contributions_per_prs = collections.defaultdict(list)\n", @@ -138,14 +137,12 @@ " for p, inner_l in enumerate(pr):\n", " for y, item in enumerate(inner_l):\n", " if item != 0:\n", - " contrib = model.NewIntVar(0, total, 'r%d c%d' % (p, y))\n", + " contrib = model.NewIntVar(0, total, \"r%d c%d\" % (p, y))\n", " contributions_per_years[y].append(contrib)\n", " contributions_per_prs[p].append(contrib)\n", " all_contribs[p, y] = contrib\n", "\n", - " year_var = [\n", - " model.NewIntVar(0, total, 'y[%i]' % i) for i in range(num_years)\n", - " ]\n", + " year_var = [model.NewIntVar(0, total, \"y[%i]\" % i) for i in range(num_years)]\n", "\n", " # Constraints\n", "\n", @@ -173,32 +170,32 @@ "\n", " # Output solution.\n", " if status == cp_model.OPTIMAL:\n", - " print('Data')\n", - " print(' - total = ', total)\n", - " print(' - year_average = ', avg)\n", - " print(' - number of projects = ', num_pr)\n", - " print(' - number of years = ', num_years)\n", + " print(\"Data\")\n", + " print(\" - total = \", total)\n", + " print(\" - year_average = \", avg)\n", + " print(\" - number of projects = \", num_pr)\n", + " print(\" - number of years = \", num_years)\n", "\n", - " print(' - input production')\n", + " print(\" - input production\")\n", " for p in range(num_pr):\n", " for y in range(num_years):\n", " if pr[p][y] == 0:\n", - " print(' ', end='')\n", + " print(\" \", end=\"\")\n", " else:\n", - " print('%10i' % pr[p][y], end='')\n", + " print(\"%10i\" % pr[p][y], end=\"\")\n", " print()\n", "\n", - " print('Solution')\n", + " print(\"Solution\")\n", " for p in range(num_pr):\n", " for y in range(num_years):\n", " if pr[p][y] == 0:\n", - " print(' ', end='')\n", + " print(\" \", end=\"\")\n", " else:\n", - " print('%10i' % solver.Value(all_contribs[p, y]), end='')\n", + " print(\"%10i\" % solver.Value(all_contribs[p, y]), end=\"\")\n", " print()\n", "\n", " for y in range(num_years):\n", - " print('%10i' % solver.Value(year_var[y]), end='')\n", + " print(\"%10i\" % solver.Value(year_var[y]), end=\"\")\n", " print()\n", "\n", "\n", diff --git a/examples/notebook/examples/shift_scheduling_sat.ipynb b/examples/notebook/examples/shift_scheduling_sat.ipynb index 5b4a551d6c1..68ae241e11f 100644 --- a/examples/notebook/examples/shift_scheduling_sat.ipynb +++ b/examples/notebook/examples/shift_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -177,7 +177,7 @@ " for length in range(hard_min, soft_min):\n", " for start in range(len(works) - length + 1):\n", " span = negated_bounded_span(works, start, length)\n", - " name = \": under_span(start=%i, length=%i)\" % (start, length)\n", + " name = f\": under_span(start={start}, length={length})\"\n", " lit = model.new_bool_var(prefix + name)\n", " span.append(lit)\n", " model.add_bool_or(span)\n", @@ -191,7 +191,7 @@ " for length in range(soft_max + 1, hard_max + 1):\n", " for start in range(len(works) - length + 1):\n", " span = negated_bounded_span(works, start, length)\n", - " name = \": over_span(start=%i, length=%i)\" % (start, length)\n", + " name = f\": over_span(start={start}, length={length})\"\n", " lit = model.new_bool_var(prefix + name)\n", " span.append(lit)\n", " model.add_bool_or(span)\n", @@ -366,7 +366,7 @@ " for e in range(num_employees):\n", " for s in range(num_shifts):\n", " for d in range(num_days):\n", - " work[e, s, d] = model.new_bool_var(\"work%i_%i_%i\" % (e, s, d))\n", + " work[e, s, d] = model.new_bool_var(f\"work{e}_{s}_{d}\")\n", "\n", " # Linear terms of the objective in a minimization context.\n", " obj_int_vars: list[cp_model.IntVar] = []\n", @@ -402,7 +402,7 @@ " soft_max,\n", " hard_max,\n", " max_cost,\n", - " \"shift_constraint(employee %i, shift %i)\" % (e, shift),\n", + " f\"shift_constraint(employee {e}, shift {shift})\",\n", " )\n", " obj_bool_vars.extend(variables)\n", " obj_bool_coeffs.extend(coeffs)\n", @@ -422,8 +422,7 @@ " soft_max,\n", " hard_max,\n", " max_cost,\n", - " \"weekly_sum_constraint(employee %i, shift %i, week %i)\"\n", - " % (e, shift, w),\n", + " f\"weekly_sum_constraint(employee {e}, shift {shift}, week {w})\",\n", " )\n", " obj_int_vars.extend(variables)\n", " obj_int_coeffs.extend(coeffs)\n", @@ -440,7 +439,7 @@ " model.add_bool_or(transition)\n", " else:\n", " trans_var = model.new_bool_var(\n", - " \"transition (employee=%i, day=%i)\" % (e, d)\n", + " f\"transition (employee={e}, day={d})\"\n", " )\n", " transition.append(trans_var)\n", " model.add_bool_or(transition)\n", @@ -458,7 +457,7 @@ " model.add(worked == sum(works))\n", " over_penalty = excess_cover_penalties[s - 1]\n", " if over_penalty > 0:\n", - " name = \"excess_demand(shift=%i, week=%i, day=%i)\" % (s, w, d)\n", + " name = f\"excess_demand(shift={s}, week={w}, day={d})\"\n", " excess = model.new_int_var(0, num_employees - min_demand, name)\n", " model.add(excess == worked - min_demand)\n", " obj_int_vars.append(excess)\n", @@ -471,7 +470,7 @@ " )\n", "\n", " if output_proto:\n", - " print(\"Writing proto to %s\" % output_proto)\n", + " print(f\"Writing proto to {output_proto}\")\n", " with open(output_proto, \"w\") as text_file:\n", " text_file.write(str(model))\n", "\n", @@ -495,7 +494,7 @@ " for s in range(num_shifts):\n", " if solver.boolean_value(work[e, s, d]):\n", " schedule += shifts[s] + \" \"\n", - " print(\"worker %i: %s\" % (e, schedule))\n", + " print(f\"worker {e}: {schedule}\")\n", " print()\n", " print(\"Penalties:\")\n", " for i, var in enumerate(obj_bool_vars):\n", @@ -509,16 +508,12 @@ " for i, var in enumerate(obj_int_vars):\n", " if solver.value(var) > 0:\n", " print(\n", - " \" %s violated by %i, linear penalty=%i\"\n", - " % (var.name, solver.value(var), obj_int_coeffs[i])\n", + " f\" {var.name} violated by {solver.value(var)}, linear\"\n", + " f\" penalty={obj_int_coeffs[i]}\"\n", " )\n", "\n", " print()\n", - " print(\"Statistics\")\n", - " print(\" - status : %s\" % solver.status_name(status))\n", - " print(\" - conflicts : %i\" % solver.num_conflicts)\n", - " print(\" - branches : %i\" % solver.num_branches)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " print(solver.response_stats())\n", "\n", "\n", "def main(_):\n", diff --git a/examples/notebook/examples/single_machine_scheduling_with_setup_release_due_dates_sat.ipynb b/examples/notebook/examples/single_machine_scheduling_with_setup_release_due_dates_sat.ipynb index db433d30809..b8f6fede11f 100644 --- a/examples/notebook/examples/single_machine_scheduling_with_setup_release_due_dates_sat.ipynb +++ b/examples/notebook/examples/single_machine_scheduling_with_setup_release_due_dates_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -95,7 +95,7 @@ ")\n", "_PARAMS = flags.define_string(\n", " \"params\",\n", - " \"num_search_workers:16,log_search_progress:false,max_time_in_seconds:45\",\n", + " \"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45\",\n", " \"Sat solver parameters.\",\n", ")\n", "_PREPROCESS = flags.define_bool(\n", @@ -108,17 +108,16 @@ "class SolutionPrinter(cp_model.CpSolverSolutionCallback):\n", " \"\"\"Print intermediate solutions.\"\"\"\n", "\n", - " def __init__(self):\n", + " def __init__(self) -> None:\n", " cp_model.CpSolverSolutionCallback.__init__(self)\n", " self.__solution_count = 0\n", "\n", - " def on_solution_callback(self):\n", - " \"\"\"Called after each new solution found.\"\"\"\n", + " def on_solution_callback(self) -> None:\n", + " \"\"\"Called at each new solution.\"\"\"\n", " print(\n", - " \"Solution %i, time = %f s, objective = %i\"\n", - " % (self.__solution_count, self.wall_time, self.objective_value)\n", + " f\"Solution {self.__solution_count}, time = {self.wall_time} s,\"\n", + " f\" objective = {self.objective_value}\"\n", " )\n", - " self.__solution_count += 1\n", "\n", "\n", "def single_machine_scheduling():\n", @@ -461,9 +460,7 @@ " if min_incoming_setup == 0:\n", " continue\n", "\n", - " print(\n", - " \"job %i has a min incoming setup of %i\" % (job_id, min_incoming_setup)\n", - " )\n", + " print(f\"job {job_id} has a min incoming setup of {min_incoming_setup}\")\n", " # We can transfer some setup times to the duration of the job.\n", " job_durations[job_id] += min_incoming_setup\n", " # Decrease corresponding incoming setup times.\n", @@ -482,7 +479,7 @@ " horizon = sum(job_durations) + sum(\n", " max(setup_times[i][j] for i in range(num_jobs + 1)) for j in range(num_jobs)\n", " )\n", - " print(\"Greedy horizon =\", horizon)\n", + " print(f\"Greedy horizon = {horizon}\")\n", "\n", " # ----------------------------------------------------------------------------\n", " # Global storage of variables.\n", @@ -497,10 +494,10 @@ " release_date = release_dates[job_id]\n", " due_date = due_dates[job_id] if due_dates[job_id] != -1 else horizon\n", " print(\n", - " \"job %2i: start = %5i, duration = %4i, end = %6i\"\n", - " % (job_id, release_date, duration, due_date)\n", + " f\"job {job_id:2}: start = {release_date:5}, duration = {duration:4},\"\n", + " f\" end = {due_date:6}\"\n", " )\n", - " name_suffix = \"_%i\" % job_id\n", + " name_suffix = f\"_{job_id}\"\n", " start = model.new_int_var(release_date, due_date, \"s\" + name_suffix)\n", " end = model.new_int_var(release_date, due_date, \"e\" + name_suffix)\n", " interval = model.new_interval_var(start, duration, end, \"i\" + name_suffix)\n", @@ -528,7 +525,7 @@ " if i == j:\n", " continue\n", "\n", - " lit = model.new_bool_var(\"%i follows %i\" % (j, i))\n", + " lit = model.new_bool_var(f\"{j} follows {i}\")\n", " arcs.append((i + 1, j + 1, lit))\n", "\n", " # We add the reified precedence to link the literal with the times of the\n", @@ -549,7 +546,7 @@ " # ----------------------------------------------------------------------------\n", " # Precedences.\n", " for before, after in precedences:\n", - " print(\"job %i is after job %i\" % (after, before))\n", + " print(f\"job {after} is after job {before}\")\n", " model.add(ends[before] <= starts[after])\n", "\n", " # ----------------------------------------------------------------------------\n", @@ -561,7 +558,7 @@ " # ----------------------------------------------------------------------------\n", " # Write problem to file.\n", " if output_proto_file:\n", - " print(\"Writing proto to %s\" % output_proto_file)\n", + " print(f\"Writing proto to {output_proto_file}\")\n", " with open(output_proto_file, \"w\") as text_file:\n", " text_file.write(str(model))\n", "\n", @@ -571,12 +568,12 @@ " if parameters:\n", " text_format.Parse(parameters, solver.parameters)\n", " solution_printer = SolutionPrinter()\n", - " solver.best_bound_callback = lambda a : print(f\"New objective lower bound: {a}\")\n", + " solver.best_bound_callback = lambda a: print(f\"New objective lower bound: {a}\")\n", " solver.solve(model, solution_printer)\n", " for job_id in all_jobs:\n", " print(\n", - " \"job %i starts at %i end ends at %i\"\n", - " % (job_id, solver.value(starts[job_id]), solver.value(ends[job_id]))\n", + " f\"job {job_id} starts at {solver.value(starts[job_id])} end ends at\"\n", + " f\" {solver.value(ends[job_id])}\"\n", " )\n", "\n", "\n", diff --git a/examples/notebook/examples/spread_robots_sat.ipynb b/examples/notebook/examples/spread_robots_sat.ipynb index 0108997abd8..f59067cf5dd 100644 --- a/examples/notebook/examples/spread_robots_sat.ipynb +++ b/examples/notebook/examples/spread_robots_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -100,7 +100,7 @@ ")\n", "\n", "\n", - "def spread_robots(num_robots: int, room_size: int, params: str):\n", + "def spread_robots(num_robots: int, room_size: int, params: str) -> None:\n", " \"\"\"Optimize robots placement.\"\"\"\n", " model = cp_model.CpModel()\n", "\n", diff --git a/examples/notebook/examples/steel_mill_slab_sat.ipynb b/examples/notebook/examples/steel_mill_slab_sat.ipynb index f41ba730583..effe92258de 100644 --- a/examples/notebook/examples/steel_mill_slab_sat.ipynb +++ b/examples/notebook/examples/steel_mill_slab_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -106,13 +106,10 @@ ")\n", "\n", "\n", - "def build_problem(problem_id):\n", + "def build_problem(\n", + " problem_id: int,\n", + ") -> tuple[int, list[int], int, list[tuple[int, int]]]:\n", " \"\"\"Build problem data.\"\"\"\n", - " capacities = None\n", - " num_colors = None\n", - " num_slabs = None\n", - " orders = None\n", - "\n", " if problem_id == 0:\n", " capacities = [\n", " # fmt:off\n", @@ -167,15 +164,22 @@ " # fmt:on\n", " ]\n", "\n", - " elif problem_id == 3:\n", + " else: # problem_id == 3, default problem.\n", " capacities = [0, 17, 44]\n", " num_colors = 8\n", " num_slabs = 10\n", " orders = [ # (size, color)\n", - " # fmt:off\n", - " (4, 1), (22, 2), (9, 3), (5, 4), (8, 5), (3, 6), (3, 4), (4, 7),\n", - " (7, 4), (7, 8), (3, 6),\n", - " # fmt:on\n", + " (4, 1),\n", + " (22, 2),\n", + " (9, 3),\n", + " (5, 4),\n", + " (8, 5),\n", + " (3, 6),\n", + " (3, 4),\n", + " (4, 7),\n", + " (7, 4),\n", + " (7, 8),\n", + " (3, 6),\n", " ]\n", "\n", " return (num_slabs, capacities, num_colors, orders)\n", @@ -184,7 +188,7 @@ "class SteelMillSlabSolutionPrinter(cp_model.CpSolverSolutionCallback):\n", " \"\"\"Print intermediate solutions.\"\"\"\n", "\n", - " def __init__(self, orders, assign, load, loss):\n", + " def __init__(self, orders, assign, load, loss) -> None:\n", " cp_model.CpSolverSolutionCallback.__init__(self)\n", " self.__orders = orders\n", " self.__assign = assign\n", @@ -195,13 +199,13 @@ " self.__all_slabs = range(len(assign[0]))\n", " self.__start_time = time.time()\n", "\n", - " def on_solution_callback(self):\n", + " def on_solution_callback(self) -> None:\n", " \"\"\"Called on each new solution.\"\"\"\n", " current_time = time.time()\n", " objective = sum(self.value(l) for l in self.__loss)\n", " print(\n", - " \"Solution %i, time = %f s, objective = %i\"\n", - " % (self.__solution_count, current_time - self.__start_time, objective)\n", + " f\"Solution {self.__solution_count}, time =\"\n", + " f\" {current_time - self.__start_time} s, objective = {objective}\"\n", " )\n", " self.__solution_count += 1\n", " orders_in_slab = [\n", @@ -210,25 +214,20 @@ " ]\n", " for s in self.__all_slabs:\n", " if orders_in_slab[s]:\n", - " line = \" - slab %i, load = %i, loss = %i, orders = [\" % (\n", - " s,\n", - " self.value(self.__load[s]),\n", - " self.value(self.__loss[s]),\n", + " line = (\n", + " f\" - slab {s}, load = {self.value(self.__load[s])}, loss =\"\n", + " f\" {self.value(self.__loss[s])}, orders = [\"\n", " )\n", " for o in orders_in_slab[s]:\n", - " line += \"#%i(w%i, c%i) \" % (\n", - " o,\n", - " self.__orders[o][0],\n", - " self.__orders[o][1],\n", - " )\n", + " line += f\"#{o}(w{self.__orders[o][0]}, c{self.__orders[o][1]})\"\n", " line += \"]\"\n", " print(line)\n", "\n", "\n", - "def steel_mill_slab(problem, break_symmetries):\n", + "def steel_mill_slab(problem_id: int, break_symmetries: bool) -> None:\n", " \"\"\"Solves the Steel Mill Slab Problem.\"\"\"\n", " ### Load problem.\n", - " (num_slabs, capacities, num_colors, orders) = build_problem(problem)\n", + " num_slabs, capacities, num_colors, orders = build_problem(problem_id)\n", "\n", " num_orders = len(orders)\n", " num_capacities = len(capacities)\n", @@ -236,8 +235,8 @@ " all_colors = range(num_colors)\n", " all_orders = range(len(orders))\n", " print(\n", - " \"Solving steel mill with %i orders, %i slabs, and %i capacities\"\n", - " % (num_orders, num_slabs, num_capacities - 1)\n", + " f\"Solving steel mill with {num_orders} orders, {num_slabs} slabs, and\"\n", + " f\" {num_capacities - 1} capacities\"\n", " )\n", "\n", " # Compute auxiliary data.\n", @@ -260,14 +259,12 @@ " # Create the model and the decision variables.\n", " model = cp_model.CpModel()\n", " assign = [\n", - " [model.new_bool_var(\"assign_%i_to_slab_%i\" % (o, s)) for s in all_slabs]\n", + " [model.new_bool_var(f\"assign_{o}_to_slab_{s}\") for s in all_slabs]\n", " for o in all_orders\n", " ]\n", - " loads = [\n", - " model.new_int_var(0, max_capacity, \"load_of_slab_%i\" % s) for s in all_slabs\n", - " ]\n", + " loads = [model.new_int_var(0, max_capacity, f\"load_of_slab_{s}\") for s in all_slabs]\n", " color_is_in_slab = [\n", - " [model.new_bool_var(\"color_%i_in_slab_%i\" % (c + 1, s)) for c in all_colors]\n", + " [model.new_bool_var(f\"color_{c + 1}_in_slab_{s}\") for c in all_colors]\n", " for s in all_slabs\n", " ]\n", "\n", @@ -334,19 +331,19 @@ " # Create position variables if there are symmetries to be broken.\n", " if break_symmetries and ordered_equivalent_orders:\n", " print(\n", - " \" - creating %i symmetry breaking constraints\"\n", - " % len(ordered_equivalent_orders)\n", + " f\" - creating {len(ordered_equivalent_orders)} symmetry breaking\"\n", + " \" constraints\"\n", " )\n", " positions = {}\n", " for p in ordered_equivalent_orders:\n", " if p[0] not in positions:\n", " positions[p[0]] = model.new_int_var(\n", - " 0, num_slabs - 1, \"position_of_slab_%i\" % p[0]\n", + " 0, num_slabs - 1, f\"position_of_slab_{p[0]}\"\n", " )\n", " model.add_map_domain(positions[p[0]], assign[p[0]])\n", " if p[1] not in positions:\n", " positions[p[1]] = model.new_int_var(\n", - " 0, num_slabs - 1, \"position_of_slab_%i\" % p[1]\n", + " 0, num_slabs - 1, f\"position_of_slab_{p[1]}\"\n", " )\n", " model.add_map_domain(positions[p[1]], assign[p[1]])\n", " # Finally add the symmetry breaking constraint.\n", @@ -354,7 +351,7 @@ "\n", " # Objective.\n", " obj = model.new_int_var(0, num_slabs * max_loss, \"obj\")\n", - " losses = [model.new_int_var(0, max_loss, \"loss_%i\" % s) for s in all_slabs]\n", + " losses = [model.new_int_var(0, max_loss, f\"loss_{s}\") for s in all_slabs]\n", " for s in all_slabs:\n", " model.add_element(loads[s], loss_array, losses[s])\n", " model.add(obj == sum(losses))\n", @@ -370,14 +367,19 @@ " ### Output the solution.\n", " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", " print(\n", - " \"Loss = %i, time = %f s, %i conflicts\"\n", - " % (solver.objective_value, solver.wall_time, solver.num_conflicts)\n", + " f\"Loss = {solver.objective_value}, time = {solver.wall_time} s,\"\n", + " f\" {solver.num_conflicts} conflicts\"\n", " )\n", " else:\n", " print(\"No solution\")\n", "\n", "\n", - "def collect_valid_slabs_dp(capacities, colors, widths, loss_array):\n", + "def collect_valid_slabs_dp(\n", + " capacities: list[int],\n", + " colors: list[int],\n", + " widths: list[int],\n", + " loss_array: list[int],\n", + ") -> list[list[int]]:\n", " \"\"\"Collect valid columns (assign, loss) for one slab.\"\"\"\n", " start_time = time.time()\n", "\n", @@ -406,8 +408,8 @@ " all_valid_assignments.extend(new_assignments)\n", "\n", " print(\n", - " \"%i assignments created in %.2f s\"\n", - " % (len(all_valid_assignments), time.time() - start_time)\n", + " f\"{len(all_valid_assignments)} assignments created in\"\n", + " f\" {time.time() - start_time:2f} s\"\n", " )\n", " tuples = []\n", " for assignment in all_valid_assignments:\n", @@ -421,10 +423,10 @@ " return tuples\n", "\n", "\n", - "def steel_mill_slab_with_valid_slabs(problem, break_symmetries):\n", + "def steel_mill_slab_with_valid_slabs(problem_id: int, break_symmetries: bool) -> None:\n", " \"\"\"Solves the Steel Mill Slab Problem.\"\"\"\n", " ### Load problem.\n", - " (num_slabs, capacities, num_colors, orders) = build_problem(problem)\n", + " (num_slabs, capacities, num_colors, orders) = build_problem(problem_id)\n", "\n", " num_orders = len(orders)\n", " num_capacities = len(capacities)\n", @@ -432,8 +434,8 @@ " all_colors = range(num_colors)\n", " all_orders = range(len(orders))\n", " print(\n", - " \"Solving steel mill with %i orders, %i slabs, and %i capacities\"\n", - " % (num_orders, num_slabs, num_capacities - 1)\n", + " f\"Solving steel mill with {num_orders} orders, {num_slabs} slabs, and\"\n", + " f\" {num_capacities - 1} capacities\"\n", " )\n", "\n", " # Compute auxiliary data.\n", @@ -450,11 +452,11 @@ " # Create the model and the decision variables.\n", " model = cp_model.CpModel()\n", " assign = [\n", - " [model.new_bool_var(\"assign_%i_to_slab_%i\" % (o, s)) for s in all_slabs]\n", + " [model.new_bool_var(r\"assign_{o}_to_slab_{s}\") for s in all_slabs]\n", " for o in all_orders\n", " ]\n", - " loads = [model.new_int_var(0, max_capacity, \"load_%i\" % s) for s in all_slabs]\n", - " losses = [model.new_int_var(0, max_loss, \"loss_%i\" % s) for s in all_slabs]\n", + " loads = [model.new_int_var(0, max_capacity, f\"load_{s}\") for s in all_slabs]\n", + " losses = [model.new_int_var(0, max_loss, f\"loss_{s}\") for s in all_slabs]\n", "\n", " unsorted_valid_slabs = collect_valid_slabs_dp(\n", " capacities, colors, widths, loss_array\n", @@ -516,19 +518,19 @@ " # Create position variables if there are symmetries to be broken.\n", " if ordered_equivalent_orders:\n", " print(\n", - " \" - creating %i symmetry breaking constraints\"\n", - " % len(ordered_equivalent_orders)\n", + " f\" - creating {len(ordered_equivalent_orders)} symmetry breaking\"\n", + " \" constraints\"\n", " )\n", " positions = {}\n", " for p in ordered_equivalent_orders:\n", " if p[0] not in positions:\n", " positions[p[0]] = model.new_int_var(\n", - " 0, num_slabs - 1, \"position_of_slab_%i\" % p[0]\n", + " 0, num_slabs - 1, f\"position_of_slab_{p[0]}\"\n", " )\n", " model.add_map_domain(positions[p[0]], assign[p[0]])\n", " if p[1] not in positions:\n", " positions[p[1]] = model.new_int_var(\n", - " 0, num_slabs - 1, \"position_of_slab_%i\" % p[1]\n", + " 0, num_slabs - 1, f\"position_of_slab_{p[1]}\"\n", " )\n", " model.add_map_domain(positions[p[1]], assign[p[1]])\n", " # Finally add the symmetry breaking constraint.\n", @@ -550,24 +552,24 @@ " ### Output the solution.\n", " if status == cp_model.OPTIMAL:\n", " print(\n", - " \"Loss = %i, time = %.2f s, %i conflicts\"\n", - " % (solver.objective_value, solver.wall_time, solver.num_conflicts)\n", + " f\"Loss = {solver.objective_value}, time = {solver.wall_time:2f} s,\"\n", + " f\" {solver.num_conflicts} conflicts\"\n", " )\n", " else:\n", " print(\"No solution\")\n", "\n", "\n", - "def steel_mill_slab_with_column_generation(problem):\n", + "def steel_mill_slab_with_column_generation(problem_id: int) -> None:\n", " \"\"\"Solves the Steel Mill Slab Problem.\"\"\"\n", " ### Load problem.\n", - " (num_slabs, capacities, _, orders) = build_problem(problem)\n", + " (num_slabs, capacities, _, orders) = build_problem(problem_id)\n", "\n", " num_orders = len(orders)\n", " num_capacities = len(capacities)\n", " all_orders = range(len(orders))\n", " print(\n", - " \"Solving steel mill with %i orders, %i slabs, and %i capacities\"\n", - " % (num_orders, num_slabs, num_capacities - 1)\n", + " f\"Solving steel mill with {num_orders} orders, {num_slabs} slabs, and\"\n", + " f\" {num_capacities - 1} capacities\"\n", " )\n", "\n", " # Compute auxiliary data.\n", @@ -591,7 +593,7 @@ "\n", " # create model and decision variables.\n", " model = cp_model.CpModel()\n", - " selected = [model.new_bool_var(\"selected_%i\" % i) for i in all_valid_slabs]\n", + " selected = [model.new_bool_var(f\"selected_{i}\") for i in all_valid_slabs]\n", "\n", " for order_id in all_orders:\n", " model.add(\n", @@ -619,8 +621,8 @@ " ### Output the solution.\n", " if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n", " print(\n", - " \"Loss = %i, time = %.2f s, %i conflicts\"\n", - " % (solver.objective_value, solver.wall_time, solver.num_conflicts)\n", + " f\"Loss = {solver.objective_value}, time = {solver.wall_time:2f} s,\"\n", + " f\" {solver.num_conflicts} conflicts\"\n", " )\n", " else:\n", " print(\"No solution\")\n", diff --git a/examples/notebook/examples/sudoku_sat.ipynb b/examples/notebook/examples/sudoku_sat.ipynb index bf099994aec..85bf780cb12 100644 --- a/examples/notebook/examples/sudoku_sat.ipynb +++ b/examples/notebook/examples/sudoku_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -86,7 +86,7 @@ "from ortools.sat.python import cp_model\n", "\n", "\n", - "def solve_sudoku():\n", + "def solve_sudoku() -> None:\n", " \"\"\"Solves the sudoku problem with the CP-SAT solver.\"\"\"\n", " # Create the model.\n", " model = cp_model.CpModel()\n", diff --git a/examples/notebook/examples/task_allocation_sat.ipynb b/examples/notebook/examples/task_allocation_sat.ipynb index 41e056b6987..ab7e77752d1 100644 --- a/examples/notebook/examples/task_allocation_sat.ipynb +++ b/examples/notebook/examples/task_allocation_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -91,7 +91,7 @@ "from ortools.sat.python import cp_model\n", "\n", "\n", - "def task_allocation_sat():\n", + "def task_allocation_sat() -> None:\n", " \"\"\"Solves the task allocation problem.\"\"\"\n", " # Availability matrix.\n", " available = [\n", @@ -314,9 +314,9 @@ " assign = {}\n", " for task in all_tasks:\n", " for slot in all_slots:\n", - " assign[(task, slot)] = model.new_bool_var(\"x[%i][%i]\" % (task, slot))\n", + " assign[(task, slot)] = model.new_bool_var(f\"x[{task}][{slot}]\")\n", " count = model.new_int_var(0, nslots, \"count\")\n", - " slot_used = [model.new_bool_var(\"slot_used[%i]\" % s) for s in all_slots]\n", + " slot_used = [model.new_bool_var(f\"slot_used[{s}]\") for s in all_slots]\n", "\n", " for task in all_tasks:\n", " model.add(\n", @@ -353,12 +353,7 @@ " # Uses the portfolion of heuristics.\n", " solver.parameters.log_search_progress = True\n", " solver.parameters.num_search_workers = 16\n", - " status = solver.solve(model)\n", - "\n", - " print(\"Statistics\")\n", - " print(\" - status =\", solver.status_name(status))\n", - " print(\" - optimal solution =\", solver.objective_value)\n", - " print(\" - wall time : %f s\" % solver.wall_time)\n", + " solver.solve(model)\n", "\n", "\n", "def main(argv: Sequence[str]) -> None:\n", diff --git a/examples/notebook/examples/tasks_and_workers_assignment_sat.ipynb b/examples/notebook/examples/tasks_and_workers_assignment_sat.ipynb index b436483c2e0..2bfdd2250f9 100644 --- a/examples/notebook/examples/tasks_and_workers_assignment_sat.ipynb +++ b/examples/notebook/examples/tasks_and_workers_assignment_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -102,7 +102,7 @@ " self.__solution_count += 1\n", "\n", "\n", - "def tasks_and_workers_assignment_sat():\n", + "def tasks_and_workers_assignment_sat() -> None:\n", " \"\"\"solve the assignment problem.\"\"\"\n", " model = cp_model.CpModel()\n", "\n", diff --git a/examples/notebook/examples/test_scheduling_sat.ipynb b/examples/notebook/examples/test_scheduling_sat.ipynb new file mode 100644 index 00000000000..fd94dc99cd0 --- /dev/null +++ b/examples/notebook/examples/test_scheduling_sat.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "google", + "metadata": {}, + "source": [ + "##### Copyright 2024 Google LLC." + ] + }, + { + "cell_type": "markdown", + "id": "apache", + "metadata": {}, + "source": [ + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License.\n" + ] + }, + { + "cell_type": "markdown", + "id": "basename", + "metadata": {}, + "source": [ + "# test_scheduling_sat" + ] + }, + { + "cell_type": "markdown", + "id": "link", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "doc", + "metadata": {}, + "source": [ + "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install ortools" + ] + }, + { + "cell_type": "markdown", + "id": "description", + "metadata": {}, + "source": [ + "\n", + "Solves a test scheduling problem.\n", + "\n", + "Tests must be run by an operator. Tests have a duration and a power consumption.\n", + "\n", + "Operators draw power from power supplies. The mapping between operators and\n", + "power supplies is given.\n", + "\n", + "Power supplies have a maximum power they can deliver.\n", + "\n", + "Can we schedule the tests so that the power consumption of each power supply is\n", + "always below its maximum power, and the total makespan is minimized?\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "code", + "metadata": {}, + "outputs": [], + "source": [ + "from collections.abc import Sequence\n", + "import io\n", + "from typing import Dict, Tuple\n", + "\n", + "from ortools.sat.colab import flags\n", + "import pandas as pd\n", + "\n", + "from google.protobuf import text_format\n", + "from ortools.sat.python import cp_model\n", + "\n", + "\n", + "_PARAMS = flags.define_string(\n", + " \"params\",\n", + " \"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45\",\n", + " \"Sat solver parameters.\",\n", + ")\n", + "\n", + "\n", + "def build_data() -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:\n", + " \"\"\"Build the data frame.\"\"\"\n", + " tests_str = \"\"\"\n", + " Name Operator TestTime AveragePower\n", + " T1 O1 300 200\n", + " T2 O1 150 40\n", + " T3 O2 100 65\n", + " T4 O2 250 150\n", + " T5 O3 210 140\n", + " \"\"\"\n", + "\n", + " operators_str = \"\"\"\n", + " Operator Supply\n", + " O1 S1\n", + " O2 S2\n", + " O3 S2\n", + " \"\"\"\n", + "\n", + " supplies_str = \"\"\"\n", + " Supply MaxAllowedPower\n", + " S1 230\n", + " S2 210\n", + " \"\"\"\n", + "\n", + " tests_data = pd.read_table(io.StringIO(tests_str), sep=r\"\\s+\")\n", + " operators_data = pd.read_table(io.StringIO(operators_str), sep=r\"\\s+\")\n", + " supplies_data = pd.read_table(io.StringIO(supplies_str), sep=r\"\\s+\")\n", + "\n", + " return (tests_data, operators_data, supplies_data)\n", + "\n", + "\n", + "def solve(\n", + " tests_data: pd.DataFrame,\n", + " operator_data: pd.DataFrame,\n", + " supplies_data: pd.DataFrame,\n", + ") -> None:\n", + " \"\"\"Solve the scheduling of tests problem.\"\"\"\n", + "\n", + " # Parses data.\n", + " operator_to_supply: Dict[str, str] = {}\n", + " for _, row in operator_data.iterrows():\n", + " operator_to_supply[row[\"Operator\"]] = row[\"Supply\"]\n", + "\n", + " supply_to_max_power: Dict[str, int] = {}\n", + " for _, row in supplies_data.iterrows():\n", + " supply_to_max_power[row[\"Supply\"]] = row[\"MaxAllowedPower\"]\n", + "\n", + " horizon = tests_data[\"TestTime\"].sum()\n", + "\n", + " # OR-Tools model.\n", + " model = cp_model.CpModel()\n", + "\n", + " # Create containers.\n", + " tests_per_supply: Dict[str, Tuple[list[cp_model.IntervalVar], list[int]]] = {}\n", + " test_supply: Dict[str, str] = {}\n", + " test_starts: Dict[str, cp_model.IntVar] = {}\n", + " test_durations: Dict[str, int] = {}\n", + " test_powers: Dict[str, int] = {}\n", + " all_ends = []\n", + "\n", + " # Creates intervals.\n", + " for _, row in tests_data.iterrows():\n", + " name: str = row[\"Name\"]\n", + " operator: str = row[\"Operator\"]\n", + " test_time: int = row[\"TestTime\"]\n", + " average_power: int = row[\"AveragePower\"]\n", + " supply: str = operator_to_supply[operator]\n", + "\n", + " start = model.new_int_var(0, horizon - test_time, f\"start_{name}\")\n", + " interval = model.new_fixed_size_interval_var(\n", + " start, test_time, f\"interval_{name}\"\n", + " )\n", + "\n", + " # Bookkeeping.\n", + " test_starts[name] = start\n", + " test_durations[name] = test_time\n", + " test_powers[name] = average_power\n", + " test_supply[name] = supply\n", + " if supply not in tests_per_supply.keys():\n", + " tests_per_supply[supply] = ([], [])\n", + " tests_per_supply[supply][0].append(interval)\n", + " tests_per_supply[supply][1].append(average_power)\n", + " all_ends.append(start + test_time)\n", + "\n", + " # Create supply cumulative constraints.\n", + " for supply, (intervals, demands) in tests_per_supply.items():\n", + " model.add_cumulative(intervals, demands, supply_to_max_power[supply])\n", + "\n", + " # Objective.\n", + " makespan = model.new_int_var(0, horizon, \"makespan\")\n", + " for end in all_ends:\n", + " model.add(makespan >= end)\n", + " model.minimize(makespan)\n", + "\n", + " # Solve model.\n", + " solver = cp_model.CpSolver()\n", + " if _PARAMS.value:\n", + " text_format.Parse(_PARAMS.value, solver.parameters)\n", + " status = solver.solve(model)\n", + "\n", + " # Report solution.\n", + " if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n", + " print(f\"Makespan = {solver.value(makespan)}\")\n", + " for name, start in test_starts.items():\n", + " print(\n", + " f\"{name}: start:{solver.value(start)} duration:{test_durations[name]}\"\n", + " f\" power:{test_powers[name]} on supply {test_supply[name]}\"\n", + " )\n", + "\n", + "\n", + "def main(argv: Sequence[str]) -> None:\n", + " \"\"\"Builds the data and solve the scheduling problem.\"\"\"\n", + " if len(argv) > 1:\n", + " raise app.UsageError(\"Too many command-line arguments.\")\n", + "\n", + " tests_data, operators_data, supplies_data = build_data()\n", + " print(\"Tests data\")\n", + " print(tests_data)\n", + " print()\n", + " print(\"Operators data\")\n", + " print(operators_data)\n", + " print()\n", + " print(\"Supplies data\")\n", + " print(supplies_data)\n", + "\n", + " solve(tests_data, operators_data, supplies_data)\n", + "\n", + "\n", + "main()\n", + "\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebook/examples/transit_time.ipynb b/examples/notebook/examples/transit_time.ipynb index 580e4f09de8..0395ce33268 100644 --- a/examples/notebook/examples/transit_time.ipynb +++ b/examples/notebook/examples/transit_time.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/tsp_sat.ipynb b/examples/notebook/examples/tsp_sat.ipynb index 3917b3e864d..8704e021603 100644 --- a/examples/notebook/examples/tsp_sat.ipynb +++ b/examples/notebook/examples/tsp_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/examples/vendor_scheduling_sat.ipynb b/examples/notebook/examples/vendor_scheduling_sat.ipynb index 8c71c011a3e..f23d5dc94b3 100644 --- a/examples/notebook/examples/vendor_scheduling_sat.ipynb +++ b/examples/notebook/examples/vendor_scheduling_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -131,7 +131,7 @@ " return self.__solution_count\n", "\n", "\n", - "def vendor_scheduling_sat():\n", + "def vendor_scheduling_sat() -> None:\n", " \"\"\"Create the shift scheduling model and solve it.\"\"\"\n", " # Create the model.\n", " model = cp_model.CpModel()\n", diff --git a/examples/notebook/examples/wedding_optimal_chart_sat.ipynb b/examples/notebook/examples/wedding_optimal_chart_sat.ipynb index 06abd55693a..c27f1c2c783 100644 --- a/examples/notebook/examples/wedding_optimal_chart_sat.ipynb +++ b/examples/notebook/examples/wedding_optimal_chart_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -198,7 +198,7 @@ " return num_tables, table_capacity, min_known_neighbors, connections, names\n", "\n", "\n", - "def solve_with_discrete_model():\n", + "def solve_with_discrete_model() -> None:\n", " \"\"\"Discrete approach.\"\"\"\n", " num_tables, table_capacity, min_known_neighbors, connections, names = build_data()\n", "\n", diff --git a/examples/notebook/examples/weighted_latency_problem_sat.ipynb b/examples/notebook/examples/weighted_latency_problem_sat.ipynb index 270789e36ed..5267fe8663a 100644 --- a/examples/notebook/examples/weighted_latency_problem_sat.ipynb +++ b/examples/notebook/examples/weighted_latency_problem_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -122,7 +122,7 @@ " return x, y, profits\n", "\n", "\n", - "def solve_with_cp_sat(x, y, profits):\n", + "def solve_with_cp_sat(x, y, profits) -> None:\n", " \"\"\"Solves the problem with the CP-SAT solver.\"\"\"\n", " model = cp_model.CpModel()\n", "\n", diff --git a/examples/notebook/examples/zebra_sat.ipynb b/examples/notebook/examples/zebra_sat.ipynb index acc02dcddcd..3b784974789 100644 --- a/examples/notebook/examples/zebra_sat.ipynb +++ b/examples/notebook/examples/zebra_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/graph/assignment_linear_sum_assignment.ipynb b/examples/notebook/graph/assignment_linear_sum_assignment.ipynb index 4d9fcd76457..c0edfe84441 100644 --- a/examples/notebook/graph/assignment_linear_sum_assignment.ipynb +++ b/examples/notebook/graph/assignment_linear_sum_assignment.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/graph/assignment_min_flow.ipynb b/examples/notebook/graph/assignment_min_flow.ipynb index 65b5fe80663..e8a998a10d0 100644 --- a/examples/notebook/graph/assignment_min_flow.ipynb +++ b/examples/notebook/graph/assignment_min_flow.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/graph/balance_min_flow.ipynb b/examples/notebook/graph/balance_min_flow.ipynb index 4f4610d7a1d..b1c9d3fbd9a 100644 --- a/examples/notebook/graph/balance_min_flow.ipynb +++ b/examples/notebook/graph/balance_min_flow.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/graph/simple_max_flow_program.ipynb b/examples/notebook/graph/simple_max_flow_program.ipynb index 7fd3732f68f..565b87b392f 100644 --- a/examples/notebook/graph/simple_max_flow_program.ipynb +++ b/examples/notebook/graph/simple_max_flow_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/graph/simple_min_cost_flow_program.ipynb b/examples/notebook/graph/simple_min_cost_flow_program.ipynb index ab10714e41e..0f754965ee6 100644 --- a/examples/notebook/graph/simple_min_cost_flow_program.ipynb +++ b/examples/notebook/graph/simple_min_cost_flow_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/assignment_groups_mip.ipynb b/examples/notebook/linear_solver/assignment_groups_mip.ipynb index cf4e28298f7..e2c72d3bd54 100644 --- a/examples/notebook/linear_solver/assignment_groups_mip.ipynb +++ b/examples/notebook/linear_solver/assignment_groups_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/assignment_mb.ipynb b/examples/notebook/linear_solver/assignment_mb.ipynb index 34b7ac0a9d2..a6808aeacdc 100644 --- a/examples/notebook/linear_solver/assignment_mb.ipynb +++ b/examples/notebook/linear_solver/assignment_mb.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/assignment_mip.ipynb b/examples/notebook/linear_solver/assignment_mip.ipynb index 6540ed8f047..f814693fa91 100644 --- a/examples/notebook/linear_solver/assignment_mip.ipynb +++ b/examples/notebook/linear_solver/assignment_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/assignment_task_sizes_mip.ipynb b/examples/notebook/linear_solver/assignment_task_sizes_mip.ipynb index 2b1f0b69f0d..3bfb3141f4b 100644 --- a/examples/notebook/linear_solver/assignment_task_sizes_mip.ipynb +++ b/examples/notebook/linear_solver/assignment_task_sizes_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/assignment_teams_mip.ipynb b/examples/notebook/linear_solver/assignment_teams_mip.ipynb index 75911a8447c..5726a5d5efb 100644 --- a/examples/notebook/linear_solver/assignment_teams_mip.ipynb +++ b/examples/notebook/linear_solver/assignment_teams_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/basic_example.ipynb b/examples/notebook/linear_solver/basic_example.ipynb index 5d9f00fd072..536f091e80d 100644 --- a/examples/notebook/linear_solver/basic_example.ipynb +++ b/examples/notebook/linear_solver/basic_example.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/bin_packing_mb.ipynb b/examples/notebook/linear_solver/bin_packing_mb.ipynb index 1f28e0fa63c..e848fa7fe92 100644 --- a/examples/notebook/linear_solver/bin_packing_mb.ipynb +++ b/examples/notebook/linear_solver/bin_packing_mb.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/bin_packing_mip.ipynb b/examples/notebook/linear_solver/bin_packing_mip.ipynb index 1b43da8711d..58fc818c3d4 100644 --- a/examples/notebook/linear_solver/bin_packing_mip.ipynb +++ b/examples/notebook/linear_solver/bin_packing_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/clone_model_mb.ipynb b/examples/notebook/linear_solver/clone_model_mb.ipynb index 17478cf4ab7..350ecc4c4bd 100644 --- a/examples/notebook/linear_solver/clone_model_mb.ipynb +++ b/examples/notebook/linear_solver/clone_model_mb.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/integer_programming_example.ipynb b/examples/notebook/linear_solver/integer_programming_example.ipynb index 8db2b7c3364..7dd5b31fdb8 100644 --- a/examples/notebook/linear_solver/integer_programming_example.ipynb +++ b/examples/notebook/linear_solver/integer_programming_example.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/linear_programming_example.ipynb b/examples/notebook/linear_solver/linear_programming_example.ipynb index 00cb97c3b83..7ee26107cdc 100644 --- a/examples/notebook/linear_solver/linear_programming_example.ipynb +++ b/examples/notebook/linear_solver/linear_programming_example.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/mip_var_array.ipynb b/examples/notebook/linear_solver/mip_var_array.ipynb index 4f1d743c2d1..f0f8664fddf 100644 --- a/examples/notebook/linear_solver/mip_var_array.ipynb +++ b/examples/notebook/linear_solver/mip_var_array.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb b/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb index e5fc31b7d70..8e8125b9afd 100644 --- a/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb +++ b/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/simple_lp_program.ipynb b/examples/notebook/linear_solver/simple_lp_program.ipynb index 3094e7382e8..4775ecdfbd1 100644 --- a/examples/notebook/linear_solver/simple_lp_program.ipynb +++ b/examples/notebook/linear_solver/simple_lp_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/simple_lp_program_mb.ipynb b/examples/notebook/linear_solver/simple_lp_program_mb.ipynb index 2d434121d86..bee79cf01f5 100644 --- a/examples/notebook/linear_solver/simple_lp_program_mb.ipynb +++ b/examples/notebook/linear_solver/simple_lp_program_mb.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/simple_mip_program.ipynb b/examples/notebook/linear_solver/simple_mip_program.ipynb index f72fc008038..b3c6b8ad242 100644 --- a/examples/notebook/linear_solver/simple_mip_program.ipynb +++ b/examples/notebook/linear_solver/simple_mip_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/simple_mip_program_mb.ipynb b/examples/notebook/linear_solver/simple_mip_program_mb.ipynb index ef4151b6368..9fb3f76d991 100644 --- a/examples/notebook/linear_solver/simple_mip_program_mb.ipynb +++ b/examples/notebook/linear_solver/simple_mip_program_mb.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/linear_solver/stigler_diet.ipynb b/examples/notebook/linear_solver/stigler_diet.ipynb index 7acaf3b7079..d57c2dafa54 100644 --- a/examples/notebook/linear_solver/stigler_diet.ipynb +++ b/examples/notebook/linear_solver/stigler_diet.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/pdlp/simple_pdlp_program.ipynb b/examples/notebook/pdlp/simple_pdlp_program.ipynb index 7a0464fed0f..4cb305fd493 100644 --- a/examples/notebook/pdlp/simple_pdlp_program.ipynb +++ b/examples/notebook/pdlp/simple_pdlp_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/cvrp_reload.ipynb b/examples/notebook/routing/cvrp_reload.ipynb index 1d1496bd8fd..5be46a21d30 100644 --- a/examples/notebook/routing/cvrp_reload.ipynb +++ b/examples/notebook/routing/cvrp_reload.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/cvrptw.ipynb b/examples/notebook/routing/cvrptw.ipynb new file mode 100644 index 00000000000..7bfc83042c0 --- /dev/null +++ b/examples/notebook/routing/cvrptw.ipynb @@ -0,0 +1,334 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "google", + "metadata": {}, + "source": [ + "##### Copyright 2024 Google LLC." + ] + }, + { + "cell_type": "markdown", + "id": "apache", + "metadata": {}, + "source": [ + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License.\n" + ] + }, + { + "cell_type": "markdown", + "id": "basename", + "metadata": {}, + "source": [ + "# cvrptw" + ] + }, + { + "cell_type": "markdown", + "id": "link", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "doc", + "metadata": {}, + "source": [ + "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install ortools" + ] + }, + { + "cell_type": "markdown", + "id": "description", + "metadata": {}, + "source": [ + "\n", + "Capacited Vehicles Routing Problem with Time Windows (CVRPTW).\n", + "\n", + "This is a sample using the routing library python wrapper to solve a VRP\n", + "problem.\n", + "A description of the problem can be found here:\n", + "http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n", + "\n", + "Distances are in meters.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "code", + "metadata": {}, + "outputs": [], + "source": [ + "from ortools.routing import enums_pb2\n", + "from ortools.routing import pywraprouting\n", + "\n", + "FirstSolutionStrategy = enums_pb2.FirstSolutionStrategy\n", + "LocalSearchMetaheuristic = enums_pb2.LocalSearchMetaheuristic\n", + "RoutingSearchStatus = enums_pb2.RoutingSearchStatus\n", + "\n", + "\n", + "def create_data_model():\n", + " \"\"\"Stores the data for the problem.\"\"\"\n", + " data = {}\n", + " data[\"distance_matrix\"] = [\n", + " # fmt: off\n", + " [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n", + " [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n", + " [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n", + " [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n", + " [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n", + " [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n", + " [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n", + " [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n", + " [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n", + " [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n", + " [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n", + " [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n", + " [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n", + " [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n", + " [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n", + " [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n", + " [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n", + " # fmt: on\n", + " ]\n", + " data[\"time_matrix\"] = [\n", + " [0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7],\n", + " [6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14],\n", + " [9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9],\n", + " [8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16],\n", + " [7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14],\n", + " [3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8],\n", + " [6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5],\n", + " [2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10],\n", + " [3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6],\n", + " [2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5],\n", + " [6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4],\n", + " [6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10],\n", + " [4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8],\n", + " [4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6],\n", + " [5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2],\n", + " [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9],\n", + " [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0],\n", + " ]\n", + " data[\"time_windows\"] = [\n", + " (0, 30), # depot\n", + " (7, 12), # 1\n", + " (10, 15), # 2\n", + " (16, 18), # 3\n", + " (10, 13), # 4\n", + " (0, 5), # 5\n", + " (5, 10), # 6\n", + " (0, 4), # 7\n", + " (5, 10), # 8\n", + " (0, 3), # 9\n", + " (10, 16), # 10\n", + " (10, 15), # 11\n", + " (0, 5), # 12\n", + " (5, 10), # 13\n", + " (7, 8), # 14\n", + " (10, 15), # 15\n", + " (11, 15), # 16\n", + " ]\n", + " assert len(data[\"distance_matrix\"]) == len(data[\"time_matrix\"])\n", + " assert len(data[\"time_matrix\"]) == len(data[\"time_windows\"])\n", + " data[\"demands\"] = [0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8]\n", + " assert len(data[\"distance_matrix\"]) == len(data[\"demands\"])\n", + " data[\"vehicle_capacities\"] = [15, 15, 15, 15]\n", + " data[\"num_vehicles\"] = len(data[\"vehicle_capacities\"])\n", + " data[\"depot\"] = 0\n", + " return data\n", + "\n", + "\n", + "def print_solution(manager, routing, solution):\n", + " \"\"\"Prints solution on console.\"\"\"\n", + " status = routing.status()\n", + " print(f\"Status: {RoutingSearchStatus.Value.Name(status)}\")\n", + " if (\n", + " status != RoutingSearchStatus.ROUTING_OPTIMAL\n", + " and status != RoutingSearchStatus.ROUTING_SUCCESS\n", + " ):\n", + " print(\"No solution found!\")\n", + " return\n", + " print(f\"Objective: {solution.ObjectiveValue()}\")\n", + " time_dimension = routing.GetDimensionOrDie(\"Time\")\n", + " capacity_dimension = routing.GetDimensionOrDie(\"Capacity\")\n", + " total_distance = 0\n", + " total_time = 0\n", + " total_load = 0\n", + " for vehicle_id in range(manager.GetNumberOfVehicles()):\n", + " index = routing.Start(vehicle_id)\n", + " plan_output = f\"Route for vehicle {vehicle_id}:\\n\"\n", + " route_distance = 0\n", + " while not routing.IsEnd(index):\n", + " time_var = time_dimension.CumulVar(index)\n", + " capacity_var = capacity_dimension.CumulVar(index)\n", + " plan_output += (\n", + " f\"Node_{manager.IndexToNode(index)}\"\n", + " f\" TW:[{time_var.Min()},{time_var.Max()}]\"\n", + " f\" Time({solution.Min(time_var)},{solution.Max(time_var)})\"\n", + " f\" Load({solution.Value(capacity_var)}/{capacity_var.Max()})\"\n", + " \" -> \"\n", + " )\n", + " previous_index = index\n", + " index = solution.Value(routing.NextVar(index))\n", + " route_distance += routing.GetArcCostForVehicle(\n", + " previous_index, index, vehicle_id\n", + " )\n", + " time_var = time_dimension.CumulVar(index)\n", + " capacity_var = capacity_dimension.CumulVar(index)\n", + " plan_output += (\n", + " f\"Node_{manager.IndexToNode(index)}\"\n", + " f\" Time({solution.Min(time_var)},{solution.Max(time_var)})\"\n", + " f\" Load({solution.Value(capacity_var)}/{capacity_var.Max()})\"\n", + " \"\\n\"\n", + " )\n", + " plan_output += f\"Distance of the route: {route_distance}m\\n\"\n", + " plan_output += f\"Time of the route: {solution.Min(time_var)}min\\n\"\n", + " plan_output += f\"Load of the route: {solution.Value(capacity_var)}\\n\"\n", + " print(plan_output)\n", + " total_distance += route_distance\n", + " total_time += solution.Min(time_var)\n", + " total_load += solution.Value(capacity_var)\n", + " print(f\"Total distance of all routes: {total_distance}m\")\n", + " print(f\"Total time of all routes: {total_time}min\")\n", + " print(f\"Total load of all routes: {total_load}\")\n", + "\n", + "\n", + "def main():\n", + " \"\"\"Entry point of the program.\"\"\"\n", + " # Instantiate the data problem.\n", + " data = create_data_model()\n", + "\n", + " # Create the routing index manager.\n", + " manager = pywraprouting.RoutingIndexManager(\n", + " len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n", + " )\n", + "\n", + " # Create Routing Model.\n", + " routing = pywraprouting.RoutingModel(manager)\n", + "\n", + " # Create and register a distance transit callback.\n", + " def distance_callback(from_index, to_index):\n", + " \"\"\"Returns the distance between the two nodes.\"\"\"\n", + " # Convert from routing variable Index to distance matrix NodeIndex.\n", + " from_node = manager.IndexToNode(from_index)\n", + " to_node = manager.IndexToNode(to_index)\n", + " return data[\"distance_matrix\"][from_node][to_node]\n", + "\n", + " distance_callback_index = routing.RegisterTransitCallback(distance_callback)\n", + "\n", + " # Define cost of each arc.\n", + " routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index)\n", + "\n", + " # Add Time Windows constraint.\n", + " def time_callback(from_index, to_index):\n", + " \"\"\"Returns the travel time between the two nodes.\"\"\"\n", + " # Convert from routing variable Index to time matrix NodeIndex.\n", + " from_node = manager.IndexToNode(from_index)\n", + " to_node = manager.IndexToNode(to_index)\n", + " return data[\"time_matrix\"][from_node][to_node]\n", + "\n", + " time_callback_index = routing.RegisterTransitCallback(time_callback)\n", + " routing.AddDimension(\n", + " time_callback_index,\n", + " 30, # allow waiting time\n", + " 30, # maximum time per vehicle\n", + " False, # Don't force start cumul to zero.\n", + " \"Time\",\n", + " )\n", + " time_dimension = routing.GetDimensionOrDie(\"Time\")\n", + " # Add time window constraints for each location except depot.\n", + " for location_idx, time_window in enumerate(data[\"time_windows\"]):\n", + " if location_idx == data[\"depot\"]:\n", + " continue\n", + " index = manager.NodeToIndex(location_idx)\n", + " time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])\n", + " # Add time window constraints for each vehicle start node.\n", + " depot_idx = data[\"depot\"]\n", + " for vehicle_id in range(data[\"num_vehicles\"]):\n", + " index = routing.Start(vehicle_id)\n", + " time_dimension.CumulVar(index).SetRange(\n", + " data[\"time_windows\"][depot_idx][0], data[\"time_windows\"][depot_idx][1]\n", + " )\n", + "\n", + " # Instantiate route start and end times to produce feasible times.\n", + " for i in range(data[\"num_vehicles\"]):\n", + " routing.AddVariableMinimizedByFinalizer(\n", + " time_dimension.CumulVar(routing.Start(i))\n", + " )\n", + " routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(i)))\n", + "\n", + " # Add Capacity constraint.\n", + " def demand_callback(from_index):\n", + " \"\"\"Returns the demand of the node.\"\"\"\n", + " # Convert from routing variable Index to demands NodeIndex.\n", + " from_node = manager.IndexToNode(from_index)\n", + " return data[\"demands\"][from_node]\n", + "\n", + " demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)\n", + " routing.AddDimensionWithVehicleCapacity(\n", + " demand_callback_index,\n", + " 0, # null capacity slack\n", + " data[\"vehicle_capacities\"], # vehicle maximum capacities\n", + " True, # start cumul to zero\n", + " \"Capacity\",\n", + " )\n", + "\n", + " # Setting first solution heuristic.\n", + " search_parameters = pywraprouting.DefaultRoutingSearchParameters()\n", + " search_parameters.first_solution_strategy = (\n", + " FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n", + " )\n", + " search_parameters.local_search_metaheuristic = (\n", + " LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n", + " )\n", + " search_parameters.time_limit.FromSeconds(3)\n", + "\n", + " # Solve the problem.\n", + " solution = routing.SolveWithParameters(search_parameters)\n", + "\n", + " # Print solution on console.\n", + " print_solution(manager, routing, solution)\n", + "\n", + "\n", + "main()\n", + "\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebook/routing/cvrptw_break.ipynb b/examples/notebook/routing/cvrptw_break.ipynb index 906cd6d94a4..98346fe349a 100644 --- a/examples/notebook/routing/cvrptw_break.ipynb +++ b/examples/notebook/routing/cvrptw_break.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/simple_routing_program.ipynb b/examples/notebook/routing/simple_routing_program.ipynb index 0727dd0bc9f..d6e2c130566 100644 --- a/examples/notebook/routing/simple_routing_program.ipynb +++ b/examples/notebook/routing/simple_routing_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/tsp.ipynb b/examples/notebook/routing/tsp.ipynb index 1342a267354..cc22b5771c0 100644 --- a/examples/notebook/routing/tsp.ipynb +++ b/examples/notebook/routing/tsp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/tsp_circuit_board.ipynb b/examples/notebook/routing/tsp_circuit_board.ipynb index 31cf1d6d3b6..c81dcc9f663 100644 --- a/examples/notebook/routing/tsp_circuit_board.ipynb +++ b/examples/notebook/routing/tsp_circuit_board.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -176,8 +176,8 @@ " index = solution.Value(routing.NextVar(index))\n", " route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)\n", " plan_output += f\" {manager.IndexToNode(index)}\\n\"\n", + " plan_output += f\"Route distance: {route_distance}mm\\n\"\n", " print(plan_output)\n", - " plan_output += f\"Objective: {route_distance}m\\n\"\n", "\n", "\n", "def main():\n", diff --git a/examples/notebook/routing/tsp_cities.ipynb b/examples/notebook/routing/tsp_cities.ipynb index 8ea139d0f6e..c83aee5269e 100644 --- a/examples/notebook/routing/tsp_cities.ipynb +++ b/examples/notebook/routing/tsp_cities.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -122,8 +122,8 @@ " index = solution.Value(routing.NextVar(index))\n", " route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)\n", " plan_output += f\" {manager.IndexToNode(index)}\\n\"\n", - " print(plan_output)\n", " plan_output += f\"Route distance: {route_distance}miles\\n\"\n", + " print(plan_output)\n", "\n", "\n", "def main():\n", diff --git a/examples/notebook/routing/tsp_distance_matrix.ipynb b/examples/notebook/routing/tsp_distance_matrix.ipynb index 65b22e735e9..178dff030c2 100644 --- a/examples/notebook/routing/tsp_distance_matrix.ipynb +++ b/examples/notebook/routing/tsp_distance_matrix.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/tsp_legacy.ipynb b/examples/notebook/routing/tsp_legacy.ipynb index 3eed9ab6dc1..3552078a742 100644 --- a/examples/notebook/routing/tsp_legacy.ipynb +++ b/examples/notebook/routing/tsp_legacy.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp.ipynb b/examples/notebook/routing/vrp.ipynb index c8d563cc8a1..fdd59d2766f 100644 --- a/examples/notebook/routing/vrp.ipynb +++ b/examples/notebook/routing/vrp.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -157,6 +157,7 @@ " print(f\"Total Distance of all routes: {total_distance}m\")\n", "\n", "\n", + "\n", "def main():\n", " \"\"\"Entry point of the program.\"\"\"\n", " # Instantiate the data problem.\n", diff --git a/examples/notebook/routing/vrp_breaks.ipynb b/examples/notebook/routing/vrp_breaks.ipynb index 2dee1c38fdf..d7bc822762a 100644 --- a/examples/notebook/routing/vrp_breaks.ipynb +++ b/examples/notebook/routing/vrp_breaks.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_breaks_from_start.ipynb b/examples/notebook/routing/vrp_breaks_from_start.ipynb index 4157245441d..786778b8dc2 100644 --- a/examples/notebook/routing/vrp_breaks_from_start.ipynb +++ b/examples/notebook/routing/vrp_breaks_from_start.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_capacity.ipynb b/examples/notebook/routing/vrp_capacity.ipynb index 37977e3398a..1497991e75b 100644 --- a/examples/notebook/routing/vrp_capacity.ipynb +++ b/examples/notebook/routing/vrp_capacity.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_drop_nodes.ipynb b/examples/notebook/routing/vrp_drop_nodes.ipynb index 8d7d936106f..badc9b1d1eb 100644 --- a/examples/notebook/routing/vrp_drop_nodes.ipynb +++ b/examples/notebook/routing/vrp_drop_nodes.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_global_span.ipynb b/examples/notebook/routing/vrp_global_span.ipynb index 80b54f48c56..214f3b8414c 100644 --- a/examples/notebook/routing/vrp_global_span.ipynb +++ b/examples/notebook/routing/vrp_global_span.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -146,6 +146,7 @@ " print(f\"Maximum of the route distances: {max_route_distance}m\")\n", "\n", "\n", + "\n", "def main():\n", " \"\"\"Entry point of the program.\"\"\"\n", " # Instantiate the data problem.\n", diff --git a/examples/notebook/routing/vrp_initial_routes.ipynb b/examples/notebook/routing/vrp_initial_routes.ipynb index 69b301a8c51..44502b587be 100644 --- a/examples/notebook/routing/vrp_initial_routes.ipynb +++ b/examples/notebook/routing/vrp_initial_routes.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -146,6 +146,7 @@ " print(f\"Maximum of the route distances: {max_route_distance}m\")\n", "\n", "\n", + "\n", "def main():\n", " \"\"\"Solve the CVRP problem.\"\"\"\n", " # Instantiate the data problem.\n", diff --git a/examples/notebook/routing/vrp_items_to_deliver.ipynb b/examples/notebook/routing/vrp_items_to_deliver.ipynb index 2c83bdba8cc..3879d606e47 100644 --- a/examples/notebook/routing/vrp_items_to_deliver.ipynb +++ b/examples/notebook/routing/vrp_items_to_deliver.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_node_max.ipynb b/examples/notebook/routing/vrp_node_max.ipynb index d3eb2197dda..b354ed5738e 100644 --- a/examples/notebook/routing/vrp_node_max.ipynb +++ b/examples/notebook/routing/vrp_node_max.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -140,6 +140,7 @@ " return data\n", "\n", "\n", + "\n", "def print_solution(data, manager, routing, solution):\n", " \"\"\"Prints solution on console.\"\"\"\n", " print(f\"Objective: {solution.ObjectiveValue()}\")\n", @@ -180,6 +181,7 @@ " print(f\"Maximum of the route distances: {max_route_distance}m\")\n", "\n", "\n", + "\n", "def main():\n", " \"\"\"Solve the CVRP problem.\"\"\"\n", " # Instantiate the data problem.\n", diff --git a/examples/notebook/routing/vrp_nodes_indices.ipynb b/examples/notebook/routing/vrp_nodes_indices.ipynb index b06627757de..4fc2e0b19e7 100644 --- a/examples/notebook/routing/vrp_nodes_indices.ipynb +++ b/examples/notebook/routing/vrp_nodes_indices.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_pickup_delivery.ipynb b/examples/notebook/routing/vrp_pickup_delivery.ipynb index a16114663c9..0ced82ad936 100644 --- a/examples/notebook/routing/vrp_pickup_delivery.ipynb +++ b/examples/notebook/routing/vrp_pickup_delivery.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_pickup_delivery_fifo.ipynb b/examples/notebook/routing/vrp_pickup_delivery_fifo.ipynb index 2ac042ece94..39dacb01edc 100644 --- a/examples/notebook/routing/vrp_pickup_delivery_fifo.ipynb +++ b/examples/notebook/routing/vrp_pickup_delivery_fifo.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_pickup_delivery_lifo.ipynb b/examples/notebook/routing/vrp_pickup_delivery_lifo.ipynb index 4ec9f0c79f1..9ff8f2372cf 100644 --- a/examples/notebook/routing/vrp_pickup_delivery_lifo.ipynb +++ b/examples/notebook/routing/vrp_pickup_delivery_lifo.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_resources.ipynb b/examples/notebook/routing/vrp_resources.ipynb index 4c256965705..a09f5dd7c8e 100644 --- a/examples/notebook/routing/vrp_resources.ipynb +++ b/examples/notebook/routing/vrp_resources.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_solution_callback.ipynb b/examples/notebook/routing/vrp_solution_callback.ipynb index ca67cb74afe..652f99476be 100644 --- a/examples/notebook/routing/vrp_solution_callback.ipynb +++ b/examples/notebook/routing/vrp_solution_callback.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_starts_ends.ipynb b/examples/notebook/routing/vrp_starts_ends.ipynb index b7a977ddf85..3f3105e7e59 100644 --- a/examples/notebook/routing/vrp_starts_ends.ipynb +++ b/examples/notebook/routing/vrp_starts_ends.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_time_windows.ipynb b/examples/notebook/routing/vrp_time_windows.ipynb index d2e81127374..f2697ff1114 100644 --- a/examples/notebook/routing/vrp_time_windows.ipynb +++ b/examples/notebook/routing/vrp_time_windows.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_time_windows_per_vehicles.ipynb b/examples/notebook/routing/vrp_time_windows_per_vehicles.ipynb index 6d8053848c0..0fa36280293 100644 --- a/examples/notebook/routing/vrp_time_windows_per_vehicles.ipynb +++ b/examples/notebook/routing/vrp_time_windows_per_vehicles.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_tokens.ipynb b/examples/notebook/routing/vrp_tokens.ipynb index a533aabeef9..b0a8c61f319 100644 --- a/examples/notebook/routing/vrp_tokens.ipynb +++ b/examples/notebook/routing/vrp_tokens.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrp_with_time_limit.ipynb b/examples/notebook/routing/vrp_with_time_limit.ipynb index d1d32de9e9c..5a674697acc 100644 --- a/examples/notebook/routing/vrp_with_time_limit.ipynb +++ b/examples/notebook/routing/vrp_with_time_limit.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/routing/vrptw_store_solution_data.ipynb b/examples/notebook/routing/vrptw_store_solution_data.ipynb index 8dfc7c98a44..1c99d42d148 100644 --- a/examples/notebook/routing/vrptw_store_solution_data.ipynb +++ b/examples/notebook/routing/vrptw_store_solution_data.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/all_different_except_zero_sample_sat.ipynb b/examples/notebook/sat/all_different_except_zero_sample_sat.ipynb index c4a826ec56a..deb2a3f1bb3 100644 --- a/examples/notebook/sat/all_different_except_zero_sample_sat.ipynb +++ b/examples/notebook/sat/all_different_except_zero_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/assignment_groups_sat.ipynb b/examples/notebook/sat/assignment_groups_sat.ipynb index 69798db58ac..5bebc34dcf7 100644 --- a/examples/notebook/sat/assignment_groups_sat.ipynb +++ b/examples/notebook/sat/assignment_groups_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/assignment_sat.ipynb b/examples/notebook/sat/assignment_sat.ipynb index 4bff58240b6..012e9219af3 100644 --- a/examples/notebook/sat/assignment_sat.ipynb +++ b/examples/notebook/sat/assignment_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/assignment_task_sizes_sat.ipynb b/examples/notebook/sat/assignment_task_sizes_sat.ipynb index e7a0ff6d7a1..aa2c528af51 100644 --- a/examples/notebook/sat/assignment_task_sizes_sat.ipynb +++ b/examples/notebook/sat/assignment_task_sizes_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/assignment_teams_sat.ipynb b/examples/notebook/sat/assignment_teams_sat.ipynb index 63bf3749f1e..5e50e35875b 100644 --- a/examples/notebook/sat/assignment_teams_sat.ipynb +++ b/examples/notebook/sat/assignment_teams_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/assumptions_sample_sat.ipynb b/examples/notebook/sat/assumptions_sample_sat.ipynb index 218b9e1c6c9..b76394e5799 100644 --- a/examples/notebook/sat/assumptions_sample_sat.ipynb +++ b/examples/notebook/sat/assumptions_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/bin_packing_sat.ipynb b/examples/notebook/sat/bin_packing_sat.ipynb index 36edf83669d..638ea767a97 100644 --- a/examples/notebook/sat/bin_packing_sat.ipynb +++ b/examples/notebook/sat/bin_packing_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/binpacking_problem_sat.ipynb b/examples/notebook/sat/binpacking_problem_sat.ipynb index 7bf8dd8ead6..81a5b31467a 100644 --- a/examples/notebook/sat/binpacking_problem_sat.ipynb +++ b/examples/notebook/sat/binpacking_problem_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb b/examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb index 48490ea2ba8..741e862cdee 100644 --- a/examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb +++ b/examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/bool_or_sample_sat.ipynb b/examples/notebook/sat/bool_or_sample_sat.ipynb index 49091ac08b1..004c5706420 100644 --- a/examples/notebook/sat/bool_or_sample_sat.ipynb +++ b/examples/notebook/sat/bool_or_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/boolean_product_sample_sat.ipynb b/examples/notebook/sat/boolean_product_sample_sat.ipynb index ba6b49ef5ab..206189e2dcd 100644 --- a/examples/notebook/sat/boolean_product_sample_sat.ipynb +++ b/examples/notebook/sat/boolean_product_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/channeling_sample_sat.ipynb b/examples/notebook/sat/channeling_sample_sat.ipynb index ffc1bdafbd6..87c91888ff6 100644 --- a/examples/notebook/sat/channeling_sample_sat.ipynb +++ b/examples/notebook/sat/channeling_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/clone_model_sample_sat.ipynb b/examples/notebook/sat/clone_model_sample_sat.ipynb index 7eeb63ca17d..0f07c81c5cd 100644 --- a/examples/notebook/sat/clone_model_sample_sat.ipynb +++ b/examples/notebook/sat/clone_model_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/cp_is_fun_sat.ipynb b/examples/notebook/sat/cp_is_fun_sat.ipynb index 511f7678ee0..d8c3facd9a2 100644 --- a/examples/notebook/sat/cp_is_fun_sat.ipynb +++ b/examples/notebook/sat/cp_is_fun_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/cp_sat_example.ipynb b/examples/notebook/sat/cp_sat_example.ipynb index 5becf973224..130032976b8 100644 --- a/examples/notebook/sat/cp_sat_example.ipynb +++ b/examples/notebook/sat/cp_sat_example.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/cumulative_variable_profile_sample_sat.ipynb b/examples/notebook/sat/cumulative_variable_profile_sample_sat.ipynb index c53482cf68f..236fdd83b44 100644 --- a/examples/notebook/sat/cumulative_variable_profile_sample_sat.ipynb +++ b/examples/notebook/sat/cumulative_variable_profile_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb b/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb index 1310572d556..3f2d6595c2d 100644 --- a/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb +++ b/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb b/examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb index 39b1147d61b..4c6e816d8c8 100644 --- a/examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb +++ b/examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/interval_relations_sample_sat.ipynb b/examples/notebook/sat/interval_relations_sample_sat.ipynb new file mode 100644 index 00000000000..397249ba5e0 --- /dev/null +++ b/examples/notebook/sat/interval_relations_sample_sat.ipynb @@ -0,0 +1,156 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "google", + "metadata": {}, + "source": [ + "##### Copyright 2024 Google LLC." + ] + }, + { + "cell_type": "markdown", + "id": "apache", + "metadata": {}, + "source": [ + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License.\n" + ] + }, + { + "cell_type": "markdown", + "id": "basename", + "metadata": {}, + "source": [ + "# interval_relations_sample_sat" + ] + }, + { + "cell_type": "markdown", + "id": "link", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "doc", + "metadata": {}, + "source": [ + "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install ortools" + ] + }, + { + "cell_type": "markdown", + "id": "description", + "metadata": {}, + "source": [ + "\n", + "Builds temporal relations between intervals.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "code", + "metadata": {}, + "outputs": [], + "source": [ + "from ortools.sat.python import cp_model\n", + "\n", + "\n", + "def interval_relations_sample_sat():\n", + " \"\"\"Showcases how to build temporal relations between intervals.\"\"\"\n", + " model = cp_model.CpModel()\n", + " horizon = 100\n", + "\n", + " # An interval can be created from three 1-var affine expressions.\n", + " start_var = model.new_int_var(0, horizon, \"start\")\n", + " duration = 10 # Python CP-SAT code accept integer variables or constants.\n", + " end_var = model.new_int_var(0, horizon, \"end\")\n", + " interval_var = model.new_interval_var(start_var, duration, end_var, \"interval\")\n", + "\n", + " # If the size is fixed, a simpler version uses the start expression and the\n", + " # size.\n", + " fixed_size_start_var = model.new_int_var(0, horizon, \"fixed_start\")\n", + " fixed_size_duration = 10\n", + " fixed_size_interval_var = model.new_fixed_size_interval_var(\n", + " fixed_size_start_var,\n", + " fixed_size_duration,\n", + " \"fixed_size_interval_var\",\n", + " )\n", + "\n", + " # An optional interval can be created from three 1-var affine expressions and\n", + " # a literal.\n", + " opt_start_var = model.new_int_var(0, horizon, \"opt_start\")\n", + " opt_duration = model.new_int_var(2, 6, \"opt_size\")\n", + " opt_end_var = model.new_int_var(0, horizon, \"opt_end\")\n", + " opt_presence_var = model.new_bool_var(\"opt_presence\")\n", + " opt_interval_var = model.new_optional_interval_var(\n", + " opt_start_var, opt_duration, opt_end_var, opt_presence_var, \"opt_interval\"\n", + " )\n", + "\n", + " # If the size is fixed, a simpler version uses the start expression, the\n", + " # size, and the presence literal.\n", + " opt_fixed_size_start_var = model.new_int_var(0, horizon, \"opt_fixed_start\")\n", + " opt_fixed_size_duration = 10\n", + " opt_fixed_size_presence_var = model.new_bool_var(\"opt_fixed_presence\")\n", + " opt_fixed_size_interval_var = model.new_optional_fixed_size_interval_var(\n", + " opt_fixed_size_start_var,\n", + " opt_fixed_size_duration,\n", + " opt_fixed_size_presence_var,\n", + " \"opt_fixed_size_interval_var\",\n", + " )\n", + "\n", + " # Simple precedence between two non optional intervals.\n", + " model.add(interval_var.start_expr() >= fixed_size_interval_var.end_expr())\n", + "\n", + " # Synchronize start between two intervals (one optional, one not)\n", + " model.add(\n", + " interval_var.start_expr() == opt_interval_var.start_expr()\n", + " ).only_enforce_if(opt_presence_var)\n", + "\n", + " # Exact delay between two optional intervals.\n", + " exact_delay: int = 5\n", + " model.add(\n", + " opt_interval_var.start_expr()\n", + " == opt_fixed_size_interval_var.end_expr() + exact_delay\n", + " ).only_enforce_if(opt_presence_var, opt_fixed_size_presence_var)\n", + "\n", + "\n", + "interval_relations_sample_sat()\n", + "\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebook/sat/interval_sample_sat.ipynb b/examples/notebook/sat/interval_sample_sat.ipynb index 3e44a96691e..493c78ace51 100644 --- a/examples/notebook/sat/interval_sample_sat.ipynb +++ b/examples/notebook/sat/interval_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/literal_sample_sat.ipynb b/examples/notebook/sat/literal_sample_sat.ipynb index 0c91794ccb0..be8ff9ab741 100644 --- a/examples/notebook/sat/literal_sample_sat.ipynb +++ b/examples/notebook/sat/literal_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/minimal_jobshop_sat.ipynb b/examples/notebook/sat/minimal_jobshop_sat.ipynb index dcf4f17a6c5..1fc48868739 100644 --- a/examples/notebook/sat/minimal_jobshop_sat.ipynb +++ b/examples/notebook/sat/minimal_jobshop_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/multiple_knapsack_sat.ipynb b/examples/notebook/sat/multiple_knapsack_sat.ipynb index 91ed388c2e5..95eec40688d 100644 --- a/examples/notebook/sat/multiple_knapsack_sat.ipynb +++ b/examples/notebook/sat/multiple_knapsack_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/no_overlap_sample_sat.ipynb b/examples/notebook/sat/no_overlap_sample_sat.ipynb index f835517bab3..2cdb3ec36ca 100644 --- a/examples/notebook/sat/no_overlap_sample_sat.ipynb +++ b/examples/notebook/sat/no_overlap_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/non_linear_sat.ipynb b/examples/notebook/sat/non_linear_sat.ipynb index bcfb65d3477..6a612e344ba 100644 --- a/examples/notebook/sat/non_linear_sat.ipynb +++ b/examples/notebook/sat/non_linear_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/nqueens_sat.ipynb b/examples/notebook/sat/nqueens_sat.ipynb index 4b25066bc29..019fb947775 100644 --- a/examples/notebook/sat/nqueens_sat.ipynb +++ b/examples/notebook/sat/nqueens_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/nurses_sat.ipynb b/examples/notebook/sat/nurses_sat.ipynb index 705fa28cc67..02cf4d5aa2f 100644 --- a/examples/notebook/sat/nurses_sat.ipynb +++ b/examples/notebook/sat/nurses_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/optional_interval_sample_sat.ipynb b/examples/notebook/sat/optional_interval_sample_sat.ipynb index 65d1e6a1d8d..f356012ecf7 100644 --- a/examples/notebook/sat/optional_interval_sample_sat.ipynb +++ b/examples/notebook/sat/optional_interval_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/overlapping_intervals_sample_sat.ipynb b/examples/notebook/sat/overlapping_intervals_sample_sat.ipynb index 4ce140f0314..0eab0653205 100644 --- a/examples/notebook/sat/overlapping_intervals_sample_sat.ipynb +++ b/examples/notebook/sat/overlapping_intervals_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb b/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb index 0270831e70d..63dc2a60b8c 100644 --- a/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb +++ b/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/ranking_circuit_sample_sat.ipynb b/examples/notebook/sat/ranking_circuit_sample_sat.ipynb index 03914786751..fb87e450640 100644 --- a/examples/notebook/sat/ranking_circuit_sample_sat.ipynb +++ b/examples/notebook/sat/ranking_circuit_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { @@ -95,7 +95,7 @@ " durations: Sequence[int],\n", " presences: Sequence[cp_model.IntVar],\n", " ranks: Sequence[cp_model.IntVar],\n", - "):\n", + ") -> None:\n", " \"\"\"This method uses a circuit constraint to rank tasks.\n", "\n", " This method assumes that all starts are disjoint, meaning that all tasks have\n", @@ -105,7 +105,7 @@ " To implement this ranking, we will create a dense graph with num_tasks + 1\n", " nodes.\n", " The extra node (with id 0) will be used to decide which task is first with\n", - " its only outgoing arc, and whhich task is last with its only incoming arc.\n", + " its only outgoing arc, and which task is last with its only incoming arc.\n", " Each task i will be associated with id i + 1, and an arc between i + 1 and j +\n", " 1 indicates that j is the immediate successor of i.\n", "\n", @@ -171,7 +171,7 @@ " model.add_circuit(arcs)\n", "\n", "\n", - "def ranking_sample_sat():\n", + "def ranking_sample_sat() -> None:\n", " \"\"\"Ranks tasks in a NoOverlap constraint.\"\"\"\n", "\n", " model = cp_model.CpModel()\n", diff --git a/examples/notebook/sat/ranking_sample_sat.ipynb b/examples/notebook/sat/ranking_sample_sat.ipynb index 0f3be41613a..8dc5a41d815 100644 --- a/examples/notebook/sat/ranking_sample_sat.ipynb +++ b/examples/notebook/sat/ranking_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/reified_sample_sat.ipynb b/examples/notebook/sat/reified_sample_sat.ipynb index 99fa6e00e6d..0ad5d781379 100644 --- a/examples/notebook/sat/reified_sample_sat.ipynb +++ b/examples/notebook/sat/reified_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/schedule_requests_sat.ipynb b/examples/notebook/sat/schedule_requests_sat.ipynb index b59b80f9acd..42333d160a0 100644 --- a/examples/notebook/sat/schedule_requests_sat.ipynb +++ b/examples/notebook/sat/schedule_requests_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb b/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb index 9d8d97094ce..0ddd27e1e61 100644 --- a/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb +++ b/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb b/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb index 4d176acba60..90bb0d27f6d 100644 --- a/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb +++ b/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/simple_sat_program.ipynb b/examples/notebook/sat/simple_sat_program.ipynb index 5af365a37ce..fcb5827ea93 100644 --- a/examples/notebook/sat/simple_sat_program.ipynb +++ b/examples/notebook/sat/simple_sat_program.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/solution_hinting_sample_sat.ipynb b/examples/notebook/sat/solution_hinting_sample_sat.ipynb index 543e327c8d3..a3a5ff8b045 100644 --- a/examples/notebook/sat/solution_hinting_sample_sat.ipynb +++ b/examples/notebook/sat/solution_hinting_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb b/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb index 0723487a2ba..f3b5e59e2e2 100644 --- a/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb +++ b/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb b/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb index 2893bc443aa..1bd9182de26 100644 --- a/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb +++ b/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/step_function_sample_sat.ipynb b/examples/notebook/sat/step_function_sample_sat.ipynb index 7dde80e322a..d0acd538eea 100644 --- a/examples/notebook/sat/step_function_sample_sat.ipynb +++ b/examples/notebook/sat/step_function_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb b/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb index 0fd972bc500..091b49d23fa 100644 --- a/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb +++ b/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb @@ -5,7 +5,7 @@ "id": "google", "metadata": {}, "source": [ - "##### Copyright 2023 Google LLC." + "##### Copyright 2024 Google LLC." ] }, { diff --git a/examples/notebook/sat/transitions_in_no_overlap_sample_sat.ipynb b/examples/notebook/sat/transitions_in_no_overlap_sample_sat.ipynb new file mode 100644 index 00000000000..77b4f68c4ca --- /dev/null +++ b/examples/notebook/sat/transitions_in_no_overlap_sample_sat.ipynb @@ -0,0 +1,280 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "google", + "metadata": {}, + "source": [ + "##### Copyright 2024 Google LLC." + ] + }, + { + "cell_type": "markdown", + "id": "apache", + "metadata": {}, + "source": [ + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License.\n" + ] + }, + { + "cell_type": "markdown", + "id": "basename", + "metadata": {}, + "source": [ + "# transitions_in_no_overlap_sample_sat" + ] + }, + { + "cell_type": "markdown", + "id": "link", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "doc", + "metadata": {}, + "source": [ + "First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "install", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install ortools" + ] + }, + { + "cell_type": "markdown", + "id": "description", + "metadata": {}, + "source": [ + "\n", + "Implements transition times and costs in a no_overlap constraint.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "code", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Dict, List, Sequence, Tuple, Union\n", + "\n", + "from ortools.sat.python import cp_model\n", + "\n", + "\n", + "def transitive_reduction_with_circuit_delays_and_penalties(\n", + " model: cp_model.CpModel,\n", + " starts: Sequence[cp_model.IntVar],\n", + " durations: Sequence[int],\n", + " presences: Sequence[Union[cp_model.IntVar, bool]],\n", + " penalties: Dict[Tuple[int, int], int],\n", + " delays: Dict[Tuple[int, int], int],\n", + ") -> Sequence[Tuple[cp_model.IntVar, int]]:\n", + " \"\"\"This method uses a circuit constraint to rank tasks.\n", + "\n", + " This method assumes that all starts are disjoint, meaning that all tasks have\n", + " a strictly positive duration, and they appear in the same NoOverlap\n", + " constraint.\n", + "\n", + " The extra node (with id 0) will be used to decide which task is first with\n", + " its only outgoing arc, and which task is last with its only incoming arc.\n", + " Each task i will be associated with id i + 1, and an arc between i + 1 and j +\n", + " 1 indicates that j is the immediate successor of i.\n", + "\n", + " The circuit constraint ensures there is at most 1 hamiltonian cycle of\n", + " length > 1. If no such path exists, then no tasks are active.\n", + " We also need to enforce that any hamiltonian cycle of size > 1 must contain\n", + " the node 0. And thus, there is a self loop on node 0 iff the circuit is empty.\n", + "\n", + " Args:\n", + " model: The CpModel to add the constraints to.\n", + " starts: The array of starts variables of all tasks.\n", + " durations: the durations of all tasks.\n", + " presences: The array of presence variables of all tasks.\n", + " penalties: the array of tuple (`tail_index`, `head_index`, `penalty`) that\n", + " specifies that if task `tail_index` is the successor of the task\n", + " `head_index`, then `penalty` must be added to the cost.\n", + " delays: the array of tuple (`tail_index`, `head_index`, `delay`) that\n", + " specifies that if task `tail_index` is the successor of the task\n", + " `head_index`, then an extra `delay` must be added between the end of the\n", + " first task and the start of the second task.\n", + "\n", + " Returns:\n", + " The list of pairs (Boolean variables, penalty) to be added to the objective.\n", + " \"\"\"\n", + "\n", + " num_tasks = len(starts)\n", + " all_tasks = range(num_tasks)\n", + "\n", + " arcs: List[cp_model.ArcT] = []\n", + " penalty_terms = []\n", + " for i in all_tasks:\n", + " # if node i is first.\n", + " start_lit = model.new_bool_var(f\"start_{i}\")\n", + " arcs.append((0, i + 1, start_lit))\n", + "\n", + " # As there are no other constraints on the problem, we can add this\n", + " # redundant constraint.\n", + " model.add(starts[i] == 0).only_enforce_if(start_lit)\n", + "\n", + " # if node i is last.\n", + " end_lit = model.new_bool_var(f\"end_{i}\")\n", + " arcs.append((i + 1, 0, end_lit))\n", + "\n", + " for j in all_tasks:\n", + " if i == j:\n", + " arcs.append((i + 1, i + 1, ~presences[i]))\n", + " else:\n", + " literal = model.new_bool_var(f\"arc_{i}_to_{j}\")\n", + " arcs.append((i + 1, j + 1, literal))\n", + "\n", + " # To perform the transitive reduction from precedences to successors,\n", + " # we need to tie the starts of the tasks with 'literal'.\n", + " # In a pure problem, the following inequality could be an equality.\n", + " # It is not true in general.\n", + " #\n", + " # Note that we could use this literal to penalize the transition, add an\n", + " # extra delay to the precedence.\n", + " min_delay = 0\n", + " key = (i, j)\n", + " if key in delays:\n", + " min_delay = delays[key]\n", + " model.add(\n", + " starts[j] >= starts[i] + durations[i] + min_delay\n", + " ).only_enforce_if(literal)\n", + "\n", + " # Create the penalties.\n", + " if key in penalties:\n", + " penalty_terms.append((literal, penalties[key]))\n", + "\n", + " # Manage the empty circuit\n", + " empty = model.new_bool_var(\"empty\")\n", + " arcs.append((0, 0, empty))\n", + "\n", + " for i in all_tasks:\n", + " model.add_implication(empty, ~presences[i])\n", + "\n", + " # Add the circuit constraint.\n", + " model.add_circuit(arcs)\n", + "\n", + " return penalty_terms\n", + "\n", + "\n", + "def transitions_in_no_overlap_sample_sat():\n", + " \"\"\"Implement transitions in a NoOverlap constraint.\"\"\"\n", + "\n", + " model = cp_model.CpModel()\n", + " horizon = 40\n", + " num_tasks = 4\n", + "\n", + " # Breaking the natural sequence induces a fixed penalty.\n", + " penalties = {\n", + " (1, 0): 10,\n", + " (2, 0): 10,\n", + " (3, 0): 10,\n", + " (2, 1): 10,\n", + " (3, 1): 10,\n", + " (3, 2): 10,\n", + " }\n", + "\n", + " # Switching from an odd to even or even to odd task indices induces a delay.\n", + " delays = {\n", + " (1, 0): 10,\n", + " (0, 1): 10,\n", + " (3, 0): 10,\n", + " (0, 3): 10,\n", + " (1, 2): 10,\n", + " (2, 1): 10,\n", + " (3, 2): 10,\n", + " (2, 3): 10,\n", + " }\n", + "\n", + " all_tasks = range(num_tasks)\n", + "\n", + " starts = []\n", + " durations = []\n", + " intervals = []\n", + " presences = []\n", + "\n", + " # Creates intervals, all present. But the cost is robust w.r.t. optional\n", + " # intervals.\n", + " for t in all_tasks:\n", + " start = model.new_int_var(0, horizon, f\"start[{t}]\")\n", + " duration = 5\n", + " presence = True\n", + " interval = model.new_optional_fixed_size_interval_var(\n", + " start, duration, presence, f\"opt_interval[{t}]\"\n", + " )\n", + "\n", + " starts.append(start)\n", + " durations.append(duration)\n", + " intervals.append(interval)\n", + " presences.append(presence)\n", + "\n", + " # Adds NoOverlap constraint.\n", + " model.add_no_overlap(intervals)\n", + "\n", + " # Adds ranking constraint.\n", + " penalty_terms = transitive_reduction_with_circuit_delays_and_penalties(\n", + " model, starts, durations, presences, penalties, delays\n", + " )\n", + "\n", + " # Minimize the sum of penalties,\n", + " model.minimize(sum(var * penalty for var, penalty in penalty_terms))\n", + "\n", + " # In practise, only one penalty can happen. Thus the two even tasks are\n", + " # together, same for the two odd tasks.\n", + " # Because of the penalties, the optimal sequence is 0 -> 2 -> 1 -> 3\n", + " # which induces one penalty and one delay.\n", + "\n", + " # Solves the model model.\n", + " solver = cp_model.CpSolver()\n", + " status = solver.solve(model)\n", + "\n", + " if status == cp_model.OPTIMAL:\n", + " # Prints out the makespan and the start times and ranks of all tasks.\n", + " print(f\"Optimal cost: {solver.objective_value}\")\n", + " for t in all_tasks:\n", + " if solver.value(presences[t]):\n", + " print(f\"Task {t} starts at {solver.value(starts[t])} \")\n", + " else:\n", + " print(f\"Task {t} in not performed\")\n", + " else:\n", + " print(f\"Solver exited with nonoptimal status: {status}\")\n", + "\n", + "\n", + "transitions_in_no_overlap_sample_sat()\n", + "\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +}