switch flags setters and getters to the absl format
This commit is contained in:
@@ -1,524 +1,282 @@
|
||||
cc_binary(
|
||||
name = "constraint_programming_cp",
|
||||
srcs = ["constraint_programming_cp.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/constraint_solver:cp",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "constraint_programming_cp",
|
||||
srcs = ["constraint_programming_cp.cc"],
|
||||
deps = ["//ortools/base", "//ortools/constraint_solver:cp", ], )
|
||||
|
||||
cc_binary(
|
||||
name = "costas_array_sat",
|
||||
srcs = ["costas_array_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "costas_array_sat", srcs = ["costas_array_sat.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:random", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
], )
|
||||
|
||||
cc_library(
|
||||
name = "cvrptw_lib",
|
||||
hdrs = ["cvrptw_lib.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
name = "cvrptw_lib", hdrs = ["cvrptw_lib.h"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:random", "//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "cvrptw",
|
||||
srcs = ["cvrptw.cc"],
|
||||
deps = [
|
||||
":cvrptw_lib",
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "cvrptw", srcs = ["cvrptw.cc"],
|
||||
deps = [":cvrptw_lib",
|
||||
"//ortools/base", "//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "cvrp_disjoint_tw",
|
||||
srcs = ["cvrp_disjoint_tw.cc"],
|
||||
deps = [
|
||||
":cvrptw_lib",
|
||||
"//ortools/base",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "cvrp_disjoint_tw", srcs = ["cvrp_disjoint_tw.cc"],
|
||||
deps = [":cvrptw_lib",
|
||||
"//ortools/base", "//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "cvrptw_with_breaks",
|
||||
srcs = ["cvrptw_with_breaks.cc"],
|
||||
deps = [
|
||||
":cvrptw_lib",
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_enums_cc_proto",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "cvrptw_with_breaks", srcs = ["cvrptw_with_breaks.cc"],
|
||||
deps = [":cvrptw_lib",
|
||||
"//ortools/base", "//ortools/base:random",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_enums_cc_proto",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "cvrptw_with_resources",
|
||||
srcs = ["cvrptw_with_resources.cc"],
|
||||
deps = [
|
||||
":cvrptw_lib",
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "cvrptw_with_resources", srcs = ["cvrptw_with_resources.cc"],
|
||||
deps = [":cvrptw_lib",
|
||||
"//ortools/base", "//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "cvrptw_with_stop_times_and_resources",
|
||||
srcs = ["cvrptw_with_stop_times_and_resources.cc"],
|
||||
deps = [
|
||||
":cvrptw_lib",
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "cvrptw_with_stop_times_and_resources",
|
||||
srcs = ["cvrptw_with_stop_times_and_resources.cc"],
|
||||
deps = [":cvrptw_lib",
|
||||
"//ortools/base", "//ortools/base:random",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "cvrptw_with_refueling",
|
||||
srcs = ["cvrptw_with_refueling.cc"],
|
||||
deps = [
|
||||
":cvrptw_lib",
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "cvrptw_with_refueling", srcs = ["cvrptw_with_refueling.cc"],
|
||||
deps = [":cvrptw_lib",
|
||||
"//ortools/base", "//ortools/base:random",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_library(
|
||||
name = "print_dimacs_assignment",
|
||||
hdrs = ["print_dimacs_assignment.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"@com_google_absl//absl/status",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
],
|
||||
)
|
||||
name = "print_dimacs_assignment", hdrs = ["print_dimacs_assignment.h"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:file", "@com_google_absl//absl/status",
|
||||
"//ortools/graph:ebert_graph", "//ortools/graph:linear_assignment",
|
||||
], )
|
||||
|
||||
cc_library(
|
||||
name = "parse_dimacs_assignment",
|
||||
hdrs = ["parse_dimacs_assignment.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:filelineiter",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
],
|
||||
)
|
||||
cc_library(name = "parse_dimacs_assignment",
|
||||
hdrs = ["parse_dimacs_assignment.h"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:filelineiter", "//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "dimacs_assignment",
|
||||
srcs = ["dimacs_assignment.cc"],
|
||||
deps = [
|
||||
":parse_dimacs_assignment",
|
||||
":print_dimacs_assignment",
|
||||
"//ortools/algorithms:hungarian",
|
||||
"//ortools/base",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
],
|
||||
)
|
||||
name = "dimacs_assignment", srcs = ["dimacs_assignment.cc"],
|
||||
deps = [":parse_dimacs_assignment",
|
||||
":print_dimacs_assignment", "//ortools/algorithms:hungarian",
|
||||
"//ortools/base", "//ortools/base:timer",
|
||||
"//ortools/graph:ebert_graph", "//ortools/graph:linear_assignment",
|
||||
], )
|
||||
|
||||
cc_binary(name = "dobble_ls", srcs = ["dobble_ls.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:map_util", "//ortools/base:random",
|
||||
"//ortools/constraint_solver:cp", "//ortools/util:bitset",
|
||||
], )
|
||||
|
||||
cc_binary(name = "flow_api", srcs = ["flow_api.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/graph:ebert_graph", "//ortools/graph:max_flow",
|
||||
"//ortools/graph:min_cost_flow",
|
||||
], )
|
||||
|
||||
cc_library(name = "fap_parser", hdrs = ["fap_parser.h"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:file", "//ortools/base:hash",
|
||||
"//ortools/base:map_util", "@com_google_absl//absl/strings",
|
||||
], )
|
||||
|
||||
cc_library(name = "fap_model_printer", hdrs = ["fap_model_printer.h"],
|
||||
deps = [":fap_parser",
|
||||
"//ortools/base", "//ortools/base:file",
|
||||
"@com_google_absl//absl/strings",
|
||||
], )
|
||||
|
||||
cc_library(name = "fap_utilities", hdrs = ["fap_utilities.h"],
|
||||
deps = [":fap_parser",
|
||||
"//ortools/base", "//ortools/base:map_util",
|
||||
"//ortools/constraint_solver:cp",
|
||||
], )
|
||||
|
||||
cc_binary(name = "frequency_assignment_problem",
|
||||
srcs = ["frequency_assignment_problem.cc"],
|
||||
deps = [":fap_model_printer",
|
||||
":fap_parser", ":fap_utilities", "//ortools/base",
|
||||
"//ortools/base:map_util", "//ortools/constraint_solver:cp",
|
||||
], )
|
||||
|
||||
cc_binary(name = "golomb_sat", srcs = ["golomb_sat.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"@com_google_absl//absl/strings", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
], )
|
||||
|
||||
cc_binary(name = "integer_programming", srcs = ["integer_programming.cc"],
|
||||
deps = ["//ortools/base", "//ortools/linear_solver", ], )
|
||||
|
||||
cc_binary(name = "jobshop_sat", srcs = ["jobshop_sat.cc", ],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:file", "@com_google_absl//absl/strings",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/data:jobshop_scheduling_cc_proto",
|
||||
"//ortools/data:jobshop_scheduling_parser",
|
||||
"//ortools/sat:cp_model", "//ortools/sat:cp_model_solver",
|
||||
"//ortools/sat:disjunctive", "//ortools/sat:integer",
|
||||
"//ortools/sat:intervals", "//ortools/sat:model",
|
||||
"//ortools/sat:optimization", "//ortools/sat:precedences",
|
||||
"//ortools/sat:sat_solver",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "dobble_ls",
|
||||
srcs = ["dobble_ls.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/base:random",
|
||||
"//ortools/constraint_solver:cp",
|
||||
"//ortools/util:bitset",
|
||||
],
|
||||
)
|
||||
name = "linear_assignment_api", srcs = ["linear_assignment_api.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/graph:ebert_graph", "//ortools/graph:linear_assignment",
|
||||
], )
|
||||
|
||||
cc_binary(name = "linear_programming", srcs = ["linear_programming.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
], )
|
||||
|
||||
cc_binary(name = "linear_solver_protocol_buffers",
|
||||
srcs = ["linear_solver_protocol_buffers.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
], )
|
||||
|
||||
cc_binary(name = "magic_square_sat", srcs = ["magic_square_sat.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"@com_google_absl//absl/strings", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
], )
|
||||
|
||||
cc_binary(name = "max_flow", srcs = ["max_flow.cc"],
|
||||
deps = ["//ortools/base", "//ortools/graph:max_flow", ], )
|
||||
|
||||
cc_binary(name = "min_cost_flow", srcs = ["min_cost_flow.cc"],
|
||||
deps = ["//ortools/base", "//ortools/graph:min_cost_flow", ], )
|
||||
|
||||
cc_binary(
|
||||
name = "flow_api",
|
||||
srcs = ["flow_api.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:max_flow",
|
||||
"//ortools/graph:min_cost_flow",
|
||||
],
|
||||
)
|
||||
name = "mps_driver", srcs = ["mps_driver.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"@com_google_absl//absl/strings", "//ortools/base:timer",
|
||||
"//ortools/glop:lp_solver", "//ortools/glop:parameters_cc_proto",
|
||||
"//ortools/lp_data:mps_reader", "//ortools/util:proto_tools",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
], )
|
||||
|
||||
cc_library(
|
||||
name = "fap_parser",
|
||||
hdrs = ["fap_parser.h"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/base:map_util",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "network_routing_sat", srcs = ["network_routing_sat.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:hash", "//ortools/base:map_util",
|
||||
"//ortools/base:random", "@com_google_absl//absl/strings",
|
||||
"//ortools/graph:shortestpaths", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:model", "//ortools/util:tuple_set",
|
||||
], )
|
||||
|
||||
cc_library(
|
||||
name = "fap_model_printer",
|
||||
hdrs = ["fap_model_printer.h"],
|
||||
deps = [
|
||||
":fap_parser",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "nqueens", srcs = ["nqueens.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:map_util", "//ortools/constraint_solver:cp",
|
||||
], )
|
||||
|
||||
cc_library(
|
||||
name = "fap_utilities",
|
||||
hdrs = ["fap_utilities.h"],
|
||||
deps = [
|
||||
":fap_parser",
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/constraint_solver:cp",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "pdptw", srcs = ["pdptw.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:file", "//ortools/base:mathutil",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "frequency_assignment_problem",
|
||||
srcs = ["frequency_assignment_problem.cc"],
|
||||
deps = [
|
||||
":fap_model_printer",
|
||||
":fap_parser",
|
||||
":fap_utilities",
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/constraint_solver:cp",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "golomb_sat",
|
||||
srcs = ["golomb_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "integer_programming",
|
||||
srcs = ["integer_programming.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "jobshop_sat",
|
||||
srcs = [
|
||||
"jobshop_sat.cc",
|
||||
],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/data:jobshop_scheduling_cc_proto",
|
||||
"//ortools/data:jobshop_scheduling_parser",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:cp_model_solver",
|
||||
"//ortools/sat:disjunctive",
|
||||
"//ortools/sat:integer",
|
||||
"//ortools/sat:intervals",
|
||||
"//ortools/sat:model",
|
||||
"//ortools/sat:optimization",
|
||||
"//ortools/sat:precedences",
|
||||
"//ortools/sat:sat_solver",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "linear_assignment_api",
|
||||
srcs = ["linear_assignment_api.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/graph:ebert_graph",
|
||||
"//ortools/graph:linear_assignment",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "linear_programming",
|
||||
srcs = ["linear_programming.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "linear_solver_protocol_buffers",
|
||||
srcs = ["linear_solver_protocol_buffers.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "magic_square_sat",
|
||||
srcs = ["magic_square_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "max_flow",
|
||||
srcs = ["max_flow.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/graph:max_flow",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "min_cost_flow",
|
||||
srcs = ["min_cost_flow.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/graph:min_cost_flow",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "mps_driver",
|
||||
srcs = ["mps_driver.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/base:timer",
|
||||
"//ortools/glop:lp_solver",
|
||||
"//ortools/glop:parameters_cc_proto",
|
||||
"//ortools/lp_data:mps_reader",
|
||||
"//ortools/util:proto_tools",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "network_routing_sat",
|
||||
srcs = ["network_routing_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:hash",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/base:random",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/graph:shortestpaths",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
"//ortools/util:tuple_set",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "nqueens",
|
||||
srcs = ["nqueens.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:map_util",
|
||||
"//ortools/constraint_solver:cp",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "pdptw",
|
||||
srcs = ["pdptw.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:mathutil",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "random_tsp",
|
||||
srcs = ["random_tsp.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:random",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "random_tsp", srcs = ["random_tsp.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:random", "@com_google_absl//absl/strings",
|
||||
"//ortools/constraint_solver:routing",
|
||||
"//ortools/constraint_solver:routing_flags",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "sat_runner",
|
||||
srcs = [
|
||||
"opb_reader.h",
|
||||
"sat_cnf_reader.h",
|
||||
"sat_runner.cc",
|
||||
],
|
||||
deps = [
|
||||
"//ortools/algorithms:sparse_permutation",
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:filelineiter",
|
||||
"//ortools/base:random",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/base:threadpool",
|
||||
"//ortools/lp_data:mps_reader",
|
||||
"//ortools/lp_data:proto_utils",
|
||||
"//ortools/sat:boolean_problem",
|
||||
"//ortools/sat:boolean_problem_cc_proto",
|
||||
"//ortools/sat:cp_model_cc_proto",
|
||||
"//ortools/sat:cp_model_solver",
|
||||
"//ortools/sat:drat_proof_handler",
|
||||
"//ortools/sat:lp_utils",
|
||||
"//ortools/sat:optimization",
|
||||
"//ortools/sat:sat_solver",
|
||||
"//ortools/sat:simplification",
|
||||
"//ortools/sat:symmetry",
|
||||
"//ortools/util:sigint",
|
||||
"//ortools/util:time_limit",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
],
|
||||
)
|
||||
srcs = ["opb_reader.h", "sat_cnf_reader.h", "sat_runner.cc", ],
|
||||
deps = ["//ortools/algorithms:sparse_permutation",
|
||||
"//ortools/base", "//ortools/base:file",
|
||||
"//ortools/base:filelineiter", "//ortools/base:random",
|
||||
"@com_google_absl//absl/status", "@com_google_absl//absl/strings",
|
||||
"//ortools/base:threadpool", "//ortools/lp_data:mps_reader",
|
||||
"//ortools/lp_data:proto_utils", "//ortools/sat:boolean_problem",
|
||||
"//ortools/sat:boolean_problem_cc_proto",
|
||||
"//ortools/sat:cp_model_cc_proto", "//ortools/sat:cp_model_solver",
|
||||
"//ortools/sat:drat_proof_handler", "//ortools/sat:lp_utils",
|
||||
"//ortools/sat:optimization", "//ortools/sat:sat_solver",
|
||||
"//ortools/sat:simplification", "//ortools/sat:symmetry",
|
||||
"//ortools/util:sigint", "//ortools/util:time_limit",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
], )
|
||||
|
||||
cc_binary(name = "shift_minimization_sat",
|
||||
srcs = ["shift_minimization_sat.cc", ],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:file", "//ortools/base:filelineiter",
|
||||
"@com_google_absl//absl/strings", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "shift_minimization_sat",
|
||||
srcs = [
|
||||
"shift_minimization_sat.cc",
|
||||
],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:filelineiter",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
],
|
||||
)
|
||||
name = "slitherlink_sat", srcs = ["slitherlink_sat.cc"],
|
||||
deps =
|
||||
["//ortools/base", "//ortools/sat:cp_model", "//ortools/sat:model", ], )
|
||||
|
||||
cc_binary(
|
||||
name = "slitherlink_sat",
|
||||
srcs = ["slitherlink_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
],
|
||||
)
|
||||
name = "solve", srcs = ["solve.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/lp_data:model_reader", "//ortools/lp_data:mps_reader",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "solve",
|
||||
srcs = ["solve.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
"//ortools/linear_solver:linear_solver_cc_proto",
|
||||
"//ortools/lp_data:model_reader",
|
||||
"//ortools/lp_data:mps_reader",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "sports_scheduling_sat", srcs = ["sports_scheduling_sat.cc"],
|
||||
deps = ["//ortools/base",
|
||||
"@com_google_absl//absl/strings", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
], )
|
||||
|
||||
cc_binary(
|
||||
name = "sports_scheduling_sat",
|
||||
srcs = ["sports_scheduling_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:model",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "stigler_diet", srcs = ["stigler_diet.cc"],
|
||||
deps = ["//ortools/base", "//ortools/linear_solver", ], )
|
||||
|
||||
cc_binary(
|
||||
name = "stigler_diet",
|
||||
srcs = ["stigler_diet.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "strawberry_fields_with_column_generation",
|
||||
srcs = ["strawberry_fields_with_column_generation.cc"],
|
||||
deps = ["//ortools/base", "//ortools/linear_solver", ], )
|
||||
|
||||
cc_binary(
|
||||
name = "strawberry_fields_with_column_generation",
|
||||
srcs = ["strawberry_fields_with_column_generation.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "uncapacitated_facility_location",
|
||||
srcs = ["uncapacitated_facility_location.cc"],
|
||||
deps = ["//ortools/base", "//ortools/linear_solver", ], )
|
||||
|
||||
cc_binary(
|
||||
name = "uncapacitated_facility_location",
|
||||
srcs = ["uncapacitated_facility_location.cc"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/linear_solver",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "variable_intervals_sat", srcs = ["variable_intervals_sat.cc"],
|
||||
deps = ["//ortools/sat:cp_model", "//ortools/util:time_limit", ], )
|
||||
|
||||
|
||||
cc_binary(
|
||||
name = "variable_intervals_sat",
|
||||
srcs = ["variable_intervals_sat.cc"],
|
||||
deps = [
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/util:time_limit",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "weighted_tardiness_sat",
|
||||
srcs = [
|
||||
"weighted_tardiness_sat.cc",
|
||||
],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"//ortools/base:file",
|
||||
"//ortools/base:filelineiter",
|
||||
"@com_google_absl//absl/strings",
|
||||
"//ortools/sat:cp_model",
|
||||
"//ortools/sat:cp_model_solver",
|
||||
"//ortools/sat:disjunctive",
|
||||
"//ortools/sat:integer",
|
||||
"//ortools/sat:integer_expr",
|
||||
"//ortools/sat:intervals",
|
||||
"//ortools/sat:model",
|
||||
"//ortools/sat:optimization",
|
||||
"//ortools/sat:precedences",
|
||||
"//ortools/sat:sat_solver",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
],
|
||||
)
|
||||
cc_binary(name = "weighted_tardiness_sat",
|
||||
srcs = ["weighted_tardiness_sat.cc", ],
|
||||
deps = ["//ortools/base",
|
||||
"//ortools/base:file", "//ortools/base:filelineiter",
|
||||
"@com_google_absl//absl/strings", "//ortools/sat:cp_model",
|
||||
"//ortools/sat:cp_model_solver", "//ortools/sat:disjunctive",
|
||||
"//ortools/sat:integer", "//ortools/sat:integer_expr",
|
||||
"//ortools/sat:intervals", "//ortools/sat:model",
|
||||
"//ortools/sat:optimization", "//ortools/sat:precedences",
|
||||
"//ortools/sat:sat_solver", "@com_google_protobuf//:protobuf",
|
||||
], )
|
||||
|
||||
@@ -1,91 +1,66 @@
|
||||
if(NOT BUILD_CXX_EXAMPLES)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_INSTALL_RPATH
|
||||
"@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path")
|
||||
elseif(UNIX)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:$ORIGIN")
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
foreach(EXECUTABLE IN ITEMS
|
||||
constraint_programming_cp
|
||||
costas_array_sat
|
||||
cvrp_disjoint_tw
|
||||
cvrptw
|
||||
cvrptw_with_breaks
|
||||
cvrptw_with_refueling
|
||||
cvrptw_with_resources
|
||||
cvrptw_with_stop_times_and_resources
|
||||
dimacs_assignment
|
||||
dobble_ls
|
||||
flow_api
|
||||
frequency_assignment_problem
|
||||
golomb_sat
|
||||
integer_programming
|
||||
jobshop_sat
|
||||
linear_assignment_api
|
||||
linear_programming
|
||||
linear_solver_protocol_buffers
|
||||
magic_square_sat
|
||||
max_flow
|
||||
min_cost_flow
|
||||
mps_driver
|
||||
network_routing_sat
|
||||
nqueens
|
||||
pdptw
|
||||
random_tsp
|
||||
shift_minimization_sat
|
||||
slitherlink_sat
|
||||
solve
|
||||
sports_scheduling_sat
|
||||
stigler_diet
|
||||
strawberry_fields_with_column_generation
|
||||
uncapacitated_facility_location
|
||||
variable_intervals_sat
|
||||
weighted_tardiness_sat)
|
||||
add_executable(${EXECUTABLE} ${EXECUTABLE}.cc)
|
||||
target_include_directories(${EXECUTABLE} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_features(${EXECUTABLE} PRIVATE cxx_std_17)
|
||||
target_link_libraries(${EXECUTABLE} PRIVATE ortools::ortools)
|
||||
install(TARGETS ${EXECUTABLE})
|
||||
endforeach()
|
||||
|
||||
foreach(TEST IN ITEMS
|
||||
costas_array_sat
|
||||
cvrp_disjoint_tw
|
||||
cvrptw
|
||||
#cvrptw_with_breaks # Too long
|
||||
#cvrptw_with_refueling # Too long
|
||||
cvrptw_with_resources
|
||||
cvrptw_with_stop_times_and_resources
|
||||
#dimacs_assignment
|
||||
#dobble_ls # Too long
|
||||
flow_api
|
||||
#frequency_assignment_problem
|
||||
golomb_sat
|
||||
integer_programming
|
||||
#jobshop_sat
|
||||
knapsack
|
||||
linear_assignment_api
|
||||
linear_programming
|
||||
linear_solver_protocol_buffers
|
||||
magic_square_sat
|
||||
#mps_driver
|
||||
#network_routing_sat
|
||||
nqueens
|
||||
#pdptw
|
||||
#rcpsp_sat
|
||||
#shift_minimization_sat
|
||||
#solve
|
||||
#sports_scheduling_sat # Too long
|
||||
#stigler_diet
|
||||
#strawberry_fields_with_column_generation # Too long
|
||||
uncapacitated_facility_location
|
||||
#variable_intervals_sat
|
||||
#weighted_tardiness_sat
|
||||
)
|
||||
add_test(NAME cxx_${TEST} COMMAND ${TEST})
|
||||
endforeach()
|
||||
if (NOT BUILD_CXX_EXAMPLES)
|
||||
return () endif() if (APPLE)
|
||||
set(CMAKE_INSTALL_RPATH
|
||||
"@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path") elseif(UNIX)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:$ORIGIN")
|
||||
endif() include(GNUInstallDirs) foreach(
|
||||
EXECUTABLE IN ITEMS constraint_programming_cp costas_array_sat cvrp_disjoint_tw
|
||||
cvrptw cvrptw_with_breaks cvrptw_with_refueling cvrptw_with_resources
|
||||
cvrptw_with_stop_times_and_resources dimacs_assignment dobble_ls
|
||||
flow_api frequency_assignment_problem golomb_sat
|
||||
integer_programming jobshop_sat linear_assignment_api
|
||||
linear_programming linear_solver_protocol_buffers
|
||||
magic_square_sat max_flow min_cost_flow mps_driver
|
||||
network_routing_sat nqueens pdptw random_tsp
|
||||
shift_minimization_sat slitherlink_sat
|
||||
solve sports_scheduling_sat stigler_diet
|
||||
strawberry_fields_with_column_generation
|
||||
uncapacitated_facility_location
|
||||
variable_intervals_sat
|
||||
weighted_tardiness_sat)
|
||||
add_executable($ {
|
||||
EXECUTABLE
|
||||
} $ {
|
||||
EXECUTABLE
|
||||
}
|
||||
.cc) target_include_directories($ {
|
||||
EXECUTABLE
|
||||
} PUBLIC $ {
|
||||
CMAKE_CURRENT_SOURCE_DIR
|
||||
}) target_compile_features($ {
|
||||
EXECUTABLE
|
||||
} PRIVATE cxx_std_17) target_link_libraries($ {
|
||||
EXECUTABLE
|
||||
} PRIVATE ortools::ortools) install(TARGETS $ {
|
||||
EXECUTABLE
|
||||
}) endforeach()
|
||||
foreach(TEST IN ITEMS costas_array_sat cvrp_disjoint_tw cvrptw
|
||||
#cvrptw_with_breaks #Too long
|
||||
#cvrptw_with_refueling #Too long
|
||||
cvrptw_with_resources cvrptw_with_stop_times_and_resources
|
||||
#dimacs_assignment
|
||||
#dobble_ls #Too long
|
||||
flow_api
|
||||
#frequency_assignment_problem
|
||||
golomb_sat integer_programming
|
||||
#jobshop_sat
|
||||
knapsack linear_assignment_api linear_programming
|
||||
linear_solver_protocol_buffers magic_square_sat
|
||||
#mps_driver
|
||||
#network_routing_sat
|
||||
nqueens
|
||||
#pdptw
|
||||
#rcpsp_sat
|
||||
#shift_minimization_sat
|
||||
#solve
|
||||
#sports_scheduling_sat #Too long
|
||||
#stigler_diet
|
||||
#strawberry_fields_with_column_generation #Too long
|
||||
uncapacitated_facility_location
|
||||
#variable_intervals_sat
|
||||
#weighted_tardiness_sat
|
||||
) add_test(NAME cxx_$ {
|
||||
TEST
|
||||
} COMMAND $ {
|
||||
TEST
|
||||
}) endforeach()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# C++ examples
|
||||
#C++ examples
|
||||
The following examples showcase how to use the different Operations Research libraries.
|
||||
|
||||
## Examples list
|
||||
@@ -55,10 +55,9 @@ The following examples showcase how to use the different Operations Research lib
|
||||
- model_util.cc A utility to manipulate model files (.cp) dumped by the
|
||||
solver.
|
||||
|
||||
# Execution
|
||||
#Execution
|
||||
Running the examples will involve building them, then running them.
|
||||
You can run the following command from the **top** directory:
|
||||
```shell
|
||||
make build SOURCE=examples/cpp/<example>.cc
|
||||
make run SOURCE=examples/cpp/<example>.cc
|
||||
```
|
||||
|
||||
@@ -23,19 +23,19 @@ void RunConstraintProgrammingExample() {
|
||||
const int64 numVals = 3;
|
||||
|
||||
// Define decision variables.
|
||||
IntVar* const x = solver.MakeIntVar(0, numVals - 1, "x");
|
||||
IntVar* const y = solver.MakeIntVar(0, numVals - 1, "y");
|
||||
IntVar* const z = solver.MakeIntVar(0, numVals - 1, "z");
|
||||
IntVar *const x = solver.MakeIntVar(0, numVals - 1, "x");
|
||||
IntVar *const y = solver.MakeIntVar(0, numVals - 1, "y");
|
||||
IntVar *const z = solver.MakeIntVar(0, numVals - 1, "z");
|
||||
|
||||
// Define constraints.
|
||||
std::vector<IntVar*> xyvars = {x, y};
|
||||
std::vector<IntVar *> xyvars = { x, y };
|
||||
solver.AddConstraint(solver.MakeAllDifferent(xyvars));
|
||||
|
||||
LOG(INFO) << "Number of constraints: " << solver.constraints();
|
||||
|
||||
// Create decision builder to search for solutions.
|
||||
std::vector<IntVar*> allvars = {x, y, z};
|
||||
DecisionBuilder* const db = solver.MakePhase(
|
||||
std::vector<IntVar *> allvars = { x, y, z };
|
||||
DecisionBuilder *const db = solver.MakePhase(
|
||||
allvars, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);
|
||||
|
||||
solver.NewSearch(db);
|
||||
@@ -51,11 +51,11 @@ void RunConstraintProgrammingExample() {
|
||||
LOG(INFO) << "Problem solved in " << solver.wall_time() << "ms";
|
||||
LOG(INFO) << "Memory usage: " << Solver::MemoryUsage() << " bytes";
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
absl::GetFlag(FLAGS_logtostderr) = 1;
|
||||
operations_research::RunConstraintProgrammingExample();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -93,8 +93,8 @@ void CostasHard(const int dim) {
|
||||
std::vector<IntVar> vars;
|
||||
Domain domain(1, dim);
|
||||
for (int i = 0; i < dim; ++i) {
|
||||
vars.push_back(
|
||||
cp_model.NewIntVar(domain).WithName(absl::StrCat("var_", i)));
|
||||
vars.push_back(cp_model.NewIntVar(domain)
|
||||
.WithName(absl::StrCat("var_", i)));
|
||||
}
|
||||
|
||||
cp_model.AddAllDifferent(vars);
|
||||
@@ -106,15 +106,18 @@ void CostasHard(const int dim) {
|
||||
|
||||
for (int j = 0; j < dim - i; ++j) {
|
||||
subset.push_back(cp_model.NewIntVar(diff));
|
||||
cp_model.AddEquality(LinearExpr::Sum({subset[j], vars[j]}), vars[j + i]);
|
||||
cp_model.AddEquality(LinearExpr::Sum({
|
||||
subset[j], vars[j]
|
||||
}),
|
||||
vars[j + i]);
|
||||
}
|
||||
|
||||
cp_model.AddAllDifferent(subset);
|
||||
}
|
||||
|
||||
Model model;
|
||||
if (!FLAGS_params.empty()) {
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
}
|
||||
const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model);
|
||||
|
||||
@@ -142,8 +145,8 @@ void CostasBool(const int dim) {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
// create the variables
|
||||
std::vector<std::vector<BoolVar>> vars(dim);
|
||||
std::vector<std::vector<BoolVar>> transposed_vars(dim);
|
||||
std::vector<std::vector<BoolVar> > vars(dim);
|
||||
std::vector<std::vector<BoolVar> > transposed_vars(dim);
|
||||
for (int i = 0; i < dim; ++i) {
|
||||
for (int j = 0; j < dim; ++j) {
|
||||
const BoolVar var = cp_model.NewBoolVar();
|
||||
@@ -168,10 +171,12 @@ void CostasBool(const int dim) {
|
||||
const BoolVar neg = cp_model.NewBoolVar();
|
||||
positive_diffs.push_back(pos);
|
||||
negative_diffs.push_back(neg);
|
||||
cp_model.AddBoolOr({Not(vars[var][value]),
|
||||
Not(vars[var + step][value + diff]), pos});
|
||||
cp_model.AddBoolOr({Not(vars[var][value + diff]),
|
||||
Not(vars[var + step][value]), neg});
|
||||
cp_model.AddBoolOr({
|
||||
Not(vars[var][value]), Not(vars[var + step][value + diff]), pos
|
||||
});
|
||||
cp_model.AddBoolOr({
|
||||
Not(vars[var][value + diff]), Not(vars[var + step][value]), neg
|
||||
});
|
||||
}
|
||||
}
|
||||
cp_model.AddLessOrEqual(LinearExpr::BooleanSum(positive_diffs), 1);
|
||||
@@ -180,8 +185,8 @@ void CostasBool(const int dim) {
|
||||
}
|
||||
|
||||
Model model;
|
||||
if (!FLAGS_params.empty()) {
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
}
|
||||
const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model);
|
||||
|
||||
@@ -213,8 +218,8 @@ void CostasBoolSoft(const int dim) {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
// create the variables
|
||||
std::vector<std::vector<BoolVar>> vars(dim);
|
||||
std::vector<std::vector<BoolVar>> transposed_vars(dim);
|
||||
std::vector<std::vector<BoolVar> > vars(dim);
|
||||
std::vector<std::vector<BoolVar> > transposed_vars(dim);
|
||||
for (int i = 0; i < dim; ++i) {
|
||||
for (int j = 0; j < dim; ++j) {
|
||||
const BoolVar var = cp_model.NewBoolVar();
|
||||
@@ -240,10 +245,12 @@ void CostasBoolSoft(const int dim) {
|
||||
const BoolVar neg = cp_model.NewBoolVar();
|
||||
positive_diffs.push_back(pos);
|
||||
negative_diffs.push_back(neg);
|
||||
cp_model.AddBoolOr({Not(vars[var][value]),
|
||||
Not(vars[var + step][value + diff]), pos});
|
||||
cp_model.AddBoolOr({Not(vars[var][value + diff]),
|
||||
Not(vars[var + step][value]), neg});
|
||||
cp_model.AddBoolOr({
|
||||
Not(vars[var][value]), Not(vars[var + step][value + diff]), pos
|
||||
});
|
||||
cp_model.AddBoolOr({
|
||||
Not(vars[var][value + diff]), Not(vars[var + step][value]), neg
|
||||
});
|
||||
}
|
||||
}
|
||||
const IntVar pos_var =
|
||||
@@ -262,8 +269,8 @@ void CostasBoolSoft(const int dim) {
|
||||
cp_model.Minimize(LinearExpr::Sum(all_violations));
|
||||
|
||||
Model model;
|
||||
if (!FLAGS_params.empty()) {
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
}
|
||||
const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model);
|
||||
|
||||
@@ -290,19 +297,19 @@ void CostasBoolSoft(const int dim) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
int min = 1;
|
||||
int max = 10;
|
||||
|
||||
if (FLAGS_minsize != 0) {
|
||||
min = FLAGS_minsize;
|
||||
if (absl::GetFlag(FLAGS_minsize) != 0) {
|
||||
min = absl::GetFlag(FLAGS_minsize);
|
||||
|
||||
if (FLAGS_maxsize != 0) {
|
||||
max = FLAGS_maxsize;
|
||||
if (absl::GetFlag(FLAGS_maxsize) != 0) {
|
||||
max = absl::GetFlag(FLAGS_maxsize);
|
||||
} else {
|
||||
max = min;
|
||||
}
|
||||
@@ -310,11 +317,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
for (int size = min; size <= max; ++size) {
|
||||
LOG(INFO) << "Computing Costas Array for dim = " << size;
|
||||
if (FLAGS_model == 1) {
|
||||
if (absl::GetFlag(FLAGS_model) == 1) {
|
||||
operations_research::sat::CostasHard(size);
|
||||
} else if (FLAGS_model == 2) {
|
||||
} else if (absl::GetFlag(FLAGS_model) == 2) {
|
||||
operations_research::sat::CostasBool(size);
|
||||
} else if (FLAGS_model == 3) {
|
||||
} else if (absl::GetFlag(FLAGS_model) == 3) {
|
||||
operations_research::sat::CostasBoolSoft(size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,75 +59,81 @@ DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char *kTime = "Time";
|
||||
const char *kCapacity = "Capacity";
|
||||
const int64 kMaxNodesPerGroup = 10;
|
||||
const int64 kSameVehicleCost = 1000;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
||||
<< "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
||||
// ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
LocationContainer locations(
|
||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
||||
++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
// Setting the cost function.
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
demand.Initialize();
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity, /*fix_start_cumul_to_zero=*/ true,
|
||||
kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
kTimePerDemandUnit, [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/ false, kTime);
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding disjoint time windows.
|
||||
Solver* solver = routing.solver();
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
Solver *solver = routing.solver();
|
||||
ACMRandom randomizer(
|
||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
std::vector<int64> forbid_points(2 * FLAGS_vrp_windows, 0);
|
||||
std::vector<int64> forbid_points(2 * absl::GetFlag(FLAGS_vrp_windows), 0);
|
||||
for (int i = 0; i < forbid_points.size(); ++i) {
|
||||
forbid_points[i] = randomizer.Uniform(kHorizon);
|
||||
}
|
||||
@@ -153,7 +159,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Adding same vehicle constraint costs for consecutive nodes.
|
||||
if (FLAGS_vrp_use_same_vehicle_costs) {
|
||||
if (absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs)) {
|
||||
std::vector<int64> group;
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
@@ -171,10 +177,11 @@ int main(int argc, char** argv) {
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
DisplayPlan(manager, routing, *solution,
|
||||
absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs),
|
||||
kMaxNodesPerGroup, kSameVehicleCost,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -57,72 +57,78 @@ DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char *kTime = "Time";
|
||||
const char *kCapacity = "Capacity";
|
||||
const int64 kMaxNodesPerGroup = 10;
|
||||
const int64 kSameVehicleCost = 1000;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
||||
<< "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
||||
// ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
LocationContainer locations(
|
||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
||||
++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
// Setting the cost function.
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
demand.Initialize();
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity, /*fix_start_cumul_to_zero=*/ true,
|
||||
kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
kTimePerDemandUnit, [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/ true, kTime);
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
ACMRandom randomizer(
|
||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
@@ -139,7 +145,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Adding same vehicle constraint costs for consecutive nodes.
|
||||
if (FLAGS_vrp_use_same_vehicle_costs) {
|
||||
if (absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs)) {
|
||||
std::vector<int64> group;
|
||||
for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < manager.num_nodes(); ++order) {
|
||||
@@ -157,10 +163,11 @@ int main(int argc, char** argv) {
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
DisplayPlan(manager, routing, *solution,
|
||||
absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs),
|
||||
kMaxNodesPerGroup, kSameVehicleCost,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
|
||||
@@ -35,7 +35,7 @@ int32 GetSeed(bool deterministic);
|
||||
// Location container, contains positions of orders and can be used to obtain
|
||||
// Manhattan distances/times between locations.
|
||||
class LocationContainer {
|
||||
public:
|
||||
public:
|
||||
LocationContainer(int64 speed, bool use_deterministic_seed);
|
||||
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
|
||||
void AddRandomLocation(int64 x_max, int64 y_max);
|
||||
@@ -51,15 +51,15 @@ class LocationContainer {
|
||||
RoutingIndexManager::NodeIndex node2) const;
|
||||
int64 SameLocationFromIndex(int64 node1, int64 node2) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
class Location {
|
||||
public:
|
||||
public:
|
||||
Location();
|
||||
Location(int64 x, int64 y);
|
||||
int64 DistanceTo(const Location& location) const;
|
||||
bool IsAtSameLocation(const Location& location) const;
|
||||
int64 DistanceTo(const Location &location) const;
|
||||
bool IsAtSameLocation(const Location &location) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
static int64 Abs(int64 value);
|
||||
|
||||
int64 x_;
|
||||
@@ -73,14 +73,14 @@ class LocationContainer {
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
public:
|
||||
RandomDemand(int size, RoutingIndexManager::NodeIndex depot,
|
||||
bool use_deterministic_seed);
|
||||
void Initialize();
|
||||
int64 Demand(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const RoutingIndexManager::NodeIndex depot_;
|
||||
@@ -89,14 +89,14 @@ class RandomDemand {
|
||||
|
||||
// Service time (proportional to demand) + transition time callback.
|
||||
class ServiceTimePlusTransition {
|
||||
public:
|
||||
public:
|
||||
ServiceTimePlusTransition(int64 time_per_demand_unit,
|
||||
RoutingNodeEvaluator2 demand,
|
||||
RoutingNodeEvaluator2 transition_time);
|
||||
int64 Compute(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
const int64 time_per_demand_unit_;
|
||||
RoutingNodeEvaluator2 demand_;
|
||||
RoutingNodeEvaluator2 transition_time_;
|
||||
@@ -104,29 +104,30 @@ class ServiceTimePlusTransition {
|
||||
|
||||
// Stop service time + transition time callback.
|
||||
class StopServiceTimePlusTransition {
|
||||
public:
|
||||
public:
|
||||
StopServiceTimePlusTransition(int64 stop_time,
|
||||
const LocationContainer& location_container,
|
||||
const LocationContainer &location_container,
|
||||
RoutingNodeEvaluator2 transition_time);
|
||||
int64 Compute(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
const int64 stop_time_;
|
||||
const LocationContainer& location_container_;
|
||||
const LocationContainer &location_container_;
|
||||
RoutingNodeEvaluator2 demand_;
|
||||
RoutingNodeEvaluator2 transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(
|
||||
const operations_research::RoutingIndexManager& manager,
|
||||
const operations_research::RoutingModel& routing,
|
||||
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
|
||||
int64 max_nodes_per_group, int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension);
|
||||
void
|
||||
DisplayPlan(const operations_research::RoutingIndexManager &manager,
|
||||
const operations_research::RoutingModel &routing,
|
||||
const operations_research::Assignment &plan,
|
||||
bool use_same_vehicle_costs, int64 max_nodes_per_group,
|
||||
int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension &capacity_dimension,
|
||||
const operations_research::RoutingDimension &time_dimension);
|
||||
|
||||
using NodeIndex = RoutingIndexManager::NodeIndex;
|
||||
|
||||
@@ -186,12 +187,12 @@ LocationContainer::Location::Location() : x_(0), y_(0) {}
|
||||
|
||||
LocationContainer::Location::Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
|
||||
int64 LocationContainer::Location::DistanceTo(const Location& location) const {
|
||||
int64 LocationContainer::Location::DistanceTo(const Location &location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
bool LocationContainer::Location::IsAtSameLocation(
|
||||
const Location& location) const {
|
||||
bool
|
||||
LocationContainer::Location::IsAtSameLocation(const Location &location) const {
|
||||
return x_ == location.x_ && y_ == location.y_;
|
||||
}
|
||||
|
||||
@@ -201,8 +202,7 @@ int64 LocationContainer::Location::Abs(int64 value) {
|
||||
|
||||
RandomDemand::RandomDemand(int size, NodeIndex depot,
|
||||
bool use_deterministic_seed)
|
||||
: size_(size),
|
||||
depot_(depot),
|
||||
: size_(size), depot_(depot),
|
||||
use_deterministic_seed_(use_deterministic_seed) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
@@ -229,8 +229,7 @@ int64 RandomDemand::Demand(NodeIndex from, NodeIndex /*to*/) const {
|
||||
ServiceTimePlusTransition::ServiceTimePlusTransition(
|
||||
int64 time_per_demand_unit, RoutingNodeEvaluator2 demand,
|
||||
RoutingNodeEvaluator2 transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(std::move(demand)),
|
||||
: time_per_demand_unit_(time_per_demand_unit), demand_(std::move(demand)),
|
||||
transition_time_(std::move(transition_time)) {}
|
||||
|
||||
int64 ServiceTimePlusTransition::Compute(NodeIndex from, NodeIndex to) const {
|
||||
@@ -238,10 +237,9 @@ int64 ServiceTimePlusTransition::Compute(NodeIndex from, NodeIndex to) const {
|
||||
}
|
||||
|
||||
StopServiceTimePlusTransition::StopServiceTimePlusTransition(
|
||||
int64 stop_time, const LocationContainer& location_container,
|
||||
int64 stop_time, const LocationContainer &location_container,
|
||||
RoutingNodeEvaluator2 transition_time)
|
||||
: stop_time_(stop_time),
|
||||
location_container_(location_container),
|
||||
: stop_time_(stop_time), location_container_(location_container),
|
||||
transition_time_(std::move(transition_time)) {}
|
||||
|
||||
int64 StopServiceTimePlusTransition::Compute(NodeIndex from,
|
||||
@@ -251,19 +249,21 @@ int64 StopServiceTimePlusTransition::Compute(NodeIndex from,
|
||||
: stop_time_ + transition_time_(from, to);
|
||||
}
|
||||
|
||||
void DisplayPlan(
|
||||
const RoutingIndexManager& manager, const RoutingModel& routing,
|
||||
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
|
||||
int64 max_nodes_per_group, int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension) {
|
||||
void
|
||||
DisplayPlan(const RoutingIndexManager &manager, const RoutingModel &routing,
|
||||
const operations_research::Assignment &plan,
|
||||
bool use_same_vehicle_costs, int64 max_nodes_per_group,
|
||||
int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension &capacity_dimension,
|
||||
const operations_research::RoutingDimension &time_dimension) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = absl::StrFormat("Cost %d\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int64 order = 0; order < routing.Size(); ++order) {
|
||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
||||
if (routing.IsStart(order) || routing.IsEnd(order))
|
||||
continue;
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
absl::StrAppendFormat(&dropped, " %d",
|
||||
@@ -283,7 +283,8 @@ void DisplayPlan(
|
||||
int64 group_same_vehicle_cost = 0;
|
||||
std::set<int> visited;
|
||||
for (int64 order = 0; order < routing.Size(); ++order) {
|
||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
||||
if (routing.IsStart(order) || routing.IsEnd(order))
|
||||
continue;
|
||||
++group_size;
|
||||
visited.insert(plan.Value(routing.VehicleVar(order)));
|
||||
if (group_size == max_nodes_per_group) {
|
||||
@@ -309,11 +310,11 @@ void DisplayPlan(
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
operations_research::IntVar* const load_var =
|
||||
operations_research::IntVar *const load_var =
|
||||
capacity_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const time_var =
|
||||
operations_research::IntVar *const time_var =
|
||||
time_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const slack_var =
|
||||
operations_research::IntVar *const slack_var =
|
||||
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
|
||||
if (slack_var != nullptr && plan.Contains(slack_var)) {
|
||||
absl::StrAppendFormat(
|
||||
@@ -327,7 +328,8 @@ void DisplayPlan(
|
||||
plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var));
|
||||
}
|
||||
if (routing.IsEnd(order)) break;
|
||||
if (routing.IsEnd(order))
|
||||
break;
|
||||
plan_output += " -> ";
|
||||
order = plan.Value(routing.NextVar(order));
|
||||
}
|
||||
@@ -336,6 +338,6 @@ void DisplayPlan(
|
||||
}
|
||||
LOG(INFO) << plan_output;
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
||||
#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
||||
|
||||
@@ -64,22 +64,26 @@ DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char *kTime = "Time";
|
||||
const char *kCapacity = "Capacity";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
||||
<< "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
||||
// ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
||||
RoutingModel routing(manager);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
parameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION);
|
||||
|
||||
@@ -87,52 +91,54 @@ int main(int argc, char** argv) {
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
LocationContainer locations(
|
||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
||||
++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
// Setting the cost function.
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
demand.Initialize();
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity, /*fix_start_cumul_to_zero=*/ true,
|
||||
kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
kTimePerDemandUnit, [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
RoutingDimension* const time_dimension = routing.GetMutableDimension(kTime);
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/ false, kTime);
|
||||
RoutingDimension *const time_dimension = routing.GetMutableDimension(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
ACMRandom randomizer(
|
||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
@@ -144,7 +150,7 @@ int main(int argc, char** argv) {
|
||||
for (int i = 0; i < routing.Size(); ++i) {
|
||||
routing.AddVariableMinimizedByFinalizer(time_dimension->CumulVar(i));
|
||||
}
|
||||
for (int j = 0; j < FLAGS_vrp_vehicles; ++j) {
|
||||
for (int j = 0; j < absl::GetFlag(FLAGS_vrp_vehicles); ++j) {
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
time_dimension->CumulVar(routing.Start(j)));
|
||||
routing.AddVariableMinimizedByFinalizer(
|
||||
@@ -165,15 +171,17 @@ int main(int argc, char** argv) {
|
||||
service_times[node] = kTimePerDemandUnit * demand.Demand(index, index);
|
||||
}
|
||||
}
|
||||
const std::vector<std::vector<int>> break_data = {
|
||||
{/*start_min*/ 11, /*start_max*/ 13, /*duration*/ 2400},
|
||||
{/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800},
|
||||
{/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800}};
|
||||
Solver* const solver = routing.solver();
|
||||
for (int vehicle = 0; vehicle < FLAGS_vrp_vehicles; ++vehicle) {
|
||||
std::vector<IntervalVar*> breaks;
|
||||
const std::vector<std::vector<int> > break_data = {
|
||||
{ /*start_min*/ 11, /*start_max*/ 13, /*duration*/ 2400 },
|
||||
{ /*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800 },
|
||||
{ /*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800 }
|
||||
};
|
||||
Solver *const solver = routing.solver();
|
||||
for (int vehicle = 0; vehicle < absl::GetFlag(FLAGS_vrp_vehicles);
|
||||
++vehicle) {
|
||||
std::vector<IntervalVar *> breaks;
|
||||
for (int i = 0; i < break_data.size(); ++i) {
|
||||
IntervalVar* const break_interval = solver->MakeFixedDurationIntervalVar(
|
||||
IntervalVar *const break_interval = solver->MakeFixedDurationIntervalVar(
|
||||
break_data[i][0] * 3600, break_data[i][1] * 3600, break_data[i][2],
|
||||
true, absl::StrCat("Break ", i, " on vehicle ", vehicle));
|
||||
breaks.push_back(break_interval);
|
||||
@@ -202,10 +210,10 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
LOG(INFO) << "Breaks: ";
|
||||
for (const auto& break_interval :
|
||||
for (const auto &break_interval :
|
||||
solution->IntervalVarContainer().elements()) {
|
||||
if (break_interval.PerformedValue() == 1) {
|
||||
LOG(INFO) << break_interval.Var()->name() << " "
|
||||
|
||||
@@ -53,9 +53,9 @@ DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char* kFuel = "Fuel";
|
||||
const char *kTime = "Time";
|
||||
const char *kCapacity = "Capacity";
|
||||
const char *kFuel = "Fuel";
|
||||
|
||||
// Returns true if node is a refueling node (based on node / refuel node ratio).
|
||||
bool IsRefuelNode(int64 node) {
|
||||
@@ -63,66 +63,72 @@ bool IsRefuelNode(int64 node) {
|
||||
return (node % kRefuelNodeRatio == 0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
||||
<< "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
||||
// ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
LocationContainer locations(
|
||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
||||
++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
// Setting the cost function.
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
demand.Initialize();
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity, /*fix_start_cumul_to_zero=*/ true,
|
||||
kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
kTimePerDemandUnit, [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/ true, kTime);
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
ACMRandom randomizer(
|
||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
if (!IsRefuelNode(order)) {
|
||||
@@ -137,11 +143,11 @@ int main(int argc, char** argv) {
|
||||
const int64 kFuelCapacity = kXMax + kYMax;
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.NegManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
}),
|
||||
kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel);
|
||||
const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel);
|
||||
return locations.NegManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
}),
|
||||
kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/ false, kFuel);
|
||||
const RoutingDimension &fuel_dimension = routing.GetDimensionOrDie(kFuel);
|
||||
for (int order = 0; order < routing.Size(); ++order) {
|
||||
// Only let slack free for refueling nodes.
|
||||
if (!IsRefuelNode(order) || routing.IsStart(order)) {
|
||||
@@ -163,11 +169,11 @@ int main(int argc, char** argv) {
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/ false,
|
||||
/*max_nodes_per_group=*/ 0, /*same_vehicle_cost=*/ 0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
} else {
|
||||
|
||||
@@ -58,70 +58,76 @@ DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char *kTime = "Time";
|
||||
const char *kCapacity = "Capacity";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size FLAGS_vrp_size.
|
||||
// Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
||||
<< "Specify a non-null vehicle fleet size.";
|
||||
// VRP of size absl::GetFlag(FLAGS_vrp_size).
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
|
||||
// ends of
|
||||
// the routes are at node 0.
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
|
||||
absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
LocationContainer locations(
|
||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
|
||||
++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
// Setting the cost function.
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
demand.Initialize();
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity, /*fix_start_cumul_to_zero=*/ true,
|
||||
kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kTimePerDemandUnit = 300;
|
||||
const int64 kHorizon = 24 * 3600;
|
||||
ServiceTimePlusTransition time(
|
||||
kTimePerDemandUnit,
|
||||
[&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
kTimePerDemandUnit, [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return demand.Demand(i, j);
|
||||
},
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/ false, kTime);
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
ACMRandom randomizer(
|
||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < manager.num_nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
@@ -130,15 +136,15 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Adding resource constraints at the depot (start and end location of
|
||||
// routes).
|
||||
std::vector<IntVar*> start_end_times;
|
||||
for (int i = 0; i < FLAGS_vrp_vehicles; ++i) {
|
||||
std::vector<IntVar *> start_end_times;
|
||||
for (int i = 0; i < absl::GetFlag(FLAGS_vrp_vehicles); ++i) {
|
||||
start_end_times.push_back(time_dimension.CumulVar(routing.End(i)));
|
||||
start_end_times.push_back(time_dimension.CumulVar(routing.Start(i)));
|
||||
}
|
||||
// Build corresponding time intervals.
|
||||
const int64 kVehicleSetup = 180;
|
||||
Solver* const solver = routing.solver();
|
||||
std::vector<IntervalVar*> intervals;
|
||||
Solver *const solver = routing.solver();
|
||||
std::vector<IntervalVar *> intervals;
|
||||
solver->MakeFixedDurationIntervalVarArray(start_end_times, kVehicleSetup,
|
||||
"depot_interval", &intervals);
|
||||
// Constrain the number of maximum simultaneous intervals at depot.
|
||||
@@ -163,11 +169,11 @@ int main(int argc, char** argv) {
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/ false,
|
||||
/*max_nodes_per_group=*/ 0, /*same_vehicle_cost=*/ 0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
} else {
|
||||
|
||||
@@ -58,52 +58,58 @@ DEFINE_string(routing_search_parameters, "",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char *kTime = "Time";
|
||||
const char *kCapacity = "Capacity";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_stops) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_orders_per_stop)
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_stops))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size.";
|
||||
const int vrp_orders = FLAGS_vrp_stops * FLAGS_vrp_orders_per_stop;
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders_per_stop))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
|
||||
<< "Specify a non-null vehicle fleet size.";
|
||||
const int vrp_orders =
|
||||
absl::GetFlag(FLAGS_vrp_stops) * absl::GetFlag(FLAGS_vrp_orders_per_stop);
|
||||
// Nodes are indexed from 0 to vrp_orders, the starts and ends of the routes
|
||||
// are at node 0.
|
||||
const RoutingIndexManager::NodeIndex kDepot(0);
|
||||
RoutingIndexManager manager(vrp_orders + 1, FLAGS_vrp_vehicles, kDepot);
|
||||
RoutingIndexManager manager(vrp_orders + 1, absl::GetFlag(FLAGS_vrp_vehicles),
|
||||
kDepot);
|
||||
RoutingModel routing(manager);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int stop = 0; stop <= FLAGS_vrp_stops; ++stop) {
|
||||
const int num_orders = stop == 0 ? 1 : FLAGS_vrp_orders_per_stop;
|
||||
LocationContainer locations(
|
||||
kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
for (int stop = 0; stop <= absl::GetFlag(FLAGS_vrp_stops); ++stop) {
|
||||
const int num_orders =
|
||||
stop == 0 ? 1 : absl::GetFlag(FLAGS_vrp_orders_per_stop);
|
||||
locations.AddRandomLocation(kXMax, kYMax, num_orders);
|
||||
}
|
||||
|
||||
// Setting the cost function.
|
||||
// Setting the cost function.
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) {
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return locations.ManhattanDistance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(manager.num_nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
|
||||
demand.Initialize();
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) {
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
/*fix_start_cumul_to_zero=*/true, kCapacity);
|
||||
return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kNullCapacitySlack, kVehicleCapacity, /*fix_start_cumul_to_zero=*/ true,
|
||||
kCapacity);
|
||||
|
||||
// Adding time dimension constraints.
|
||||
const int64 kStopTime = 300;
|
||||
@@ -111,55 +117,55 @@ int main(int argc, char** argv) {
|
||||
StopServiceTimePlusTransition time(
|
||||
kStopTime, locations,
|
||||
[&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
return locations.ManhattanTime(i, j);
|
||||
});
|
||||
routing.AddDimension(
|
||||
routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) {
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
}),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/ false, kTime);
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows, for the sake of simplicty same for each stop.
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
ACMRandom randomizer(
|
||||
GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
|
||||
for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
for (int stop_order = 0; stop_order < FLAGS_vrp_orders_per_stop;
|
||||
++stop_order) {
|
||||
const int order = stop * FLAGS_vrp_orders_per_stop + stop_order + 1;
|
||||
for (int stop_order = 0;
|
||||
stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) {
|
||||
const int order =
|
||||
stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1;
|
||||
time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Adding resource constraints at order locations.
|
||||
Solver* const solver = routing.solver();
|
||||
std::vector<IntervalVar*> intervals;
|
||||
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
|
||||
std::vector<IntervalVar*> stop_intervals;
|
||||
for (int stop_order = 0; stop_order < FLAGS_vrp_orders_per_stop;
|
||||
++stop_order) {
|
||||
const int order = stop * FLAGS_vrp_orders_per_stop + stop_order + 1;
|
||||
IntervalVar* const interval = solver->MakeFixedDurationIntervalVar(
|
||||
Solver *const solver = routing.solver();
|
||||
std::vector<IntervalVar *> intervals;
|
||||
for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) {
|
||||
std::vector<IntervalVar *> stop_intervals;
|
||||
for (int stop_order = 0;
|
||||
stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) {
|
||||
const int order =
|
||||
stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1;
|
||||
IntervalVar *const interval = solver->MakeFixedDurationIntervalVar(
|
||||
0, kHorizon, kStopTime, true, absl::StrCat("Order", order));
|
||||
intervals.push_back(interval);
|
||||
stop_intervals.push_back(interval);
|
||||
// Link order and interval.
|
||||
IntVar* const order_start = time_dimension.CumulVar(order);
|
||||
IntVar *const order_start = time_dimension.CumulVar(order);
|
||||
solver->AddConstraint(
|
||||
solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start,
|
||||
interval->PerformedExpr()->Var()));
|
||||
// Make interval performed iff corresponding order has service time.
|
||||
// An order has no service time iff it is at the same location as the
|
||||
// next order on the route.
|
||||
IntVar* const is_null_duration =
|
||||
solver
|
||||
->MakeElement(
|
||||
[&locations, order](int64 index) {
|
||||
return locations.SameLocationFromIndex(order, index);
|
||||
},
|
||||
routing.NextVar(order))
|
||||
->Var();
|
||||
// Make interval performed iff corresponding order has service time.
|
||||
// An order has no service time iff it is at the same location as the
|
||||
// next order on the route.
|
||||
IntVar *const is_null_duration =
|
||||
solver->MakeElement([&locations, order](int64 index) {
|
||||
return locations.SameLocationFromIndex(order, index);
|
||||
},
|
||||
routing.NextVar(order))->Var();
|
||||
solver->AddConstraint(
|
||||
solver->MakeNonEquality(interval->PerformedExpr(), is_null_duration));
|
||||
routing.AddIntervalToAssignment(interval);
|
||||
@@ -190,15 +196,15 @@ int main(int argc, char** argv) {
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/ false,
|
||||
/*max_nodes_per_group=*/ 0, /*same_vehicle_cost=*/ 0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
LOG(INFO) << "Stop intervals:";
|
||||
for (IntervalVar* const interval : intervals) {
|
||||
for (IntervalVar *const interval : intervals) {
|
||||
if (solution->PerformedValue(interval)) {
|
||||
LOG(INFO) << interval->name() << ": " << solution->StartValue(interval);
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ typedef ForwardStarStaticGraph GraphType;
|
||||
|
||||
template <typename GraphType>
|
||||
CostValue BuildAndSolveHungarianInstance(
|
||||
const LinearSumAssignment<GraphType>& assignment) {
|
||||
const GraphType& graph = assignment.Graph();
|
||||
const LinearSumAssignment<GraphType> &assignment) {
|
||||
const GraphType &graph = assignment.Graph();
|
||||
typedef std::vector<double> HungarianRow;
|
||||
typedef std::vector<HungarianRow> HungarianProblem;
|
||||
HungarianProblem hungarian_cost;
|
||||
@@ -106,7 +106,7 @@ CostValue BuildAndSolveHungarianInstance(
|
||||
}
|
||||
|
||||
template <typename GraphType>
|
||||
void DisplayAssignment(const LinearSumAssignment<GraphType>& assignment) {
|
||||
void DisplayAssignment(const LinearSumAssignment<GraphType> &assignment) {
|
||||
for (typename LinearSumAssignment<GraphType>::BipartiteLeftNodeIterator
|
||||
node_it(assignment);
|
||||
node_it.Ok(); node_it.Next()) {
|
||||
@@ -119,18 +119,18 @@ void DisplayAssignment(const LinearSumAssignment<GraphType>& assignment) {
|
||||
}
|
||||
|
||||
template <typename GraphType>
|
||||
int SolveDimacsAssignment(int argc, char* argv[]) {
|
||||
int SolveDimacsAssignment(int argc, char *argv[]) {
|
||||
std::string error_message;
|
||||
// Handle on the graph we will need to delete because the
|
||||
// LinearSumAssignment object does not take ownership of it.
|
||||
GraphType* graph = nullptr;
|
||||
GraphType *graph = nullptr;
|
||||
DimacsAssignmentParser<GraphType> parser(argv[1]);
|
||||
LinearSumAssignment<GraphType>* assignment =
|
||||
LinearSumAssignment<GraphType> *assignment =
|
||||
parser.Parse(&error_message, &graph);
|
||||
if (assignment == nullptr) {
|
||||
LOG(FATAL) << error_message;
|
||||
}
|
||||
if (!FLAGS_assignment_problem_output_file.empty()) {
|
||||
if (!absl::GetFlag(FLAGS_assignment_problem_output_file).empty()) {
|
||||
// The following tail array management stuff is done in a generic
|
||||
// way so we can plug in different types of graphs for which the
|
||||
// TailArrayManager template can be instantiated, even though we
|
||||
@@ -139,12 +139,13 @@ int SolveDimacsAssignment(int argc, char* argv[]) {
|
||||
// this file and making no other changes to the code.
|
||||
TailArrayManager<GraphType> tail_array_manager(graph);
|
||||
PrintDimacsAssignmentProblem<GraphType>(
|
||||
*assignment, tail_array_manager, FLAGS_assignment_problem_output_file);
|
||||
*assignment, tail_array_manager,
|
||||
absl::GetFlag(FLAGS_assignment_problem_output_file));
|
||||
tail_array_manager.ReleaseTailArrayIfForwardGraph();
|
||||
}
|
||||
CostValue hungarian_cost = 0.0;
|
||||
bool hungarian_solved = false;
|
||||
if (FLAGS_assignment_compare_hungarian) {
|
||||
if (absl::GetFlag(FLAGS_assignment_compare_hungarian)) {
|
||||
hungarian_cost = BuildAndSolveHungarianInstance(*assignment);
|
||||
hungarian_solved = true;
|
||||
}
|
||||
@@ -169,16 +170,16 @@ int SolveDimacsAssignment(int argc, char* argv[]) {
|
||||
delete graph;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
static const char* const kUsageTemplate = "usage: %s <filename>";
|
||||
static const char *const kUsageTemplate = "usage: %s <filename>";
|
||||
|
||||
using ::operations_research::ForwardStarGraph;
|
||||
using ::operations_research::ForwardStarStaticGraph;
|
||||
using ::operations_research::SolveDimacsAssignment;
|
||||
using ::operations_research::StarGraph;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int main(int argc, char *argv[]) {
|
||||
std::string usage;
|
||||
if (argc < 1) {
|
||||
usage = absl::StrFormat(kUsageTemplate, "solve_dimacs_assignment");
|
||||
@@ -192,9 +193,9 @@ int main(int argc, char* argv[]) {
|
||||
LOG(FATAL) << usage;
|
||||
}
|
||||
|
||||
if (FLAGS_assignment_static_graph) {
|
||||
if (absl::GetFlag(FLAGS_assignment_static_graph)) {
|
||||
return SolveDimacsAssignment<ForwardStarStaticGraph>(argc, argv);
|
||||
} else if (FLAGS_assignment_reverse_arcs) {
|
||||
} else if (absl::GetFlag(FLAGS_assignment_reverse_arcs)) {
|
||||
return SolveDimacsAssignment<StarGraph>(argc, argv);
|
||||
} else {
|
||||
return SolveDimacsAssignment<ForwardStarGraph>(argc, argv);
|
||||
|
||||
@@ -41,9 +41,8 @@
|
||||
#include "ortools/util/bitset.h"
|
||||
|
||||
DEFINE_int32(symbols_per_card, 8, "Number of symbols per card.");
|
||||
DEFINE_int32(ls_seed, 1,
|
||||
"Seed for the random number generator (used by "
|
||||
"the Local Neighborhood Search).");
|
||||
DEFINE_int32(ls_seed, 1, "Seed for the random number generator (used by "
|
||||
"the Local Neighborhood Search).");
|
||||
DEFINE_bool(use_filter, true, "Use filter in the local search to prune moves.");
|
||||
DEFINE_int32(num_swaps, 4,
|
||||
"If num_swap > 0, the search for an optimal "
|
||||
@@ -61,14 +60,13 @@ namespace operations_research {
|
||||
// sum_i(card1_symbol_vars[i]*card2_symbol_vars[i]) == count_var.
|
||||
// with all card_symbol_vars[i] being boolean variables.
|
||||
class SymbolsSharedByTwoCardsConstraint : public Constraint {
|
||||
public:
|
||||
public:
|
||||
// This constructor does not take any ownership on its arguments.
|
||||
SymbolsSharedByTwoCardsConstraint(
|
||||
Solver* const solver, const std::vector<IntVar*>& card1_symbol_vars,
|
||||
const std::vector<IntVar*>& card2_symbol_vars,
|
||||
IntVar* const num_symbols_in_common_var)
|
||||
: Constraint(solver),
|
||||
card1_symbol_vars_(card1_symbol_vars),
|
||||
Solver *const solver, const std::vector<IntVar *> &card1_symbol_vars,
|
||||
const std::vector<IntVar *> &card2_symbol_vars,
|
||||
IntVar *const num_symbols_in_common_var)
|
||||
: Constraint(solver), card1_symbol_vars_(card1_symbol_vars),
|
||||
card2_symbol_vars_(card2_symbol_vars),
|
||||
num_symbols_(card1_symbol_vars.size()),
|
||||
num_symbols_in_common_var_(num_symbols_in_common_var) {
|
||||
@@ -96,7 +94,7 @@ class SymbolsSharedByTwoCardsConstraint : public Constraint {
|
||||
// such, InitialPropagate will be called after all normal demons
|
||||
// and constraints have reached a fixed point. Note
|
||||
// that ownership of the 'global_demon' belongs to the solver.
|
||||
Demon* const global_demon =
|
||||
Demon *const global_demon =
|
||||
solver()->MakeDelayedConstraintInitialPropagateCallback(this);
|
||||
// Attach to all variables.
|
||||
for (int i = 0; i < num_symbols_; ++i) {
|
||||
@@ -177,11 +175,11 @@ class SymbolsSharedByTwoCardsConstraint : public Constraint {
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<IntVar*> card1_symbol_vars_;
|
||||
std::vector<IntVar*> card2_symbol_vars_;
|
||||
private:
|
||||
std::vector<IntVar *> card1_symbol_vars_;
|
||||
std::vector<IntVar *> card2_symbol_vars_;
|
||||
const int num_symbols_;
|
||||
IntVar* const num_symbols_in_common_var_;
|
||||
IntVar *const num_symbols_in_common_var_;
|
||||
};
|
||||
|
||||
// Creates two integer variables: one that counts the number of
|
||||
@@ -189,11 +187,11 @@ class SymbolsSharedByTwoCardsConstraint : public Constraint {
|
||||
// difference between the first var and 1 (i.e. the violation of the
|
||||
// objective). Returns the latter (both vars are owned by the Solver
|
||||
// anyway).
|
||||
IntVar* CreateViolationVar(Solver* const solver,
|
||||
const std::vector<IntVar*>& card1_symbol_vars,
|
||||
const std::vector<IntVar*>& card2_symbol_vars,
|
||||
IntVar *CreateViolationVar(Solver *const solver,
|
||||
const std::vector<IntVar *> &card1_symbol_vars,
|
||||
const std::vector<IntVar *> &card2_symbol_vars,
|
||||
int num_symbols_per_card) {
|
||||
IntVar* const num_symbols_in_common_var =
|
||||
IntVar *const num_symbols_in_common_var =
|
||||
solver->MakeIntVar(0, num_symbols_per_card);
|
||||
// RevAlloc transfers the ownership of the constraint to the solver.
|
||||
solver->AddConstraint(solver->RevAlloc(new SymbolsSharedByTwoCardsConstraint(
|
||||
@@ -233,13 +231,11 @@ IntVar* CreateViolationVar(Solver* const solver,
|
||||
// parent class below, which contains some shared code to store a
|
||||
// compact representation of which symbols appeal on each cards.
|
||||
class DobbleOperator : public IntVarLocalSearchOperator {
|
||||
public:
|
||||
DobbleOperator(const std::vector<IntVar*>& card_symbol_vars, int num_cards,
|
||||
public:
|
||||
DobbleOperator(const std::vector<IntVar *> &card_symbol_vars, int num_cards,
|
||||
int num_symbols, int num_symbols_per_card)
|
||||
: IntVarLocalSearchOperator(card_symbol_vars),
|
||||
num_cards_(num_cards),
|
||||
num_symbols_(num_symbols),
|
||||
num_symbols_per_card_(num_symbols_per_card),
|
||||
: IntVarLocalSearchOperator(card_symbol_vars), num_cards_(num_cards),
|
||||
num_symbols_(num_symbols), num_symbols_per_card_(num_symbols_per_card),
|
||||
symbols_per_card_(num_cards) {
|
||||
CHECK_GT(num_cards, 0);
|
||||
CHECK_GT(num_symbols, 0);
|
||||
@@ -251,7 +247,7 @@ class DobbleOperator : public IntVarLocalSearchOperator {
|
||||
|
||||
~DobbleOperator() override {}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// OnStart() simply stores the current symbols per card in
|
||||
// symbols_per_card_, and defers further initialization to the
|
||||
// subclass's InitNeighborhoodSearch() method.
|
||||
@@ -298,14 +294,12 @@ class DobbleOperator : public IntVarLocalSearchOperator {
|
||||
// symbol to a card that already had it); see the DobbleFilter class
|
||||
// below to see how we filter those out.
|
||||
class SwapSymbols : public DobbleOperator {
|
||||
public:
|
||||
SwapSymbols(const std::vector<IntVar*>& card_symbol_vars, int num_cards,
|
||||
public:
|
||||
SwapSymbols(const std::vector<IntVar *> &card_symbol_vars, int num_cards,
|
||||
int num_symbols, int num_symbols_per_card)
|
||||
: DobbleOperator(card_symbol_vars, num_cards, num_symbols,
|
||||
num_symbols_per_card),
|
||||
current_card1_(-1),
|
||||
current_card2_(-1),
|
||||
current_symbol1_(-1),
|
||||
current_card1_(-1), current_card2_(-1), current_symbol1_(-1),
|
||||
current_symbol2_(-1) {}
|
||||
|
||||
~SwapSymbols() override {}
|
||||
@@ -323,7 +317,7 @@ class SwapSymbols : public DobbleOperator {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Reinit the exploration loop.
|
||||
void InitNeighborhoodSearch() override {
|
||||
current_card1_ = 0;
|
||||
@@ -368,20 +362,19 @@ class SwapSymbols : public DobbleOperator {
|
||||
// randomized "infinite" version instead of an iterative, exhaustive
|
||||
// one.
|
||||
class SwapSymbolsOnCardPairs : public DobbleOperator {
|
||||
public:
|
||||
SwapSymbolsOnCardPairs(const std::vector<IntVar*>& card_symbol_vars,
|
||||
public:
|
||||
SwapSymbolsOnCardPairs(const std::vector<IntVar *> &card_symbol_vars,
|
||||
int num_cards, int num_symbols,
|
||||
int num_symbols_per_card, int max_num_swaps)
|
||||
: DobbleOperator(card_symbol_vars, num_cards, num_symbols,
|
||||
num_symbols_per_card),
|
||||
rand_(FLAGS_ls_seed),
|
||||
max_num_swaps_(max_num_swaps) {
|
||||
rand_(absl::GetFlag(FLAGS_ls_seed)), max_num_swaps_(max_num_swaps) {
|
||||
CHECK_GE(max_num_swaps, 2);
|
||||
}
|
||||
|
||||
~SwapSymbolsOnCardPairs() override {}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
bool MakeOneNeighbor() override {
|
||||
const int num_swaps =
|
||||
absl::Uniform<int32_t>(rand_, 0, max_num_swaps_ - 1) + 2;
|
||||
@@ -401,7 +394,7 @@ class SwapSymbolsOnCardPairs : public DobbleOperator {
|
||||
|
||||
void InitNeighborhoodSearch() override {}
|
||||
|
||||
private:
|
||||
private:
|
||||
std::mt19937 rand_;
|
||||
const int max_num_swaps_;
|
||||
};
|
||||
@@ -434,20 +427,17 @@ class SwapSymbolsOnCardPairs : public DobbleOperator {
|
||||
// effectively limits the number of cards to 63, and thus the number
|
||||
// of symbols per card to 8.
|
||||
class DobbleFilter : public IntVarLocalSearchFilter {
|
||||
public:
|
||||
DobbleFilter(const std::vector<IntVar*>& card_symbol_vars, int num_cards,
|
||||
public:
|
||||
DobbleFilter(const std::vector<IntVar *> &card_symbol_vars, int num_cards,
|
||||
int num_symbols, int num_symbols_per_card)
|
||||
: IntVarLocalSearchFilter(card_symbol_vars),
|
||||
num_cards_(num_cards),
|
||||
num_symbols_(num_symbols),
|
||||
num_symbols_per_card_(num_symbols_per_card),
|
||||
temporary_bitset_(0),
|
||||
symbol_bitmask_per_card_(num_cards, 0),
|
||||
: IntVarLocalSearchFilter(card_symbol_vars), num_cards_(num_cards),
|
||||
num_symbols_(num_symbols), num_symbols_per_card_(num_symbols_per_card),
|
||||
temporary_bitset_(0), symbol_bitmask_per_card_(num_cards, 0),
|
||||
violation_costs_(num_cards_, std::vector<int>(num_cards_, 0)) {}
|
||||
|
||||
// We build the current bitmap and the matrix of violation cost
|
||||
// between any two cards.
|
||||
void OnSynchronize(const Assignment* delta) override {
|
||||
void OnSynchronize(const Assignment *delta) override {
|
||||
symbol_bitmask_per_card_.assign(num_cards_, 0);
|
||||
for (int card = 0; card < num_cards_; ++card) {
|
||||
for (int symbol = 0; symbol < num_symbols_; ++symbol) {
|
||||
@@ -468,9 +458,9 @@ class DobbleFilter : public IntVarLocalSearchFilter {
|
||||
// The LocalSearchFilter::Accept() API also takes a deltadelta,
|
||||
// which is the difference between the current delta and the last
|
||||
// delta that was given to Accept() -- but we don't use it here.
|
||||
bool Accept(const Assignment* delta, const Assignment* unused_deltadelta,
|
||||
bool Accept(const Assignment *delta, const Assignment *unused_deltadelta,
|
||||
int64 objective_min, int64 objective_max) override {
|
||||
const Assignment::IntContainer& solution_delta = delta->IntVarContainer();
|
||||
const Assignment::IntContainer &solution_delta = delta->IntVarContainer();
|
||||
const int solution_delta_size = solution_delta.Size();
|
||||
|
||||
// The input const Assignment* delta given to Accept() may
|
||||
@@ -528,7 +518,7 @@ class DobbleFilter : public IntVarLocalSearchFilter {
|
||||
return cost_delta < 0;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Undo information after an evaluation.
|
||||
struct UndoChange {
|
||||
UndoChange(int c, uint64 b) : card(c), bitset(b) {}
|
||||
@@ -543,14 +533,14 @@ class DobbleFilter : public IntVarLocalSearchFilter {
|
||||
// For each touched card, compare against all others to compute the
|
||||
// delta in term of cost. We use an bitset to avoid counting twice
|
||||
// between two cards appearing in the local search move.
|
||||
int ComputeNewCost(const std::vector<int>& touched_cards) {
|
||||
int ComputeNewCost(const std::vector<int> &touched_cards) {
|
||||
ClearBitset();
|
||||
int cost_delta = 0;
|
||||
for (int i = 0; i < touched_cards.size(); ++i) {
|
||||
const int touched = touched_cards[i];
|
||||
SetBit64(&temporary_bitset_, touched);
|
||||
const uint64 card_bitset = symbol_bitmask_per_card_[touched];
|
||||
const std::vector<int>& row_cost = violation_costs_[touched];
|
||||
const std::vector<int> &row_cost = violation_costs_[touched];
|
||||
for (int other_card = 0; other_card < num_cards_; ++other_card) {
|
||||
if (!IsBitSet64(&temporary_bitset_, other_card)) {
|
||||
cost_delta += ViolationCost(
|
||||
@@ -563,8 +553,8 @@ class DobbleFilter : public IntVarLocalSearchFilter {
|
||||
}
|
||||
|
||||
// Collects all card indices appearing in the local search move.
|
||||
void ComputeTouchedCards(const Assignment::IntContainer& solution_delta,
|
||||
std::vector<int>* const touched_cards) {
|
||||
void ComputeTouchedCards(const Assignment::IntContainer &solution_delta,
|
||||
std::vector<int> *const touched_cards) {
|
||||
ClearBitset();
|
||||
const int solution_delta_size = solution_delta.Size();
|
||||
const int kUnassigned = -1;
|
||||
@@ -640,8 +630,8 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
// Creates the solver.
|
||||
Solver solver("dobble");
|
||||
// Creates the matrix of boolean variables (cards * symbols).
|
||||
std::vector<std::vector<IntVar*> > card_symbol_vars(num_cards);
|
||||
std::vector<IntVar*> all_card_symbol_vars;
|
||||
std::vector<std::vector<IntVar *> > card_symbol_vars(num_cards);
|
||||
std::vector<IntVar *> all_card_symbol_vars;
|
||||
for (int card_index = 0; card_index < num_cards; ++card_index) {
|
||||
solver.MakeBoolVarArray(num_symbols,
|
||||
absl::StrFormat("card_%i_", card_index),
|
||||
@@ -653,7 +643,7 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
}
|
||||
// Creates cardinality intersection variables and remember the
|
||||
// violation variables.
|
||||
std::vector<IntVar*> violation_vars;
|
||||
std::vector<IntVar *> violation_vars;
|
||||
for (int card1 = 0; card1 < num_cards; ++card1) {
|
||||
for (int card2 = 0; card2 < num_cards; ++card2) {
|
||||
if (card1 != card2) {
|
||||
@@ -664,7 +654,7 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
}
|
||||
}
|
||||
// Create the objective variable.
|
||||
IntVar* const objective_var = solver.MakeSum(violation_vars)->Var();
|
||||
IntVar *const objective_var = solver.MakeSum(violation_vars)->Var();
|
||||
|
||||
// Add constraint: there must be exactly num_symbols_per_card
|
||||
// symbols per card.
|
||||
@@ -680,7 +670,7 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
// is a (non-trivial) consequence of the other constraints and of
|
||||
// the model. But adding it makes the search go faster.
|
||||
for (int symbol_index = 0; symbol_index < num_symbols; ++symbol_index) {
|
||||
std::vector<IntVar*> tmp;
|
||||
std::vector<IntVar *> tmp;
|
||||
for (int card_index = 0; card_index < num_cards; ++card_index) {
|
||||
tmp.push_back(card_symbol_vars[card_index][symbol_index]);
|
||||
}
|
||||
@@ -689,33 +679,35 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
|
||||
// Search.
|
||||
LOG(INFO) << "Solving with Local Search";
|
||||
LOG(INFO) << " - time limit = " << FLAGS_time_limit_in_ms << " ms";
|
||||
LOG(INFO) << " - time limit = " << absl::GetFlag(FLAGS_time_limit_in_ms)
|
||||
<< " ms";
|
||||
|
||||
// Start a DecisionBuilder phase to find a first solution, using the
|
||||
// strategy "Pick some random, yet unassigned card symbol variable
|
||||
// and set its value to 1".
|
||||
DecisionBuilder* const build_db = solver.MakePhase(
|
||||
all_card_symbol_vars, Solver::CHOOSE_RANDOM, // Solver::IntVarStrategy
|
||||
Solver::ASSIGN_MAX_VALUE); // Solver::IntValueStrategy
|
||||
DecisionBuilder *const build_db = solver.MakePhase(
|
||||
all_card_symbol_vars, Solver::CHOOSE_RANDOM, // Solver::IntVarStrategy
|
||||
Solver::ASSIGN_MAX_VALUE); // Solver::IntValueStrategy
|
||||
|
||||
// Creates local search operators.
|
||||
std::vector<LocalSearchOperator*> operators;
|
||||
LocalSearchOperator* const switch_operator = solver.RevAlloc(new SwapSymbols(
|
||||
std::vector<LocalSearchOperator *> operators;
|
||||
LocalSearchOperator *const switch_operator = solver.RevAlloc(new SwapSymbols(
|
||||
all_card_symbol_vars, num_cards, num_symbols, num_symbols_per_card));
|
||||
operators.push_back(switch_operator);
|
||||
LOG(INFO) << " - add switch operator";
|
||||
if (FLAGS_num_swaps > 0) {
|
||||
LocalSearchOperator* const swaps_operator = solver.RevAlloc(
|
||||
new SwapSymbolsOnCardPairs(all_card_symbol_vars, num_cards, num_symbols,
|
||||
num_symbols_per_card, FLAGS_num_swaps));
|
||||
if (absl::GetFlag(FLAGS_num_swaps) > 0) {
|
||||
LocalSearchOperator *const swaps_operator =
|
||||
solver.RevAlloc(new SwapSymbolsOnCardPairs(
|
||||
all_card_symbol_vars, num_cards, num_symbols, num_symbols_per_card,
|
||||
absl::GetFlag(FLAGS_num_swaps)));
|
||||
operators.push_back(swaps_operator);
|
||||
LOG(INFO) << " - add swaps operator with at most " << FLAGS_num_swaps
|
||||
<< " swaps";
|
||||
LOG(INFO) << " - add swaps operator with at most "
|
||||
<< absl::GetFlag(FLAGS_num_swaps) << " swaps";
|
||||
}
|
||||
|
||||
// Creates filter.
|
||||
std::vector<LocalSearchFilter*> filters;
|
||||
if (FLAGS_use_filter) {
|
||||
std::vector<LocalSearchFilter *> filters;
|
||||
if (absl::GetFlag(FLAGS_use_filter)) {
|
||||
filters.push_back(solver.RevAlloc(new DobbleFilter(
|
||||
all_card_symbol_vars, num_cards, num_symbols, num_symbols_per_card)));
|
||||
}
|
||||
@@ -723,42 +715,42 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) {
|
||||
// Main decision builder that regroups the first solution decision
|
||||
// builder and the combination of local search operators and
|
||||
// filters.
|
||||
LocalSearchFilterManager* filter_manager =
|
||||
LocalSearchFilterManager *filter_manager =
|
||||
solver.RevAlloc(new LocalSearchFilterManager(filters));
|
||||
DecisionBuilder* const final_db = solver.MakeLocalSearchPhase(
|
||||
DecisionBuilder *const final_db = solver.MakeLocalSearchPhase(
|
||||
all_card_symbol_vars, build_db,
|
||||
solver.MakeLocalSearchPhaseParameters(
|
||||
objective_var, solver.ConcatenateOperators(operators, true),
|
||||
nullptr, // Sub decision builder, not needed here.
|
||||
nullptr, // Limit the search for improving move, we will stop
|
||||
// the exploration of the local search at the first
|
||||
// improving solution (first accept).
|
||||
nullptr, // Sub decision builder, not needed here.
|
||||
nullptr, // Limit the search for improving move, we will stop
|
||||
// the exploration of the local search at the first
|
||||
// improving solution (first accept).
|
||||
filter_manager));
|
||||
|
||||
std::vector<SearchMonitor*> monitors;
|
||||
std::vector<SearchMonitor *> monitors;
|
||||
// Optimize var search monitor.
|
||||
OptimizeVar* const optimize = solver.MakeMinimize(objective_var, 1);
|
||||
OptimizeVar *const optimize = solver.MakeMinimize(objective_var, 1);
|
||||
monitors.push_back(optimize);
|
||||
|
||||
// Search log.
|
||||
SearchMonitor* const log = solver.MakeSearchLog(100000, optimize);
|
||||
SearchMonitor *const log = solver.MakeSearchLog(100000, optimize);
|
||||
monitors.push_back(log);
|
||||
|
||||
// Search limit.
|
||||
SearchLimit* const time_limit =
|
||||
solver.MakeLimit(FLAGS_time_limit_in_ms, kint64max, kint64max, kint64max);
|
||||
SearchLimit *const time_limit = solver.MakeLimit(
|
||||
absl::GetFlag(FLAGS_time_limit_in_ms), kint64max, kint64max, kint64max);
|
||||
monitors.push_back(time_limit);
|
||||
|
||||
// And solve!
|
||||
solver.Solve(final_db, monitors);
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
// These constants comes directly from the dobble game.
|
||||
// There are actually 55 cards, but we can create up to 57 cards.
|
||||
const int kSymbolsPerCard = FLAGS_symbols_per_card;
|
||||
const int kSymbolsPerCard = absl::GetFlag(FLAGS_symbols_per_card);
|
||||
const int kCards = kSymbolsPerCard * (kSymbolsPerCard - 1) + 1;
|
||||
const int kSymbols = kCards;
|
||||
operations_research::SolveDobble(kCards, kSymbols, kSymbolsPerCard);
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace operations_research {
|
||||
|
||||
// Prints the instance of the Frequency Assignment Problem.
|
||||
class FapModelPrinter {
|
||||
public:
|
||||
FapModelPrinter(const std::map<int, FapVariable>& variables,
|
||||
const std::vector<FapConstraint>& constraints,
|
||||
const std::string& objective, const std::vector<int>& values);
|
||||
public:
|
||||
FapModelPrinter(const std::map<int, FapVariable> &variables,
|
||||
const std::vector<FapConstraint> &constraints,
|
||||
const std::string &objective, const std::vector<int> &values);
|
||||
~FapModelPrinter();
|
||||
|
||||
void PrintFapObjective();
|
||||
@@ -41,7 +41,7 @@ class FapModelPrinter {
|
||||
void PrintFapConstraints();
|
||||
void PrintFapValues();
|
||||
|
||||
private:
|
||||
private:
|
||||
const std::map<int, FapVariable> variables_;
|
||||
const std::vector<FapConstraint> constraints_;
|
||||
const std::string objective_;
|
||||
@@ -49,20 +49,18 @@ class FapModelPrinter {
|
||||
DISALLOW_COPY_AND_ASSIGN(FapModelPrinter);
|
||||
};
|
||||
|
||||
FapModelPrinter::FapModelPrinter(const std::map<int, FapVariable>& variables,
|
||||
const std::vector<FapConstraint>& constraints,
|
||||
const std::string& objective,
|
||||
const std::vector<int>& values)
|
||||
: variables_(variables),
|
||||
constraints_(constraints),
|
||||
objective_(objective),
|
||||
FapModelPrinter::FapModelPrinter(const std::map<int, FapVariable> &variables,
|
||||
const std::vector<FapConstraint> &constraints,
|
||||
const std::string &objective,
|
||||
const std::vector<int> &values)
|
||||
: variables_(variables), constraints_(constraints), objective_(objective),
|
||||
values_(values) {}
|
||||
|
||||
FapModelPrinter::~FapModelPrinter() {}
|
||||
|
||||
void FapModelPrinter::PrintFapVariables() {
|
||||
LOG(INFO) << "Variable File:";
|
||||
for (const auto& it : variables_) {
|
||||
for (const auto &it : variables_) {
|
||||
std::string domain = "{";
|
||||
for (const int value : it.second.domain) {
|
||||
absl::StrAppendFormat(&domain, "%d ", value);
|
||||
@@ -87,7 +85,7 @@ void FapModelPrinter::PrintFapVariables() {
|
||||
|
||||
void FapModelPrinter::PrintFapConstraints() {
|
||||
LOG(INFO) << "Constraint File:";
|
||||
for (const FapConstraint& ct : constraints_) {
|
||||
for (const FapConstraint &ct : constraints_) {
|
||||
std::string hard = " ";
|
||||
if (ct.hard) {
|
||||
hard = " hard";
|
||||
@@ -114,5 +112,5 @@ void FapModelPrinter::PrintFapValues() {
|
||||
LOG(INFO) << domain;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_
|
||||
|
||||
@@ -34,20 +34,15 @@ namespace operations_research {
|
||||
|
||||
// Takes a filename and a buffer and fills the lines buffer
|
||||
// with the lines of the file corresponding to the filename.
|
||||
void ParseFileByLines(const std::string& filename,
|
||||
std::vector<std::string>* lines);
|
||||
void ParseFileByLines(const std::string &filename,
|
||||
std::vector<std::string> *lines);
|
||||
|
||||
// The FapVariable struct represents a radio link of the
|
||||
// frequency assignment problem.
|
||||
struct FapVariable {
|
||||
FapVariable()
|
||||
: domain_index(-1),
|
||||
domain_size(0),
|
||||
domain(),
|
||||
degree(0),
|
||||
initial_position(-1),
|
||||
mobility_index(-1),
|
||||
mobility_cost(-1),
|
||||
: domain_index(-1), domain_size(0), domain(), degree(0),
|
||||
initial_position(-1), mobility_index(-1), mobility_cost(-1),
|
||||
hard(false) {}
|
||||
|
||||
~FapVariable() {}
|
||||
@@ -84,15 +79,8 @@ struct FapVariable {
|
||||
// radio links of the frequency assignment problem.
|
||||
struct FapConstraint {
|
||||
FapConstraint()
|
||||
: variable1(-1),
|
||||
variable2(-1),
|
||||
impact(0),
|
||||
type(""),
|
||||
operation(""),
|
||||
value(-1),
|
||||
weight_index(-1),
|
||||
weight_cost(-1),
|
||||
hard(false) {}
|
||||
: variable1(-1), variable2(-1), impact(0), type(""), operation(""),
|
||||
value(-1), weight_index(-1), weight_cost(-1), hard(false) {}
|
||||
~FapConstraint() {}
|
||||
|
||||
// Fields:
|
||||
@@ -147,15 +135,15 @@ struct FapComponent {
|
||||
// This file describes all the variables in the instance.
|
||||
// Each line corresponds to one variable.
|
||||
class VariableParser {
|
||||
public:
|
||||
explicit VariableParser(const std::string& data_directory);
|
||||
public:
|
||||
explicit VariableParser(const std::string &data_directory);
|
||||
~VariableParser();
|
||||
|
||||
const std::map<int, FapVariable>& variables() const { return variables_; }
|
||||
const std::map<int, FapVariable> &variables() const { return variables_; }
|
||||
|
||||
void Parse();
|
||||
|
||||
private:
|
||||
private:
|
||||
const std::string filename_;
|
||||
// A map is used because in the model, the variables have ids which may not
|
||||
// be consecutive, may be very sparse and don't have a specific upper-bound.
|
||||
@@ -169,15 +157,15 @@ class VariableParser {
|
||||
// This file describes the domains used by the variables of the problem.
|
||||
// Each line describes one domain.
|
||||
class DomainParser {
|
||||
public:
|
||||
explicit DomainParser(const std::string& data_directory);
|
||||
public:
|
||||
explicit DomainParser(const std::string &data_directory);
|
||||
~DomainParser();
|
||||
|
||||
const std::map<int, std::vector<int> >& domains() const { return domains_; }
|
||||
const std::map<int, std::vector<int> > &domains() const { return domains_; }
|
||||
|
||||
void Parse();
|
||||
|
||||
private:
|
||||
private:
|
||||
const std::string filename_;
|
||||
// A map is used because in the model, the ids of the different available
|
||||
// domains may be random values, since they are used as names. The key of the
|
||||
@@ -191,15 +179,15 @@ class DomainParser {
|
||||
// This file describes the constraints of the instance.
|
||||
// Each line defines a binary constraint.
|
||||
class ConstraintParser {
|
||||
public:
|
||||
explicit ConstraintParser(const std::string& data_directory);
|
||||
public:
|
||||
explicit ConstraintParser(const std::string &data_directory);
|
||||
~ConstraintParser();
|
||||
|
||||
const std::vector<FapConstraint>& constraints() const { return constraints_; }
|
||||
const std::vector<FapConstraint> &constraints() const { return constraints_; }
|
||||
|
||||
void Parse();
|
||||
|
||||
private:
|
||||
private:
|
||||
const std::string filename_;
|
||||
std::vector<FapConstraint> constraints_;
|
||||
|
||||
@@ -211,19 +199,19 @@ class ConstraintParser {
|
||||
// It may also contain 8 coefficients: 4 for different constraint violation
|
||||
// costs and 4 for different variable mobility costs.
|
||||
class ParametersParser {
|
||||
public:
|
||||
explicit ParametersParser(const std::string& data_directory);
|
||||
public:
|
||||
explicit ParametersParser(const std::string &data_directory);
|
||||
~ParametersParser();
|
||||
|
||||
std::string objective() const { return objective_; }
|
||||
const std::vector<int>& constraint_weights() const {
|
||||
const std::vector<int> &constraint_weights() const {
|
||||
return constraint_weights_;
|
||||
}
|
||||
const std::vector<int>& variable_weights() const { return variable_weights_; }
|
||||
const std::vector<int> &variable_weights() const { return variable_weights_; }
|
||||
|
||||
void Parse();
|
||||
|
||||
private:
|
||||
private:
|
||||
const std::string filename_;
|
||||
static const int constraint_coefficient_no_ = 4;
|
||||
static const int variable_coefficient_no_ = 4;
|
||||
@@ -234,33 +222,33 @@ class ParametersParser {
|
||||
};
|
||||
|
||||
namespace {
|
||||
int strtoint32(const std::string& word) {
|
||||
int strtoint32(const std::string &word) {
|
||||
int result;
|
||||
CHECK(absl::SimpleAtoi(word, &result));
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
// Function that finds the disjoint sub-graphs of the graph of the instance.
|
||||
void FindComponents(const std::vector<FapConstraint>& constraints,
|
||||
const std::map<int, FapVariable>& variables,
|
||||
void FindComponents(const std::vector<FapConstraint> &constraints,
|
||||
const std::map<int, FapVariable> &variables,
|
||||
const int maximum_variable_id,
|
||||
absl::flat_hash_map<int, FapComponent>* components);
|
||||
absl::flat_hash_map<int, FapComponent> *components);
|
||||
|
||||
// Function that computes the impact of a constraint.
|
||||
int EvaluateConstraintImpact(const std::map<int, FapVariable>& variables,
|
||||
int EvaluateConstraintImpact(const std::map<int, FapVariable> &variables,
|
||||
const int max_weight_cost,
|
||||
const FapConstraint constraint);
|
||||
|
||||
// Function that parses an instance of frequency assignment problem.
|
||||
void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
std::map<int, FapVariable>* variables,
|
||||
std::vector<FapConstraint>* constraints,
|
||||
std::string* objective, std::vector<int>* frequencies,
|
||||
absl::flat_hash_map<int, FapComponent>* components);
|
||||
void ParseInstance(const std::string &data_directory, bool find_components,
|
||||
std::map<int, FapVariable> *variables,
|
||||
std::vector<FapConstraint> *constraints,
|
||||
std::string *objective, std::vector<int> *frequencies,
|
||||
absl::flat_hash_map<int, FapComponent> *components);
|
||||
|
||||
void ParseFileByLines(const std::string& filename,
|
||||
std::vector<std::string>* lines) {
|
||||
void ParseFileByLines(const std::string &filename,
|
||||
std::vector<std::string> *lines) {
|
||||
CHECK(lines != nullptr);
|
||||
std::string result;
|
||||
CHECK_OK(file::GetContents(filename, &result, file::Defaults()));
|
||||
@@ -268,7 +256,7 @@ void ParseFileByLines(const std::string& filename,
|
||||
}
|
||||
|
||||
// VariableParser Implementation
|
||||
VariableParser::VariableParser(const std::string& data_directory)
|
||||
VariableParser::VariableParser(const std::string &data_directory)
|
||||
: filename_(data_directory + "/var.txt") {}
|
||||
|
||||
VariableParser::~VariableParser() {}
|
||||
@@ -276,7 +264,7 @@ VariableParser::~VariableParser() {}
|
||||
void VariableParser::Parse() {
|
||||
std::vector<std::string> lines;
|
||||
ParseFileByLines(filename_, &lines);
|
||||
for (const std::string& line : lines) {
|
||||
for (const std::string &line : lines) {
|
||||
std::vector<std::string> tokens =
|
||||
absl::StrSplit(line, ' ', absl::SkipEmpty());
|
||||
if (tokens.empty()) {
|
||||
@@ -295,7 +283,7 @@ void VariableParser::Parse() {
|
||||
}
|
||||
|
||||
// DomainParser Implementation
|
||||
DomainParser::DomainParser(const std::string& data_directory)
|
||||
DomainParser::DomainParser(const std::string &data_directory)
|
||||
: filename_(data_directory + "/dom.txt") {}
|
||||
|
||||
DomainParser::~DomainParser() {}
|
||||
@@ -303,7 +291,7 @@ DomainParser::~DomainParser() {}
|
||||
void DomainParser::Parse() {
|
||||
std::vector<std::string> lines;
|
||||
ParseFileByLines(filename_, &lines);
|
||||
for (const std::string& line : lines) {
|
||||
for (const std::string &line : lines) {
|
||||
std::vector<std::string> tokens =
|
||||
absl::StrSplit(line, ' ', absl::SkipEmpty());
|
||||
if (tokens.empty()) {
|
||||
@@ -326,7 +314,7 @@ void DomainParser::Parse() {
|
||||
}
|
||||
|
||||
// ConstraintParser Implementation
|
||||
ConstraintParser::ConstraintParser(const std::string& data_directory)
|
||||
ConstraintParser::ConstraintParser(const std::string &data_directory)
|
||||
: filename_(data_directory + "/ctr.txt") {}
|
||||
|
||||
ConstraintParser::~ConstraintParser() {}
|
||||
@@ -334,7 +322,7 @@ ConstraintParser::~ConstraintParser() {}
|
||||
void ConstraintParser::Parse() {
|
||||
std::vector<std::string> lines;
|
||||
ParseFileByLines(filename_, &lines);
|
||||
for (const std::string& line : lines) {
|
||||
for (const std::string &line : lines) {
|
||||
std::vector<std::string> tokens =
|
||||
absl::StrSplit(line, ' ', absl::SkipEmpty());
|
||||
if (tokens.empty()) {
|
||||
@@ -361,9 +349,8 @@ const int ParametersParser::constraint_coefficient_no_;
|
||||
const int ParametersParser::variable_coefficient_no_;
|
||||
const int ParametersParser::coefficient_no_;
|
||||
|
||||
ParametersParser::ParametersParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/cst.txt"),
|
||||
objective_(""),
|
||||
ParametersParser::ParametersParser(const std::string &data_directory)
|
||||
: filename_(data_directory + "/cst.txt"), objective_(""),
|
||||
constraint_weights_(constraint_coefficient_no_, 0),
|
||||
variable_weights_(variable_coefficient_no_, 0) {}
|
||||
|
||||
@@ -380,7 +367,7 @@ void ParametersParser::Parse() {
|
||||
std::vector<std::string> lines;
|
||||
|
||||
ParseFileByLines(filename_, &lines);
|
||||
for (const std::string& line : lines) {
|
||||
for (const std::string &line : lines) {
|
||||
if (objective) {
|
||||
largest_token =
|
||||
largest_token || (line.find("largest") != std::string::npos);
|
||||
@@ -426,17 +413,17 @@ void ParametersParser::Parse() {
|
||||
}
|
||||
|
||||
// TODO(user): Make FindComponents linear instead of quadratic.
|
||||
void FindComponents(const std::vector<FapConstraint>& constraints,
|
||||
const std::map<int, FapVariable>& variables,
|
||||
void FindComponents(const std::vector<FapConstraint> &constraints,
|
||||
const std::map<int, FapVariable> &variables,
|
||||
const int maximum_variable_id,
|
||||
absl::flat_hash_map<int, FapComponent>* components) {
|
||||
absl::flat_hash_map<int, FapComponent> *components) {
|
||||
std::vector<int> in_component(maximum_variable_id + 1, -1);
|
||||
int constraint_index = 0;
|
||||
for (const FapConstraint& constraint : constraints) {
|
||||
for (const FapConstraint &constraint : constraints) {
|
||||
const int variable_id1 = constraint.variable1;
|
||||
const int variable_id2 = constraint.variable2;
|
||||
const FapVariable& variable1 = gtl::FindOrDie(variables, variable_id1);
|
||||
const FapVariable& variable2 = gtl::FindOrDie(variables, variable_id2);
|
||||
const FapVariable &variable1 = gtl::FindOrDie(variables, variable_id1);
|
||||
const FapVariable &variable2 = gtl::FindOrDie(variables, variable_id2);
|
||||
CHECK_LT(variable_id1, in_component.size());
|
||||
CHECK_LT(variable_id2, in_component.size());
|
||||
if (in_component[variable_id1] < 0 && in_component[variable_id2] < 0) {
|
||||
@@ -482,24 +469,22 @@ void FindComponents(const std::vector<FapConstraint>& constraints,
|
||||
CHECK(gtl::ContainsKey(*components, max_component_index));
|
||||
if (min_component_index != max_component_index) {
|
||||
// Update the component_index of maximum indexed component's variables.
|
||||
for (const auto& variable :
|
||||
for (const auto &variable :
|
||||
(*components)[max_component_index].variables) {
|
||||
int variable_id = variable.first;
|
||||
in_component[variable_id] = min_component_index;
|
||||
}
|
||||
// Insert all the variables of the maximum indexed component to the
|
||||
// variables of the minimum indexed component.
|
||||
((*components)[min_component_index])
|
||||
.variables.insert(
|
||||
((*components)[max_component_index]).variables.begin(),
|
||||
((*components)[max_component_index]).variables.end());
|
||||
((*components)[min_component_index]).variables
|
||||
.insert(((*components)[max_component_index]).variables.begin(),
|
||||
((*components)[max_component_index]).variables.end());
|
||||
// Insert all the constraints of the maximum indexed component to the
|
||||
// constraints of the minimum indexed component.
|
||||
((*components)[min_component_index])
|
||||
.constraints.insert(
|
||||
((*components)[min_component_index]).constraints.end(),
|
||||
((*components)[max_component_index]).constraints.begin(),
|
||||
((*components)[max_component_index]).constraints.end());
|
||||
((*components)[min_component_index]).constraints
|
||||
.insert(((*components)[min_component_index]).constraints.end(),
|
||||
((*components)[max_component_index]).constraints.begin(),
|
||||
((*components)[max_component_index]).constraints.end());
|
||||
(*components)[min_component_index].constraints.push_back(constraint);
|
||||
// Delete the maximum indexed component from the components set.
|
||||
components->erase(max_component_index);
|
||||
@@ -512,12 +497,12 @@ void FindComponents(const std::vector<FapConstraint>& constraints,
|
||||
}
|
||||
}
|
||||
|
||||
int EvaluateConstraintImpact(const std::map<int, FapVariable>& variables,
|
||||
int EvaluateConstraintImpact(const std::map<int, FapVariable> &variables,
|
||||
const int max_weight_cost,
|
||||
const FapConstraint constraint) {
|
||||
const FapVariable& variable1 =
|
||||
const FapVariable &variable1 =
|
||||
gtl::FindOrDie(variables, constraint.variable1);
|
||||
const FapVariable& variable2 =
|
||||
const FapVariable &variable2 =
|
||||
gtl::FindOrDie(variables, constraint.variable2);
|
||||
const int degree1 = variable1.degree;
|
||||
const int degree2 = variable2.degree;
|
||||
@@ -535,11 +520,11 @@ int EvaluateConstraintImpact(const std::map<int, FapVariable>& variables,
|
||||
return max_degree + min_degree + operator_impact + hardness_impact;
|
||||
}
|
||||
|
||||
void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
std::map<int, FapVariable>* variables,
|
||||
std::vector<FapConstraint>* constraints,
|
||||
std::string* objective, std::vector<int>* frequencies,
|
||||
absl::flat_hash_map<int, FapComponent>* components) {
|
||||
void ParseInstance(const std::string &data_directory, bool find_components,
|
||||
std::map<int, FapVariable> *variables,
|
||||
std::vector<FapConstraint> *constraints,
|
||||
std::string *objective, std::vector<int> *frequencies,
|
||||
absl::flat_hash_map<int, FapComponent> *components) {
|
||||
CHECK(variables != nullptr);
|
||||
CHECK(constraints != nullptr);
|
||||
CHECK(objective != nullptr);
|
||||
@@ -564,7 +549,7 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
(cst.constraint_weights()).begin(), (cst.constraint_weights()).end());
|
||||
|
||||
// Make the variables of the instance.
|
||||
for (auto& it : *variables) {
|
||||
for (auto &it : *variables) {
|
||||
it.second.domain = gtl::FindOrDie(dom.domains(), it.second.domain_index);
|
||||
it.second.domain_size = it.second.domain.size();
|
||||
|
||||
@@ -579,7 +564,7 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
}
|
||||
}
|
||||
// Make the constraints of the instance.
|
||||
for (FapConstraint& ct : *constraints) {
|
||||
for (FapConstraint &ct : *constraints) {
|
||||
if ((ct.weight_index == -1) || (ct.weight_index == 0)) {
|
||||
ct.weight_cost = -1;
|
||||
ct.hard = true;
|
||||
@@ -599,18 +584,18 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
CHECK(components != nullptr);
|
||||
FindComponents(*constraints, *variables, maximum_variable_id, components);
|
||||
// Evaluate each components's constraints impacts.
|
||||
for (auto& component : *components) {
|
||||
for (auto& constraint : component.second.constraints) {
|
||||
for (auto &component : *components) {
|
||||
for (auto &constraint : component.second.constraints) {
|
||||
constraint.impact = EvaluateConstraintImpact(
|
||||
*variables, maximum_weight_cost, constraint);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (FapConstraint& constraint : *constraints) {
|
||||
for (FapConstraint &constraint : *constraints) {
|
||||
constraint.impact =
|
||||
EvaluateConstraintImpact(*variables, maximum_weight_cost, constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_
|
||||
|
||||
@@ -33,48 +33,48 @@ namespace operations_research {
|
||||
// Checks if the solution given from the Solver satisfies all
|
||||
// the hard binary constraints specified in the ctr.txt.
|
||||
bool CheckConstraintSatisfaction(
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::vector<int>& variables,
|
||||
const std::map<int, int>& index_from_key);
|
||||
const std::vector<FapConstraint> &data_constraints,
|
||||
const std::vector<int> &variables,
|
||||
const std::map<int, int> &index_from_key);
|
||||
|
||||
// Checks if the solution given from the Solver has not modified the values of
|
||||
// the variables that were initially assigned and denoted as hard in var.txt.
|
||||
bool CheckVariablePosition(const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<int>& variables,
|
||||
const std::map<int, int>& index_from_key);
|
||||
bool CheckVariablePosition(const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<int> &variables,
|
||||
const std::map<int, int> &index_from_key);
|
||||
|
||||
// Counts the number of different values in the variable vector.
|
||||
int NumberOfAssignedValues(const std::vector<int>& variables);
|
||||
int NumberOfAssignedValues(const std::vector<int> &variables);
|
||||
|
||||
// Prints the duration of the solving process.
|
||||
void PrintElapsedTime(const int64 time1, const int64 time2);
|
||||
|
||||
// Prints the solution found by the Hard Solver for feasible instances.
|
||||
void PrintResultsHard(SolutionCollector* const collector,
|
||||
const std::vector<IntVar*>& variables,
|
||||
IntVar* const objective_var,
|
||||
const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::map<int, int>& index_from_key,
|
||||
const std::vector<int>& key_from_index);
|
||||
void PrintResultsHard(SolutionCollector *const collector,
|
||||
const std::vector<IntVar *> &variables,
|
||||
IntVar *const objective_var,
|
||||
const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<FapConstraint> &data_constraints,
|
||||
const std::map<int, int> &index_from_key,
|
||||
const std::vector<int> &key_from_index);
|
||||
|
||||
// Prints the solution found by the Soft Solver for unfeasible instances.
|
||||
void PrintResultsSoft(SolutionCollector* const collector,
|
||||
const std::vector<IntVar*>& variables,
|
||||
IntVar* const total_cost,
|
||||
const std::map<int, FapVariable>& hard_variables,
|
||||
const std::vector<FapConstraint>& hard_constraints,
|
||||
const std::map<int, FapVariable>& soft_variables,
|
||||
const std::vector<FapConstraint>& soft_constraints,
|
||||
const std::map<int, int>& index_from_key,
|
||||
const std::vector<int>& key_from_index);
|
||||
void PrintResultsSoft(SolutionCollector *const collector,
|
||||
const std::vector<IntVar *> &variables,
|
||||
IntVar *const total_cost,
|
||||
const std::map<int, FapVariable> &hard_variables,
|
||||
const std::vector<FapConstraint> &hard_constraints,
|
||||
const std::map<int, FapVariable> &soft_variables,
|
||||
const std::vector<FapConstraint> &soft_constraints,
|
||||
const std::map<int, int> &index_from_key,
|
||||
const std::vector<int> &key_from_index);
|
||||
|
||||
bool CheckConstraintSatisfaction(
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::vector<int>& variables,
|
||||
const std::map<int, int>& index_from_key) {
|
||||
bool
|
||||
CheckConstraintSatisfaction(const std::vector<FapConstraint> &data_constraints,
|
||||
const std::vector<int> &variables,
|
||||
const std::map<int, int> &index_from_key) {
|
||||
bool status = true;
|
||||
for (const FapConstraint& ct : data_constraints) {
|
||||
for (const FapConstraint &ct : data_constraints) {
|
||||
const int index1 = gtl::FindOrDie(index_from_key, ct.variable1);
|
||||
const int index2 = gtl::FindOrDie(index_from_key, ct.variable2);
|
||||
CHECK_LT(index1, variables.size());
|
||||
@@ -100,11 +100,11 @@ bool CheckConstraintSatisfaction(
|
||||
return status;
|
||||
}
|
||||
|
||||
bool CheckVariablePosition(const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<int>& variables,
|
||||
const std::map<int, int>& index_from_key) {
|
||||
bool CheckVariablePosition(const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<int> &variables,
|
||||
const std::map<int, int> &index_from_key) {
|
||||
bool status = true;
|
||||
for (const auto& it : data_variables) {
|
||||
for (const auto &it : data_variables) {
|
||||
const int index = gtl::FindOrDie(index_from_key, it.first);
|
||||
CHECK_LT(index, variables.size());
|
||||
const int var = variables[index];
|
||||
@@ -120,7 +120,7 @@ bool CheckVariablePosition(const std::map<int, FapVariable>& data_variables,
|
||||
return status;
|
||||
}
|
||||
|
||||
int NumberOfAssignedValues(const std::vector<int>& variables) {
|
||||
int NumberOfAssignedValues(const std::vector<int> &variables) {
|
||||
std::set<int> assigned(variables.begin(), variables.end());
|
||||
return static_cast<int>(assigned.size());
|
||||
}
|
||||
@@ -131,18 +131,18 @@ void PrintElapsedTime(const int64 time1, const int64 time2) {
|
||||
<< " seconds.";
|
||||
}
|
||||
|
||||
void PrintResultsHard(SolutionCollector* const collector,
|
||||
const std::vector<IntVar*>& variables,
|
||||
IntVar* const objective_var,
|
||||
const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::map<int, int>& index_from_key,
|
||||
const std::vector<int>& key_from_index) {
|
||||
void PrintResultsHard(SolutionCollector *const collector,
|
||||
const std::vector<IntVar *> &variables,
|
||||
IntVar *const objective_var,
|
||||
const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<FapConstraint> &data_constraints,
|
||||
const std::map<int, int> &index_from_key,
|
||||
const std::vector<int> &key_from_index) {
|
||||
LOG(INFO) << "Printing...";
|
||||
LOG(INFO) << "Number of Solutions: " << collector->solution_count();
|
||||
for (int solution_index = 0; solution_index < collector->solution_count();
|
||||
++solution_index) {
|
||||
Assignment* const solution = collector->solution(solution_index);
|
||||
Assignment *const solution = collector->solution(solution_index);
|
||||
std::vector<int> results(variables.size());
|
||||
LOG(INFO) << "------------------------------------------------------------";
|
||||
LOG(INFO) << "Solution " << solution_index + 1;
|
||||
@@ -164,27 +164,27 @@ void PrintResultsHard(SolutionCollector* const collector,
|
||||
}
|
||||
|
||||
LOG(INFO) << "Values used: " << NumberOfAssignedValues(results);
|
||||
LOG(INFO) << "Maximum value used: "
|
||||
<< *std::max_element(results.begin(), results.end());
|
||||
LOG(INFO) << "Maximum value used: " << *std::max_element(results.begin(),
|
||||
results.end());
|
||||
LOG(INFO) << " Failures: " << collector->failures(solution_index);
|
||||
}
|
||||
LOG(INFO) << " ============================================================";
|
||||
}
|
||||
|
||||
void PrintResultsSoft(SolutionCollector* const collector,
|
||||
const std::vector<IntVar*>& variables,
|
||||
IntVar* const total_cost,
|
||||
const std::map<int, FapVariable>& hard_variables,
|
||||
const std::vector<FapConstraint>& hard_constraints,
|
||||
const std::map<int, FapVariable>& soft_variables,
|
||||
const std::vector<FapConstraint>& soft_constraints,
|
||||
const std::map<int, int>& index_from_key,
|
||||
const std::vector<int>& key_from_index) {
|
||||
void PrintResultsSoft(SolutionCollector *const collector,
|
||||
const std::vector<IntVar *> &variables,
|
||||
IntVar *const total_cost,
|
||||
const std::map<int, FapVariable> &hard_variables,
|
||||
const std::vector<FapConstraint> &hard_constraints,
|
||||
const std::map<int, FapVariable> &soft_variables,
|
||||
const std::vector<FapConstraint> &soft_constraints,
|
||||
const std::map<int, int> &index_from_key,
|
||||
const std::vector<int> &key_from_index) {
|
||||
LOG(INFO) << "Printing...";
|
||||
LOG(INFO) << "Number of Solutions: " << collector->solution_count();
|
||||
for (int solution_index = 0; solution_index < collector->solution_count();
|
||||
++solution_index) {
|
||||
Assignment* const solution = collector->solution(solution_index);
|
||||
Assignment *const solution = collector->solution(solution_index);
|
||||
std::vector<int> results(variables.size());
|
||||
LOG(INFO) << "------------------------------------------------------------";
|
||||
LOG(INFO) << "Solution";
|
||||
@@ -216,12 +216,12 @@ void PrintResultsSoft(SolutionCollector* const collector,
|
||||
}
|
||||
|
||||
LOG(INFO) << "Values used: " << NumberOfAssignedValues(results);
|
||||
LOG(INFO) << "Maximum value used: "
|
||||
<< *std::max_element(results.begin(), results.end());
|
||||
LOG(INFO) << "Maximum value used: " << *std::max_element(results.begin(),
|
||||
results.end());
|
||||
LOG(INFO) << " Failures: " << collector->failures(solution_index);
|
||||
}
|
||||
LOG(INFO) << " ============================================================";
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_
|
||||
|
||||
@@ -27,10 +27,10 @@ void MinCostFlowOn4x4Matrix() {
|
||||
LOG(INFO) << "Min Cost Flow on 4x4 Matrix";
|
||||
const int kNumSources = 4;
|
||||
const int kNumTargets = 4;
|
||||
const CostValue kCost[kNumSources][kNumTargets] = {{90, 75, 75, 80},
|
||||
{35, 85, 55, 65},
|
||||
{125, 95, 90, 105},
|
||||
{45, 110, 95, 115}};
|
||||
const CostValue kCost[kNumSources][kNumTargets] = { { 90, 75, 75, 80 },
|
||||
{ 35, 85, 55, 65 },
|
||||
{ 125, 95, 90, 105 },
|
||||
{ 45, 110, 95, 115 } };
|
||||
const CostValue kExpectedCost = 275;
|
||||
StarGraph graph(kNumSources + kNumTargets, kNumSources * kNumTargets);
|
||||
MinCostFlow min_cost_flow(&graph);
|
||||
@@ -59,10 +59,10 @@ void MaxFeasibleFlow() {
|
||||
LOG(INFO) << "Max Feasible Flow";
|
||||
const int kNumNodes = 6;
|
||||
const int kNumArcs = 9;
|
||||
const NodeIndex kTail[kNumArcs] = {0, 0, 0, 0, 1, 2, 3, 3, 4};
|
||||
const NodeIndex kHead[kNumArcs] = {1, 2, 3, 4, 3, 4, 4, 5, 5};
|
||||
const FlowQuantity kCapacity[kNumArcs] = {5, 8, 5, 3, 4, 5, 6, 6, 4};
|
||||
const FlowQuantity kExpectedFlow[kNumArcs] = {1, 1, 5, 3, 1, 1, 0, 6, 4};
|
||||
const NodeIndex kTail[kNumArcs] = { 0, 0, 0, 0, 1, 2, 3, 3, 4 };
|
||||
const NodeIndex kHead[kNumArcs] = { 1, 2, 3, 4, 3, 4, 4, 5, 5 };
|
||||
const FlowQuantity kCapacity[kNumArcs] = { 5, 8, 5, 3, 4, 5, 6, 6, 4 };
|
||||
const FlowQuantity kExpectedFlow[kNumArcs] = { 1, 1, 5, 3, 1, 1, 0, 6, 4 };
|
||||
const FlowQuantity kExpectedTotalFlow = 10;
|
||||
StarGraph graph(kNumNodes, kNumArcs);
|
||||
MaxFlow max_flow(&graph, 0, kNumNodes - 1);
|
||||
@@ -78,9 +78,9 @@ void MaxFeasibleFlow() {
|
||||
CHECK_EQ(kExpectedFlow[i], max_flow.Flow(i)) << " i = " << i;
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::MinCostFlowOn4x4Matrix();
|
||||
operations_research::MaxFeasibleFlow();
|
||||
|
||||
@@ -89,42 +89,40 @@ namespace operations_research {
|
||||
// Decision on the relative order that the two variables of a constraint
|
||||
// will have. It takes as parameters the components of the constraint.
|
||||
class OrderingDecision : public Decision {
|
||||
public:
|
||||
OrderingDecision(IntVar* const variable1, IntVar* const variable2, int value,
|
||||
public:
|
||||
OrderingDecision(IntVar *const variable1, IntVar *const variable2, int value,
|
||||
std::string operation)
|
||||
: variable1_(variable1),
|
||||
variable2_(variable2),
|
||||
value_(value),
|
||||
: variable1_(variable1), variable2_(variable2), value_(value),
|
||||
operator_(std::move(operation)) {}
|
||||
~OrderingDecision() override {}
|
||||
|
||||
// Apply will be called first when the decision is executed.
|
||||
void Apply(Solver* const s) override {
|
||||
void Apply(Solver *const s) override {
|
||||
// variable1 < variable2
|
||||
MakeDecision(s, variable1_, variable2_);
|
||||
}
|
||||
|
||||
// Refute will be called after a backtrack.
|
||||
void Refute(Solver* const s) override {
|
||||
void Refute(Solver *const s) override {
|
||||
// variable1 > variable2
|
||||
MakeDecision(s, variable2_, variable1_);
|
||||
}
|
||||
|
||||
private:
|
||||
void MakeDecision(Solver* s, IntVar* variable1, IntVar* variable2) {
|
||||
private:
|
||||
void MakeDecision(Solver *s, IntVar *variable1, IntVar *variable2) {
|
||||
if (operator_ == ">") {
|
||||
IntExpr* difference = (s->MakeDifference(variable2, variable1));
|
||||
IntExpr *difference = (s->MakeDifference(variable2, variable1));
|
||||
s->AddConstraint(s->MakeGreater(difference, value_));
|
||||
} else if (operator_ == "=") {
|
||||
IntExpr* difference = (s->MakeDifference(variable2, variable1));
|
||||
IntExpr *difference = (s->MakeDifference(variable2, variable1));
|
||||
s->AddConstraint(s->MakeEquality(difference, value_));
|
||||
} else {
|
||||
LOG(FATAL) << "No right operator specified.";
|
||||
}
|
||||
}
|
||||
|
||||
IntVar* const variable1_;
|
||||
IntVar* const variable2_;
|
||||
IntVar *const variable1_;
|
||||
IntVar *const variable2_;
|
||||
const int value_;
|
||||
const std::string operator_;
|
||||
|
||||
@@ -134,26 +132,26 @@ class OrderingDecision : public Decision {
|
||||
// Decision on whether a soft constraint will be added to a model
|
||||
// or if it will be violated.
|
||||
class ConstraintDecision : public Decision {
|
||||
public:
|
||||
explicit ConstraintDecision(IntVar* const constraint_violation)
|
||||
public:
|
||||
explicit ConstraintDecision(IntVar *const constraint_violation)
|
||||
: constraint_violation_(constraint_violation) {}
|
||||
|
||||
~ConstraintDecision() override {}
|
||||
|
||||
// Apply will be called first when the decision is executed.
|
||||
void Apply(Solver* const s) override {
|
||||
void Apply(Solver *const s) override {
|
||||
// The constraint with which the builder is dealing, will be satisfied.
|
||||
constraint_violation_->SetValue(0);
|
||||
}
|
||||
|
||||
// Refute will be called after a backtrack.
|
||||
void Refute(Solver* const s) override {
|
||||
void Refute(Solver *const s) override {
|
||||
// The constraint with which the builder is dealing, will not be satisfied.
|
||||
constraint_violation_->SetValue(1);
|
||||
}
|
||||
|
||||
private:
|
||||
IntVar* const constraint_violation_;
|
||||
private:
|
||||
IntVar *const constraint_violation_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ConstraintDecision);
|
||||
};
|
||||
@@ -163,23 +161,23 @@ class ConstraintDecision : public Decision {
|
||||
// solving becomes much more efficient since we are branching on the
|
||||
// disjunction implied by the absolute value expression.
|
||||
class OrderingBuilder : public DecisionBuilder {
|
||||
public:
|
||||
enum Order { LESS = -1, EQUAL = 0, GREATER = 1 };
|
||||
public:
|
||||
enum Order {
|
||||
LESS = -1,
|
||||
EQUAL = 0,
|
||||
GREATER = 1
|
||||
};
|
||||
|
||||
OrderingBuilder(const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::vector<IntVar*>& variables,
|
||||
const std::vector<IntVar*>& violated_constraints,
|
||||
const std::map<int, int>& index_from_key)
|
||||
: data_variables_(data_variables),
|
||||
data_constraints_(data_constraints),
|
||||
variables_(variables),
|
||||
violated_constraints_(violated_constraints),
|
||||
index_from_key_(index_from_key),
|
||||
size_(data_constraints.size()),
|
||||
iter_(0),
|
||||
checked_iter_(0) {
|
||||
for (const auto& it : data_variables_) {
|
||||
OrderingBuilder(const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<FapConstraint> &data_constraints,
|
||||
const std::vector<IntVar *> &variables,
|
||||
const std::vector<IntVar *> &violated_constraints,
|
||||
const std::map<int, int> &index_from_key)
|
||||
: data_variables_(data_variables), data_constraints_(data_constraints),
|
||||
variables_(variables), violated_constraints_(violated_constraints),
|
||||
index_from_key_(index_from_key), size_(data_constraints.size()),
|
||||
iter_(0), checked_iter_(0) {
|
||||
for (const auto &it : data_variables_) {
|
||||
int first_element = (it.second.domain)[0];
|
||||
minimum_value_available_.push_back(first_element);
|
||||
variable_state_.push_back(EQUAL);
|
||||
@@ -190,13 +188,13 @@ class OrderingBuilder : public DecisionBuilder {
|
||||
|
||||
~OrderingBuilder() override {}
|
||||
|
||||
Decision* Next(Solver* const s) override {
|
||||
Decision *Next(Solver *const s) override {
|
||||
if (iter_ < size_) {
|
||||
FapConstraint constraint = data_constraints_[iter_];
|
||||
const int index1 = gtl::FindOrDie(index_from_key_, constraint.variable1);
|
||||
const int index2 = gtl::FindOrDie(index_from_key_, constraint.variable2);
|
||||
IntVar* variable1 = variables_[index1];
|
||||
IntVar* variable2 = variables_[index2];
|
||||
IntVar *variable1 = variables_[index1];
|
||||
IntVar *variable2 = variables_[index2];
|
||||
|
||||
// checked_iter is equal to 0 means that whether the constraint is to be
|
||||
// added or dropped hasn't been checked.
|
||||
@@ -204,7 +202,7 @@ class OrderingBuilder : public DecisionBuilder {
|
||||
// of the constraint is to be done.
|
||||
if (!checked_iter_ && !constraint.hard) {
|
||||
// New Soft Constraint: Check if it will be added or dropped.
|
||||
ConstraintDecision* constraint_decision =
|
||||
ConstraintDecision *constraint_decision =
|
||||
new ConstraintDecision(violated_constraints_[iter_]);
|
||||
|
||||
s->SaveAndAdd(&checked_iter_, 1);
|
||||
@@ -215,7 +213,7 @@ class OrderingBuilder : public DecisionBuilder {
|
||||
if (violated_constraints_[iter_]->Bound() &&
|
||||
violated_constraints_[iter_]->Value() == 0) {
|
||||
// If the constraint is added, do the ordering of its variables.
|
||||
OrderingDecision* ordering_decision;
|
||||
OrderingDecision *ordering_decision;
|
||||
Order hint = Hint(constraint);
|
||||
if (hint == LESS || hint == EQUAL) {
|
||||
ordering_decision = new OrderingDecision(
|
||||
@@ -239,7 +237,7 @@ class OrderingBuilder : public DecisionBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
Order Variable1LessVariable2(const int variable1, const int variable2,
|
||||
const int value) {
|
||||
minimum_value_available_[variable2] =
|
||||
@@ -268,7 +266,7 @@ class OrderingBuilder : public DecisionBuilder {
|
||||
// variable2. This ordering is more efficient if used with the
|
||||
// Solver::ASSIGN_MIN_VALUE value selection strategy.
|
||||
// It returns 1 if variable1 > variable2 or -1 if variable1 < variable2.
|
||||
Order Hint(const FapConstraint& constraint) {
|
||||
Order Hint(const FapConstraint &constraint) {
|
||||
const int id1 = constraint.variable1;
|
||||
const int id2 = constraint.variable2;
|
||||
const int variable1 = gtl::FindOrDie(index_from_key_, id1);
|
||||
@@ -306,8 +304,8 @@ class OrderingBuilder : public DecisionBuilder {
|
||||
// Passed as arguments from the function that creates the Decision Builder.
|
||||
const std::map<int, FapVariable> data_variables_;
|
||||
const std::vector<FapConstraint> data_constraints_;
|
||||
const std::vector<IntVar*> variables_;
|
||||
const std::vector<IntVar*> violated_constraints_;
|
||||
const std::vector<IntVar *> variables_;
|
||||
const std::vector<IntVar *> violated_constraints_;
|
||||
const std::map<int, int> index_from_key_;
|
||||
// Used by Next() for monitoring decisions.
|
||||
const int size_;
|
||||
@@ -330,12 +328,12 @@ bool ConstraintImpactComparator(FapConstraint constraint1,
|
||||
}
|
||||
|
||||
int64 ValueEvaluator(
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64>>* value_evaluator_map,
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64> > *value_evaluator_map,
|
||||
int64 variable_index, int64 value) {
|
||||
CHECK(value_evaluator_map != nullptr);
|
||||
// Evaluate the choice. Smaller ranking denotes a better choice.
|
||||
int64 ranking = -1;
|
||||
for (const auto& it : *value_evaluator_map) {
|
||||
for (const auto &it : *value_evaluator_map) {
|
||||
if ((it.first != variable_index) && (it.second.first == value)) {
|
||||
ranking = -2;
|
||||
break;
|
||||
@@ -343,7 +341,7 @@ int64 ValueEvaluator(
|
||||
}
|
||||
|
||||
// Update the history of assigned values and their rankings of each variable.
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64>>::iterator it;
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64> >::iterator it;
|
||||
int64 new_value = value;
|
||||
int64 new_ranking = ranking;
|
||||
if ((it = value_evaluator_map->find(variable_index)) !=
|
||||
@@ -367,8 +365,8 @@ int64 ValueEvaluator(
|
||||
|
||||
// The variables which participate in more constraints and have the
|
||||
// smaller domain should be in higher priority for assignment.
|
||||
int64 VariableEvaluator(const std::vector<int>& key_from_index,
|
||||
const std::map<int, FapVariable>& data_variables,
|
||||
int64 VariableEvaluator(const std::vector<int> &key_from_index,
|
||||
const std::map<int, FapVariable> &data_variables,
|
||||
int64 variable_index) {
|
||||
FapVariable variable =
|
||||
gtl::FindOrDie(data_variables, key_from_index[variable_index]);
|
||||
@@ -377,10 +375,11 @@ int64 VariableEvaluator(const std::vector<int>& key_from_index,
|
||||
}
|
||||
|
||||
// Creates the variables of the solver from the parsed data.
|
||||
void CreateModelVariables(const std::map<int, FapVariable>& data_variables,
|
||||
Solver* solver, std::vector<IntVar*>* model_variables,
|
||||
std::map<int, int>* index_from_key,
|
||||
std::vector<int>* key_from_index) {
|
||||
void CreateModelVariables(const std::map<int, FapVariable> &data_variables,
|
||||
Solver *solver,
|
||||
std::vector<IntVar *> *model_variables,
|
||||
std::map<int, int> *index_from_key,
|
||||
std::vector<int> *key_from_index) {
|
||||
CHECK(solver != nullptr);
|
||||
CHECK(model_variables != nullptr);
|
||||
CHECK(index_from_key != nullptr);
|
||||
@@ -391,7 +390,7 @@ void CreateModelVariables(const std::map<int, FapVariable>& data_variables,
|
||||
key_from_index->resize(number_of_variables);
|
||||
|
||||
int index = 0;
|
||||
for (const auto& it : data_variables) {
|
||||
for (const auto &it : data_variables) {
|
||||
CHECK_LT(index, model_variables->size());
|
||||
(*model_variables)[index] = solver->MakeIntVar(it.second.domain);
|
||||
gtl::InsertOrUpdate(index_from_key, it.first, index);
|
||||
@@ -407,20 +406,20 @@ void CreateModelVariables(const std::map<int, FapVariable>& data_variables,
|
||||
}
|
||||
|
||||
// Creates the constraints of the instance from the parsed data.
|
||||
void CreateModelConstraints(const std::vector<FapConstraint>& data_constraints,
|
||||
const std::vector<IntVar*>& variables,
|
||||
const std::map<int, int>& index_from_key,
|
||||
Solver* solver) {
|
||||
void CreateModelConstraints(const std::vector<FapConstraint> &data_constraints,
|
||||
const std::vector<IntVar *> &variables,
|
||||
const std::map<int, int> &index_from_key,
|
||||
Solver *solver) {
|
||||
CHECK(solver != nullptr);
|
||||
|
||||
for (const FapConstraint& ct : data_constraints) {
|
||||
for (const FapConstraint &ct : data_constraints) {
|
||||
const int index1 = gtl::FindOrDie(index_from_key, ct.variable1);
|
||||
const int index2 = gtl::FindOrDie(index_from_key, ct.variable2);
|
||||
CHECK_LT(index1, variables.size());
|
||||
CHECK_LT(index2, variables.size());
|
||||
IntVar* var1 = variables[index1];
|
||||
IntVar* var2 = variables[index2];
|
||||
IntVar* absolute_difference =
|
||||
IntVar *var1 = variables[index1];
|
||||
IntVar *var2 = variables[index2];
|
||||
IntVar *absolute_difference =
|
||||
solver->MakeAbs(solver->MakeDifference(var1, var2))->Var();
|
||||
if (ct.operation == ">") {
|
||||
solver->AddConstraint(solver->MakeGreater(absolute_difference, ct.value));
|
||||
@@ -436,67 +435,69 @@ void CreateModelConstraints(const std::vector<FapConstraint>& data_constraints,
|
||||
|
||||
// According to the value of a command line flag, chooses the strategy which
|
||||
// determines the selection of the variable to be assigned next.
|
||||
void ChooseVariableStrategy(Solver::IntVarStrategy* variable_strategy) {
|
||||
void ChooseVariableStrategy(Solver::IntVarStrategy *variable_strategy) {
|
||||
CHECK(variable_strategy != nullptr);
|
||||
|
||||
switch (FLAGS_choose_next_variable_strategy) {
|
||||
case 1: {
|
||||
*variable_strategy = Solver::CHOOSE_FIRST_UNBOUND;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_FIRST_UNBOUND "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
*variable_strategy = Solver::CHOOSE_MIN_SIZE_LOWEST_MIN;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_LOWEST_MIN "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
*variable_strategy = Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
*variable_strategy = Solver::CHOOSE_RANDOM;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_RANDOM "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG(FATAL) << "Should not be here";
|
||||
return;
|
||||
}
|
||||
switch (absl::GetFlag(FLAGS_choose_next_variable_strategy)) {
|
||||
case 1: {
|
||||
*variable_strategy = Solver::CHOOSE_FIRST_UNBOUND;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_FIRST_UNBOUND "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
*variable_strategy = Solver::CHOOSE_MIN_SIZE_LOWEST_MIN;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_LOWEST_MIN "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
*variable_strategy = Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
*variable_strategy = Solver::CHOOSE_RANDOM;
|
||||
LOG(INFO) << "Using Solver::CHOOSE_RANDOM "
|
||||
"for variable selection strategy.";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG(FATAL) << "Should not be here";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// According to the values of some command line flags, adds some monitors
|
||||
// for the search of the Solver.
|
||||
void CreateAdditionalMonitors(OptimizeVar* const objective, Solver* solver,
|
||||
std::vector<SearchMonitor*>* monitors) {
|
||||
void CreateAdditionalMonitors(OptimizeVar *const objective, Solver *solver,
|
||||
std::vector<SearchMonitor *> *monitors) {
|
||||
CHECK(solver != nullptr);
|
||||
CHECK(monitors != nullptr);
|
||||
|
||||
// Search Log
|
||||
if (FLAGS_log_search) {
|
||||
SearchMonitor* const log = solver->MakeSearchLog(100000, objective);
|
||||
if (absl::GetFlag(FLAGS_log_search)) {
|
||||
SearchMonitor *const log = solver->MakeSearchLog(100000, objective);
|
||||
monitors->push_back(log);
|
||||
}
|
||||
|
||||
// Time Limit
|
||||
if (FLAGS_time_limit_in_ms != 0) {
|
||||
LOG(INFO) << "Adding time limit of " << FLAGS_time_limit_in_ms << " ms.";
|
||||
SearchLimit* const limit = solver->MakeLimit(
|
||||
FLAGS_time_limit_in_ms, kint64max, kint64max, kint64max);
|
||||
if (absl::GetFlag(FLAGS_time_limit_in_ms) != 0) {
|
||||
LOG(INFO) << "Adding time limit of "
|
||||
<< absl::GetFlag(FLAGS_time_limit_in_ms) << " ms.";
|
||||
SearchLimit *const limit = solver->MakeLimit(
|
||||
absl::GetFlag(FLAGS_time_limit_in_ms), kint64max, kint64max, kint64max);
|
||||
monitors->push_back(limit);
|
||||
}
|
||||
|
||||
// Search Restart
|
||||
SearchMonitor* const restart =
|
||||
FLAGS_restart != -1
|
||||
? (FLAGS_luby ? solver->MakeLubyRestart(FLAGS_restart)
|
||||
: solver->MakeConstantRestart(FLAGS_restart))
|
||||
SearchMonitor *const restart =
|
||||
absl::GetFlag(FLAGS_restart) != -1
|
||||
? (absl::GetFlag(FLAGS_luby)
|
||||
? solver->MakeLubyRestart(absl::GetFlag(FLAGS_restart))
|
||||
: solver->MakeConstantRestart(absl::GetFlag(FLAGS_restart)))
|
||||
: nullptr;
|
||||
if (restart) {
|
||||
monitors->push_back(restart);
|
||||
@@ -507,15 +508,15 @@ void CreateAdditionalMonitors(OptimizeVar* const objective, Solver* solver,
|
||||
// instances of the problem with objective either the minimization of
|
||||
// the largest frequency assigned or the minimization of the number
|
||||
// of frequencies used to the solution.
|
||||
void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::string& data_objective,
|
||||
const std::vector<int>& values) {
|
||||
void HardFapSolver(const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<FapConstraint> &data_constraints,
|
||||
const std::string &data_objective,
|
||||
const std::vector<int> &values) {
|
||||
Solver solver("HardFapSolver");
|
||||
std::vector<SearchMonitor*> monitors;
|
||||
std::vector<SearchMonitor *> monitors;
|
||||
|
||||
// Create Model Variables.
|
||||
std::vector<IntVar*> variables;
|
||||
std::vector<IntVar *> variables;
|
||||
std::map<int, int> index_from_key;
|
||||
std::vector<int> key_from_index;
|
||||
CreateModelVariables(data_variables, &solver, &variables, &index_from_key,
|
||||
@@ -529,15 +530,15 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
std::sort(ordered_constraints.begin(), ordered_constraints.end(),
|
||||
ConstraintImpactComparator);
|
||||
|
||||
std::vector<IntVar*> violated_constraints;
|
||||
std::vector<IntVar *> violated_constraints;
|
||||
solver.MakeIntVarArray(ordered_constraints.size(), 0, 0,
|
||||
&violated_constraints);
|
||||
|
||||
// Objective:
|
||||
// Either minimize the largest assigned frequency or
|
||||
// minimize the number of different frequencies assigned.
|
||||
IntVar* objective_var;
|
||||
OptimizeVar* objective;
|
||||
IntVar *objective_var;
|
||||
OptimizeVar *objective;
|
||||
if (data_objective == "Minimize the largest assigned value.") {
|
||||
LOG(INFO) << "Minimize the largest assigned value.";
|
||||
// The objective_var is set to hold the maximum value assigned
|
||||
@@ -547,11 +548,11 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
} else if (data_objective == "Minimize the number of assigned values.") {
|
||||
LOG(INFO) << "Minimize the number of assigned values.";
|
||||
|
||||
std::vector<IntVar*> cardinality;
|
||||
std::vector<IntVar *> cardinality;
|
||||
solver.MakeIntVarArray(static_cast<int>(values.size()), 0,
|
||||
static_cast<int>(variables.size()), &cardinality);
|
||||
solver.AddConstraint(solver.MakeDistribute(variables, values, cardinality));
|
||||
std::vector<IntVar*> value_not_assigned;
|
||||
std::vector<IntVar *> value_not_assigned;
|
||||
for (int val = 0; val < values.size(); ++val) {
|
||||
value_not_assigned.push_back(
|
||||
solver.MakeIsEqualCstVar(cardinality[val], 0));
|
||||
@@ -568,7 +569,7 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
monitors.push_back(objective);
|
||||
|
||||
// Ordering Builder
|
||||
OrderingBuilder* ob = solver.RevAlloc(
|
||||
OrderingBuilder *ob = solver.RevAlloc(
|
||||
new OrderingBuilder(data_variables, ordered_constraints, variables,
|
||||
violated_constraints, index_from_key));
|
||||
|
||||
@@ -577,14 +578,15 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
Solver::IntVarStrategy variable_strategy;
|
||||
ChooseVariableStrategy(&variable_strategy);
|
||||
// Choose the value selection strategy.
|
||||
DecisionBuilder* db;
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64>> history;
|
||||
if (FLAGS_value_evaluator == "value_evaluator") {
|
||||
DecisionBuilder *db;
|
||||
absl::flat_hash_map<int64, std::pair<int64, int64> > history;
|
||||
if (absl::GetFlag(FLAGS_value_evaluator) == "value_evaluator") {
|
||||
LOG(INFO) << "Using ValueEvaluator for value selection strategy.";
|
||||
Solver::IndexEvaluator2 index_evaluator2 = [&history](int64 var,
|
||||
int64 value) {
|
||||
Solver::IndexEvaluator2 index_evaluator2 =
|
||||
[&history](int64 var, int64 value) {
|
||||
return ValueEvaluator(&history, var, value);
|
||||
};
|
||||
}
|
||||
;
|
||||
LOG(INFO) << "Using ValueEvaluator for value selection strategy.";
|
||||
db = solver.MakePhase(variables, variable_strategy, index_evaluator2);
|
||||
} else {
|
||||
@@ -593,13 +595,13 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
Solver::ASSIGN_MIN_VALUE);
|
||||
}
|
||||
|
||||
DecisionBuilder* final_db = solver.Compose(ob, db);
|
||||
DecisionBuilder *final_db = solver.Compose(ob, db);
|
||||
|
||||
// Create Additional Monitors.
|
||||
CreateAdditionalMonitors(objective, &solver, &monitors);
|
||||
|
||||
// Collector
|
||||
SolutionCollector* const collector = solver.MakeLastSolutionCollector();
|
||||
SolutionCollector *const collector = solver.MakeLastSolutionCollector();
|
||||
collector->Add(variables);
|
||||
collector->Add(objective_var);
|
||||
monitors.push_back(collector);
|
||||
@@ -611,21 +613,21 @@ void HardFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
const int64 time2 = solver.wall_time();
|
||||
|
||||
// Display Time.
|
||||
if (FLAGS_display_time) {
|
||||
if (absl::GetFlag(FLAGS_display_time)) {
|
||||
PrintElapsedTime(time1, time2);
|
||||
}
|
||||
// Display Results.
|
||||
if (FLAGS_display_results) {
|
||||
if (absl::GetFlag(FLAGS_display_results)) {
|
||||
PrintResultsHard(collector, variables, objective_var, data_variables,
|
||||
data_constraints, index_from_key, key_from_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Splits variables of the instance to hard and soft.
|
||||
void SplitVariablesHardSoft(const std::map<int, FapVariable>& data_variables,
|
||||
std::map<int, FapVariable>* hard_variables,
|
||||
std::map<int, FapVariable>* soft_variables) {
|
||||
for (const auto& it : data_variables) {
|
||||
void SplitVariablesHardSoft(const std::map<int, FapVariable> &data_variables,
|
||||
std::map<int, FapVariable> *hard_variables,
|
||||
std::map<int, FapVariable> *soft_variables) {
|
||||
for (const auto &it : data_variables) {
|
||||
if (it.second.initial_position != -1) {
|
||||
if (it.second.hard) {
|
||||
CHECK_LT(it.second.mobility_cost, 0);
|
||||
@@ -639,10 +641,10 @@ void SplitVariablesHardSoft(const std::map<int, FapVariable>& data_variables,
|
||||
}
|
||||
|
||||
// Splits constraints of the instance to hard and soft.
|
||||
void SplitConstraintHardSoft(const std::vector<FapConstraint>& data_constraints,
|
||||
std::vector<FapConstraint>* hard_constraints,
|
||||
std::vector<FapConstraint>* soft_constraints) {
|
||||
for (const FapConstraint& ct : data_constraints) {
|
||||
void SplitConstraintHardSoft(const std::vector<FapConstraint> &data_constraints,
|
||||
std::vector<FapConstraint> *hard_constraints,
|
||||
std::vector<FapConstraint> *soft_constraints) {
|
||||
for (const FapConstraint &ct : data_constraints) {
|
||||
if (ct.hard) {
|
||||
CHECK_LT(ct.weight_cost, 0);
|
||||
hard_constraints->push_back(ct);
|
||||
@@ -655,17 +657,17 @@ void SplitConstraintHardSoft(const std::vector<FapConstraint>& data_constraints,
|
||||
|
||||
// Penalize the modification of the initial position of soft variable of
|
||||
// the instance.
|
||||
void PenalizeVariablesViolation(
|
||||
const std::map<int, FapVariable>& soft_variables,
|
||||
const std::map<int, int>& index_from_key,
|
||||
const std::vector<IntVar*>& variables, std::vector<IntVar*>* cost,
|
||||
Solver* solver) {
|
||||
for (const auto& it : soft_variables) {
|
||||
void
|
||||
PenalizeVariablesViolation(const std::map<int, FapVariable> &soft_variables,
|
||||
const std::map<int, int> &index_from_key,
|
||||
const std::vector<IntVar *> &variables,
|
||||
std::vector<IntVar *> *cost, Solver *solver) {
|
||||
for (const auto &it : soft_variables) {
|
||||
const int index = gtl::FindOrDie(index_from_key, it.first);
|
||||
CHECK_LT(index, variables.size());
|
||||
IntVar* const displaced = solver->MakeIsDifferentCstVar(
|
||||
IntVar *const displaced = solver->MakeIsDifferentCstVar(
|
||||
variables[index], it.second.initial_position);
|
||||
IntVar* const weight =
|
||||
IntVar *const weight =
|
||||
solver->MakeProd(displaced, it.second.mobility_cost)->Var();
|
||||
cost->push_back(weight);
|
||||
}
|
||||
@@ -673,34 +675,31 @@ void PenalizeVariablesViolation(
|
||||
|
||||
// Penalize the violation of soft constraints of the instance.
|
||||
void PenalizeConstraintsViolation(
|
||||
const std::vector<FapConstraint>& constraints,
|
||||
const std::vector<FapConstraint>& soft_constraints,
|
||||
const std::map<int, int>& index_from_key,
|
||||
const std::vector<IntVar*>& variables, std::vector<IntVar*>* cost,
|
||||
std::vector<IntVar*>* violated_constraints, Solver* solver) {
|
||||
const std::vector<FapConstraint> &constraints,
|
||||
const std::vector<FapConstraint> &soft_constraints,
|
||||
const std::map<int, int> &index_from_key,
|
||||
const std::vector<IntVar *> &variables, std::vector<IntVar *> *cost,
|
||||
std::vector<IntVar *> *violated_constraints, Solver *solver) {
|
||||
int violated_constraints_index = 0;
|
||||
for (const FapConstraint& ct : constraints) {
|
||||
for (const FapConstraint &ct : constraints) {
|
||||
CHECK_LT(violated_constraints_index, violated_constraints->size());
|
||||
if (!ct.hard) {
|
||||
// The violated_constraints_index will stop at the first soft constraint.
|
||||
break;
|
||||
}
|
||||
IntVar* const hard_violation = solver->MakeIntVar(0, 0);
|
||||
IntVar *const hard_violation = solver->MakeIntVar(0, 0);
|
||||
(*violated_constraints)[violated_constraints_index] = hard_violation;
|
||||
violated_constraints_index++;
|
||||
}
|
||||
|
||||
for (const FapConstraint& ct : soft_constraints) {
|
||||
for (const FapConstraint &ct : soft_constraints) {
|
||||
const int index1 = gtl::FindOrDie(index_from_key, ct.variable1);
|
||||
const int index2 = gtl::FindOrDie(index_from_key, ct.variable2);
|
||||
CHECK_LT(index1, variables.size());
|
||||
CHECK_LT(index2, variables.size());
|
||||
IntVar* const absolute_difference =
|
||||
solver
|
||||
->MakeAbs(
|
||||
solver->MakeDifference(variables[index1], variables[index2]))
|
||||
->Var();
|
||||
IntVar* violation = nullptr;
|
||||
IntVar *const absolute_difference = solver->MakeAbs(solver->MakeDifference(
|
||||
variables[index1], variables[index2]))->Var();
|
||||
IntVar *violation = nullptr;
|
||||
if (ct.operation == ">") {
|
||||
violation = solver->MakeIsLessCstVar(absolute_difference, ct.value);
|
||||
} else if (ct.operation == "=") {
|
||||
@@ -708,7 +707,7 @@ void PenalizeConstraintsViolation(
|
||||
} else {
|
||||
LOG(FATAL) << "Invalid operator detected.";
|
||||
}
|
||||
IntVar* const weight = solver->MakeProd(violation, ct.weight_cost)->Var();
|
||||
IntVar *const weight = solver->MakeProd(violation, ct.weight_cost)->Var();
|
||||
cost->push_back(weight);
|
||||
CHECK_LT(violated_constraints_index, violated_constraints->size());
|
||||
(*violated_constraints)[violated_constraints_index] = violation;
|
||||
@@ -720,12 +719,12 @@ void PenalizeConstraintsViolation(
|
||||
// The Soft Solver is dealing with the optimization of unfeasible instances
|
||||
// and aims to minimize the total cost of violated constraints. Returning value
|
||||
// equal to 0 denotes that the instance is feasible.
|
||||
int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::string& data_objective,
|
||||
const std::vector<int>& values) {
|
||||
int SoftFapSolver(const std::map<int, FapVariable> &data_variables,
|
||||
const std::vector<FapConstraint> &data_constraints,
|
||||
const std::string &data_objective,
|
||||
const std::vector<int> &values) {
|
||||
Solver solver("SoftFapSolver");
|
||||
std::vector<SearchMonitor*> monitors;
|
||||
std::vector<SearchMonitor *> monitors;
|
||||
|
||||
// Split variables to hard and soft.
|
||||
std::map<int, FapVariable> hard_variables;
|
||||
@@ -743,7 +742,7 @@ int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
&soft_constraints);
|
||||
|
||||
// Create Model Variables.
|
||||
std::vector<IntVar*> variables;
|
||||
std::vector<IntVar *> variables;
|
||||
std::map<int, int> index_from_key;
|
||||
std::vector<int> key_from_index;
|
||||
CreateModelVariables(data_variables, &solver, &variables, &index_from_key,
|
||||
@@ -753,9 +752,9 @@ int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
CreateModelConstraints(hard_constraints, variables, index_from_key, &solver);
|
||||
|
||||
// Penalize variable and constraint violations.
|
||||
std::vector<IntVar*> cost;
|
||||
std::vector<IntVar*> violated_constraints(ordered_constraints.size(),
|
||||
nullptr);
|
||||
std::vector<IntVar *> cost;
|
||||
std::vector<IntVar *> violated_constraints(ordered_constraints.size(),
|
||||
nullptr);
|
||||
PenalizeVariablesViolation(soft_variables, index_from_key, variables, &cost,
|
||||
&solver);
|
||||
PenalizeConstraintsViolation(ordered_constraints, soft_constraints,
|
||||
@@ -764,25 +763,26 @@ int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
|
||||
// Objective
|
||||
// Minimize the sum of violation penalties.
|
||||
IntVar* objective_var = solver.MakeSum(cost)->Var();
|
||||
OptimizeVar* objective = solver.MakeMinimize(objective_var, 1);
|
||||
IntVar *objective_var = solver.MakeSum(cost)->Var();
|
||||
OptimizeVar *objective = solver.MakeMinimize(objective_var, 1);
|
||||
monitors.push_back(objective);
|
||||
|
||||
// Ordering Builder
|
||||
OrderingBuilder* ob = solver.RevAlloc(
|
||||
OrderingBuilder *ob = solver.RevAlloc(
|
||||
new OrderingBuilder(data_variables, ordered_constraints, variables,
|
||||
violated_constraints, index_from_key));
|
||||
|
||||
// Decision Builder Configuration
|
||||
// Choose the next variable selection strategy.
|
||||
DecisionBuilder* db;
|
||||
if (FLAGS_variable_evaluator == "variable_evaluator") {
|
||||
DecisionBuilder *db;
|
||||
if (absl::GetFlag(FLAGS_variable_evaluator) == "variable_evaluator") {
|
||||
LOG(INFO) << "Using VariableEvaluator for variable selection strategy and "
|
||||
"Solver::ASSIGN_MIN_VALUE for value selection strategy.";
|
||||
Solver::IndexEvaluator1 var_evaluator = [&key_from_index,
|
||||
&data_variables](int64 index) {
|
||||
Solver::IndexEvaluator1 var_evaluator =
|
||||
[&key_from_index, &data_variables](int64 index) {
|
||||
return VariableEvaluator(key_from_index, data_variables, index);
|
||||
};
|
||||
}
|
||||
;
|
||||
db = solver.MakePhase(variables, var_evaluator, Solver::ASSIGN_MIN_VALUE);
|
||||
} else {
|
||||
LOG(INFO) << "Using Solver::CHOOSE_FIRST_UNBOUND for variable selection "
|
||||
@@ -791,13 +791,13 @@ int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
db = solver.MakePhase(variables, Solver::CHOOSE_FIRST_UNBOUND,
|
||||
Solver::ASSIGN_MIN_VALUE);
|
||||
}
|
||||
DecisionBuilder* final_db = solver.Compose(ob, db);
|
||||
DecisionBuilder *final_db = solver.Compose(ob, db);
|
||||
|
||||
// Create Additional Monitors.
|
||||
CreateAdditionalMonitors(objective, &solver, &monitors);
|
||||
|
||||
// Collector
|
||||
SolutionCollector* const collector = solver.MakeLastSolutionCollector();
|
||||
SolutionCollector *const collector = solver.MakeLastSolutionCollector();
|
||||
collector->Add(variables);
|
||||
collector->Add(objective_var);
|
||||
monitors.push_back(collector);
|
||||
@@ -811,11 +811,11 @@ int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
int violation_sum =
|
||||
collector->Value(collector->solution_count() - 1, objective_var);
|
||||
// Display Time.
|
||||
if (FLAGS_display_time) {
|
||||
if (absl::GetFlag(FLAGS_display_time)) {
|
||||
PrintElapsedTime(time1, time2);
|
||||
}
|
||||
// Display Results.
|
||||
if (FLAGS_display_results) {
|
||||
if (absl::GetFlag(FLAGS_display_results)) {
|
||||
PrintResultsSoft(collector, variables, objective_var, hard_variables,
|
||||
hard_constraints, soft_variables, soft_constraints,
|
||||
index_from_key, key_from_index);
|
||||
@@ -824,9 +824,9 @@ int SoftFapSolver(const std::map<int, FapVariable>& data_variables,
|
||||
return violation_sum;
|
||||
}
|
||||
|
||||
void SolveProblem(const std::map<int, FapVariable>& variables,
|
||||
const std::vector<FapConstraint>& constraints,
|
||||
const std::string& objective, const std::vector<int>& values,
|
||||
void SolveProblem(const std::map<int, FapVariable> &variables,
|
||||
const std::vector<FapConstraint> &constraints,
|
||||
const std::string &objective, const std::vector<int> &values,
|
||||
bool soft) {
|
||||
// Print Instance!
|
||||
FapModelPrinter model_printer(variables, constraints, objective, values);
|
||||
@@ -850,35 +850,37 @@ void SolveProblem(const std::map<int, FapVariable>& variables,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
CHECK(!FLAGS_directory.empty()) << "Requires --directory=<directory name>";
|
||||
CHECK(!absl::GetFlag(FLAGS_directory).empty())
|
||||
<< "Requires --directory=<directory name>";
|
||||
|
||||
LOG(INFO) << "Solving instance in directory " << FLAGS_directory;
|
||||
LOG(INFO) << "Solving instance in directory "
|
||||
<< absl::GetFlag(FLAGS_directory);
|
||||
// Parse!
|
||||
std::map<int, operations_research::FapVariable> variables;
|
||||
std::vector<operations_research::FapConstraint> constraints;
|
||||
std::string objective;
|
||||
std::vector<int> values;
|
||||
absl::flat_hash_map<int, operations_research::FapComponent> components;
|
||||
operations_research::ParseInstance(FLAGS_directory, FLAGS_find_components,
|
||||
&variables, &constraints, &objective,
|
||||
&values, &components);
|
||||
if (!FLAGS_find_components) {
|
||||
operations_research::ParseInstance(
|
||||
absl::GetFlag(FLAGS_directory), absl::GetFlag(FLAGS_find_components),
|
||||
&variables, &constraints, &objective, &values, &components);
|
||||
if (!absl::GetFlag(FLAGS_find_components)) {
|
||||
operations_research::SolveProblem(variables, constraints, objective, values,
|
||||
FLAGS_soft);
|
||||
absl::GetFlag(FLAGS_soft));
|
||||
} else {
|
||||
int component_id = 1;
|
||||
LOG(INFO) << "Number of components in the RLFAP graph "
|
||||
<< components.size();
|
||||
for (const auto& component : components) {
|
||||
for (const auto &component : components) {
|
||||
LOG(INFO) << "Solving Component " << component_id;
|
||||
operations_research::SolveProblem(component.second.variables,
|
||||
component.second.constraints, objective,
|
||||
values, FLAGS_soft);
|
||||
values, absl::GetFlag(FLAGS_soft));
|
||||
component_id++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,11 @@ DEFINE_int32(
|
||||
"Size of the problem. If equal to 0, will test several increasing sizes.");
|
||||
DEFINE_string(params, "", "Sat parameters.");
|
||||
|
||||
static const int kBestSolutions[] = {0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85,
|
||||
// just for the optimistics ones, the rest:
|
||||
106, 127, 151, 177, 199, 216, 246};
|
||||
static const int kBestSolutions[] = {
|
||||
0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85,
|
||||
// just for the optimistics ones, the rest:
|
||||
106, 127, 151, 177, 199, 216, 246
|
||||
};
|
||||
|
||||
static const int kKnownSolutions = 19;
|
||||
|
||||
@@ -63,7 +65,10 @@ void GolombRuler(int size) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
for (int j = i + 1; j < size; ++j) {
|
||||
const IntVar diff = cp_model.NewIntVar(domain);
|
||||
cp_model.AddEquality(LinearExpr::Sum({diff, ticks[i]}), ticks[j]);
|
||||
cp_model.AddEquality(LinearExpr::Sum({
|
||||
diff, ticks[i]
|
||||
}),
|
||||
ticks[j]);
|
||||
diffs.push_back(diff);
|
||||
}
|
||||
}
|
||||
@@ -86,10 +91,10 @@ void GolombRuler(int size) {
|
||||
SatParameters parameters;
|
||||
parameters.set_search_branching(SatParameters::FIXED_SEARCH);
|
||||
// Parse the --params flag.
|
||||
if (!FLAGS_params.empty()) {
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(FLAGS_params,
|
||||
¶meters))
|
||||
<< FLAGS_params;
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
absl::GetFlag(FLAGS_params), ¶meters))
|
||||
<< absl::GetFlag(FLAGS_params);
|
||||
}
|
||||
model.Add(NewSatParameters(parameters));
|
||||
const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model);
|
||||
@@ -102,7 +107,7 @@ void GolombRuler(int size) {
|
||||
if (size - 1 < kKnownSolutions) {
|
||||
CHECK_EQ(result, kBestSolutions[size - 1]);
|
||||
}
|
||||
if (FLAGS_print) {
|
||||
if (absl::GetFlag(FLAGS_print)) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const int64 tick = SolutionIntegerValue(response, ticks[i]);
|
||||
printf("%d ", static_cast<int>(tick));
|
||||
@@ -112,13 +117,13 @@ void GolombRuler(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_size != 0) {
|
||||
operations_research::sat::GolombRuler(FLAGS_size);
|
||||
if (absl::GetFlag(FLAGS_size) != 0) {
|
||||
operations_research::sat::GolombRuler(absl::GetFlag(FLAGS_size));
|
||||
} else {
|
||||
for (int n = 1; n < 11; ++n) {
|
||||
operations_research::sat::GolombRuler(n);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "ortools/linear_solver/linear_solver.h"
|
||||
|
||||
namespace operations_research {
|
||||
void RunIntegerProgrammingExample(const std::string& solver_id) {
|
||||
void RunIntegerProgrammingExample(const std::string &solver_id) {
|
||||
LOG(INFO) << "---- Integer programming example with " << solver_id << " ----";
|
||||
|
||||
MPSolver::OptimizationProblemType problem_type;
|
||||
@@ -36,22 +36,22 @@ void RunIntegerProgrammingExample(const std::string& solver_id) {
|
||||
|
||||
const double infinity = solver.infinity();
|
||||
// x and y are integer non-negative variables.
|
||||
MPVariable* const x = solver.MakeIntVar(0.0, infinity, "x");
|
||||
MPVariable* const y = solver.MakeIntVar(0.0, infinity, "y");
|
||||
MPVariable *const x = solver.MakeIntVar(0.0, infinity, "x");
|
||||
MPVariable *const y = solver.MakeIntVar(0.0, infinity, "y");
|
||||
|
||||
// Maximize x + 10 * y.
|
||||
MPObjective* const objective = solver.MutableObjective();
|
||||
MPObjective *const objective = solver.MutableObjective();
|
||||
objective->SetCoefficient(x, 1);
|
||||
objective->SetCoefficient(y, 10);
|
||||
objective->SetMaximization();
|
||||
|
||||
// x + 7 * y <= 17.5.
|
||||
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 17.5);
|
||||
MPConstraint *const c0 = solver.MakeRowConstraint(-infinity, 17.5);
|
||||
c0->SetCoefficient(x, 1);
|
||||
c0->SetCoefficient(y, 7);
|
||||
|
||||
// x <= 3.5
|
||||
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 3.5);
|
||||
MPConstraint *const c1 = solver.MakeRowConstraint(-infinity, 3.5);
|
||||
c1->SetCoefficient(x, 1);
|
||||
c1->SetCoefficient(y, 0);
|
||||
|
||||
@@ -83,12 +83,12 @@ void RunAllExamples() {
|
||||
RunIntegerProgrammingExample("GLPK");
|
||||
RunIntegerProgrammingExample("CPLEX");
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&FLAGS_log_prefix, false);
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_log_prefix), false);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::RunAllExamples();
|
||||
return 0;
|
||||
|
||||
@@ -46,11 +46,11 @@ namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
// Compute a valid horizon from a problem.
|
||||
int64 ComputeHorizon(const JsspInputProblem& problem) {
|
||||
int64 ComputeHorizon(const JsspInputProblem &problem) {
|
||||
int64 sum_of_durations = 0;
|
||||
int64 max_latest_end = 0;
|
||||
int64 max_earliest_start = 0;
|
||||
for (const Job& job : problem.jobs()) {
|
||||
for (const Job &job : problem.jobs()) {
|
||||
if (job.has_latest_end()) {
|
||||
max_latest_end = std::max(max_latest_end, job.latest_end().value());
|
||||
} else {
|
||||
@@ -60,7 +60,7 @@ int64 ComputeHorizon(const JsspInputProblem& problem) {
|
||||
max_earliest_start =
|
||||
std::max(max_earliest_start, job.earliest_start().value());
|
||||
}
|
||||
for (const Task& task : job.tasks()) {
|
||||
for (const Task &task : job.tasks()) {
|
||||
int64 max_duration = 0;
|
||||
for (int64 d : task.duration()) {
|
||||
max_duration = std::max(max_duration, d);
|
||||
@@ -71,9 +71,10 @@ int64 ComputeHorizon(const JsspInputProblem& problem) {
|
||||
|
||||
const int num_jobs = problem.jobs_size();
|
||||
int64 sum_of_transitions = 0;
|
||||
for (const Machine& machine : problem.machines()) {
|
||||
if (!machine.has_transition_time_matrix()) continue;
|
||||
const TransitionTimeMatrix& matrix = machine.transition_time_matrix();
|
||||
for (const Machine &machine : problem.machines()) {
|
||||
if (!machine.has_transition_time_matrix())
|
||||
continue;
|
||||
const TransitionTimeMatrix &matrix = machine.transition_time_matrix();
|
||||
for (int i = 0; i < num_jobs; ++i) {
|
||||
int64 max_transition = 0;
|
||||
for (int j = 0; j < num_jobs; ++j) {
|
||||
@@ -89,8 +90,8 @@ int64 ComputeHorizon(const JsspInputProblem& problem) {
|
||||
}
|
||||
|
||||
// Solve a JobShop scheduling problem using SAT.
|
||||
void Solve(const JsspInputProblem& problem) {
|
||||
if (FLAGS_display_model) {
|
||||
void Solve(const JsspInputProblem &problem) {
|
||||
if (absl::GetFlag(FLAGS_display_model)) {
|
||||
LOG(INFO) << problem.DebugString();
|
||||
}
|
||||
|
||||
@@ -107,11 +108,11 @@ void Solve(const JsspInputProblem& problem) {
|
||||
|
||||
const IntVar makespan = cp_model.NewIntVar(all_horizon);
|
||||
|
||||
std::vector<std::vector<IntervalVar>> machine_to_intervals(num_machines);
|
||||
std::vector<std::vector<int>> machine_to_jobs(num_machines);
|
||||
std::vector<std::vector<IntVar>> machine_to_starts(num_machines);
|
||||
std::vector<std::vector<IntVar>> machine_to_ends(num_machines);
|
||||
std::vector<std::vector<BoolVar>> machine_to_presences(num_machines);
|
||||
std::vector<std::vector<IntervalVar> > machine_to_intervals(num_machines);
|
||||
std::vector<std::vector<int> > machine_to_jobs(num_machines);
|
||||
std::vector<std::vector<IntVar> > machine_to_starts(num_machines);
|
||||
std::vector<std::vector<IntVar> > machine_to_ends(num_machines);
|
||||
std::vector<std::vector<BoolVar> > machine_to_presences(num_machines);
|
||||
std::vector<IntVar> job_starts(num_jobs);
|
||||
std::vector<IntVar> job_ends(num_jobs);
|
||||
std::vector<IntVar> task_starts;
|
||||
@@ -120,7 +121,7 @@ void Solve(const JsspInputProblem& problem) {
|
||||
std::vector<int64> objective_coeffs;
|
||||
|
||||
for (int j = 0; j < num_jobs; ++j) {
|
||||
const Job& job = problem.jobs(j);
|
||||
const Job &job = problem.jobs(j);
|
||||
IntVar previous_end;
|
||||
const int64 hard_start =
|
||||
job.has_earliest_start() ? job.earliest_start().value() : 0L;
|
||||
@@ -128,7 +129,7 @@ void Solve(const JsspInputProblem& problem) {
|
||||
job.has_latest_end() ? job.latest_end().value() : horizon;
|
||||
|
||||
for (int t = 0; t < job.tasks_size(); ++t) {
|
||||
const Task& task = job.tasks(t);
|
||||
const Task &task = job.tasks(t);
|
||||
const int num_alternatives = task.machine_size();
|
||||
CHECK_EQ(num_alternatives, task.duration_size());
|
||||
|
||||
@@ -177,19 +178,19 @@ void Solve(const JsspInputProblem& problem) {
|
||||
for (int a = 0; a < num_alternatives; ++a) {
|
||||
const BoolVar presence = cp_model.NewBoolVar();
|
||||
const IntVar local_start =
|
||||
FLAGS_use_optional_variables
|
||||
absl::GetFlag(FLAGS_use_optional_variables)
|
||||
? cp_model.NewIntVar(Domain(hard_start, hard_end))
|
||||
: start;
|
||||
const IntVar local_duration = cp_model.NewConstant(task.duration(a));
|
||||
const IntVar local_end =
|
||||
FLAGS_use_optional_variables
|
||||
absl::GetFlag(FLAGS_use_optional_variables)
|
||||
? cp_model.NewIntVar(Domain(hard_start, hard_end))
|
||||
: end;
|
||||
const IntervalVar local_interval = cp_model.NewOptionalIntervalVar(
|
||||
local_start, local_duration, local_end, presence);
|
||||
|
||||
// Link local and global variables.
|
||||
if (FLAGS_use_optional_variables) {
|
||||
if (absl::GetFlag(FLAGS_use_optional_variables)) {
|
||||
cp_model.AddEquality(start, local_start).OnlyEnforceIf(presence);
|
||||
cp_model.AddEquality(end, local_end).OnlyEnforceIf(presence);
|
||||
|
||||
@@ -237,8 +238,9 @@ void Solve(const JsspInputProblem& problem) {
|
||||
cp_model.AddEquality(shifted_var,
|
||||
LinearExpr(previous_end).AddConstant(-due_date));
|
||||
const IntVar lateness_var = cp_model.NewIntVar(all_horizon);
|
||||
cp_model.AddMaxEquality(lateness_var,
|
||||
{cp_model.NewConstant(0), shifted_var});
|
||||
cp_model.AddMaxEquality(lateness_var, {
|
||||
cp_model.NewConstant(0), shifted_var
|
||||
});
|
||||
objective_vars.push_back(lateness_var);
|
||||
objective_coeffs.push_back(lateness_penalty);
|
||||
}
|
||||
@@ -250,11 +252,14 @@ void Solve(const JsspInputProblem& problem) {
|
||||
if (due_date > 0) {
|
||||
const IntVar shifted_var =
|
||||
cp_model.NewIntVar(Domain(due_date - horizon, due_date));
|
||||
cp_model.AddEquality(LinearExpr::Sum({shifted_var, previous_end}),
|
||||
cp_model.AddEquality(LinearExpr::Sum({
|
||||
shifted_var, previous_end
|
||||
}),
|
||||
due_date);
|
||||
const IntVar earliness_var = cp_model.NewIntVar(all_horizon);
|
||||
cp_model.AddMaxEquality(earliness_var,
|
||||
{cp_model.NewConstant(0), shifted_var});
|
||||
cp_model.AddMaxEquality(earliness_var, {
|
||||
cp_model.NewConstant(0), shifted_var
|
||||
});
|
||||
objective_vars.push_back(earliness_var);
|
||||
objective_coeffs.push_back(earliness_penalty);
|
||||
}
|
||||
@@ -266,7 +271,7 @@ void Solve(const JsspInputProblem& problem) {
|
||||
cp_model.AddNoOverlap(machine_to_intervals[m]);
|
||||
|
||||
if (problem.machines(m).has_transition_time_matrix()) {
|
||||
const TransitionTimeMatrix& transitions =
|
||||
const TransitionTimeMatrix &transitions =
|
||||
problem.machines(m).transition_time_matrix();
|
||||
const int num_intervals = machine_to_intervals[m].size();
|
||||
|
||||
@@ -292,9 +297,8 @@ void Solve(const JsspInputProblem& problem) {
|
||||
const IntVar end = machine_to_ends[m][i];
|
||||
circuit.AddArc(i + 1, j + 1, lit);
|
||||
// Push the new start with an extra transition.
|
||||
cp_model
|
||||
.AddLessOrEqual(LinearExpr(end).AddConstant(transition), start)
|
||||
.OnlyEnforceIf(lit);
|
||||
cp_model.AddLessOrEqual(LinearExpr(end).AddConstant(transition),
|
||||
start).OnlyEnforceIf(lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,7 +306,7 @@ void Solve(const JsspInputProblem& problem) {
|
||||
}
|
||||
|
||||
// Add job precedences.
|
||||
for (const JobPrecedence& precedence : problem.precedences()) {
|
||||
for (const JobPrecedence &precedence : problem.precedences()) {
|
||||
const IntVar start = job_starts[precedence.second_job_index()];
|
||||
const IntVar end = job_ends[precedence.first_job_index()];
|
||||
cp_model.AddLessOrEqual(LinearExpr(end).AddConstant(precedence.min_delay()),
|
||||
@@ -329,14 +333,14 @@ void Solve(const JsspInputProblem& problem) {
|
||||
LOG(INFO) << "#jobs:" << num_jobs;
|
||||
LOG(INFO) << "horizon:" << horizon;
|
||||
|
||||
if (FLAGS_display_sat_model) {
|
||||
if (absl::GetFlag(FLAGS_display_sat_model)) {
|
||||
LOG(INFO) << cp_model.Proto().DebugString();
|
||||
}
|
||||
|
||||
LOG(INFO) << CpModelStats(cp_model.Proto());
|
||||
|
||||
Model model;
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
|
||||
const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model);
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
@@ -376,18 +380,18 @@ void Solve(const JsspInputProblem& problem) {
|
||||
CHECK_LE(response.objective_value(), final_cost + tolerance);
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
int main(int argc, char **argv) {
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_input.empty()) {
|
||||
if (absl::GetFlag(FLAGS_input).empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
}
|
||||
|
||||
operations_research::data::jssp::JsspParser parser;
|
||||
CHECK(parser.ParseFile(FLAGS_input));
|
||||
CHECK(parser.ParseFile(absl::GetFlag(FLAGS_input)));
|
||||
operations_research::sat::Solve(parser.problem());
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ void AssignmentOn4x4Matrix() {
|
||||
LOG(INFO) << "Assignment on 4x4 Matrix";
|
||||
const int kNumSources = 4;
|
||||
const int kNumTargets = 4;
|
||||
const CostValue kCost[kNumSources][kNumTargets] = {{90, 76, 75, 80},
|
||||
{35, 85, 55, 65},
|
||||
{125, 95, 90, 105},
|
||||
{45, 110, 95, 115}};
|
||||
const CostValue kCost[kNumSources][kNumTargets] = { { 90, 76, 75, 80 },
|
||||
{ 35, 85, 55, 65 },
|
||||
{ 125, 95, 90, 105 },
|
||||
{ 45, 110, 95, 115 } };
|
||||
const CostValue kExpectedCost =
|
||||
kCost[0][3] + kCost[1][2] + kCost[2][1] + kCost[3][0];
|
||||
ForwardStarGraph graph(kNumSources + kNumTargets, kNumSources * kNumTargets);
|
||||
@@ -46,8 +46,12 @@ void AssignmentOn4x4Matrix() {
|
||||
|
||||
void AnotherAssignment() {
|
||||
LOG(INFO) << "Another assignment on 4x4 matrix";
|
||||
std::vector<std::vector<int>> matrice(
|
||||
{{8, 7, 9, 9}, {5, 2, 7, 8}, {6, 1, 4, 9}, {2, 3, 2, 6}});
|
||||
std::vector<std::vector<int> > matrice({
|
||||
{ 8, 7, 9, 9 }
|
||||
, { 5, 2, 7, 8 }
|
||||
, { 6, 1, 4, 9 }
|
||||
, { 2, 3, 2, 6 }
|
||||
});
|
||||
const int kSize = matrice.size();
|
||||
ForwardStarGraph graph(2 * kSize, kSize * kSize);
|
||||
LinearSumAssignment<ForwardStarGraph> assignement(graph, kSize);
|
||||
@@ -63,9 +67,9 @@ void AnotherAssignment() {
|
||||
LOG(INFO) << "Cost : " << assignement.GetCost();
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::AssignmentOn4x4Matrix();
|
||||
operations_research::AnotherAssignment();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "ortools/linear_solver/linear_solver.pb.h"
|
||||
|
||||
namespace operations_research {
|
||||
void RunLinearProgrammingExample(const std::string& solver_id) {
|
||||
void RunLinearProgrammingExample(const std::string &solver_id) {
|
||||
LOG(INFO) << "---- Linear programming example with " << solver_id << " ----";
|
||||
MPSolver::OptimizationProblemType problem_type;
|
||||
if (!MPSolver::ParseSolverType(solver_id, &problem_type)) {
|
||||
@@ -36,31 +36,31 @@ void RunLinearProgrammingExample(const std::string& solver_id) {
|
||||
|
||||
const double infinity = solver.infinity();
|
||||
// x1, x2 and x3 are continuous non-negative variables.
|
||||
MPVariable* const x1 = solver.MakeNumVar(0.0, infinity, "x1");
|
||||
MPVariable* const x2 = solver.MakeNumVar(0.0, infinity, "x2");
|
||||
MPVariable* const x3 = solver.MakeNumVar(0.0, infinity, "x3");
|
||||
MPVariable *const x1 = solver.MakeNumVar(0.0, infinity, "x1");
|
||||
MPVariable *const x2 = solver.MakeNumVar(0.0, infinity, "x2");
|
||||
MPVariable *const x3 = solver.MakeNumVar(0.0, infinity, "x3");
|
||||
|
||||
// Maximize 10 * x1 + 6 * x2 + 4 * x3.
|
||||
MPObjective* const objective = solver.MutableObjective();
|
||||
MPObjective *const objective = solver.MutableObjective();
|
||||
objective->SetCoefficient(x1, 10);
|
||||
objective->SetCoefficient(x2, 6);
|
||||
objective->SetCoefficient(x3, 4);
|
||||
objective->SetMaximization();
|
||||
|
||||
// x1 + x2 + x3 <= 100.
|
||||
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 100.0);
|
||||
MPConstraint *const c0 = solver.MakeRowConstraint(-infinity, 100.0);
|
||||
c0->SetCoefficient(x1, 1);
|
||||
c0->SetCoefficient(x2, 1);
|
||||
c0->SetCoefficient(x3, 1);
|
||||
|
||||
// 10 * x1 + 4 * x2 + 5 * x3 <= 600.
|
||||
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 600.0);
|
||||
MPConstraint *const c1 = solver.MakeRowConstraint(-infinity, 600.0);
|
||||
c1->SetCoefficient(x1, 10);
|
||||
c1->SetCoefficient(x2, 4);
|
||||
c1->SetCoefficient(x3, 5);
|
||||
|
||||
// 2 * x1 + 2 * x2 + 6 * x3 <= 300.
|
||||
MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 300.0);
|
||||
MPConstraint *const c2 = solver.MakeRowConstraint(-infinity, 300.0);
|
||||
c2->SetCoefficient(x1, 2);
|
||||
c2->SetCoefficient(x2, 2);
|
||||
c2->SetCoefficient(x3, 6);
|
||||
@@ -109,12 +109,12 @@ void RunAllExamples() {
|
||||
RunLinearProgrammingExample("GLPK_LP");
|
||||
RunLinearProgrammingExample("XPRESS_LP");
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&FLAGS_log_prefix, false);
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_log_prefix), false);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::RunAllExamples();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
|
||||
namespace operations_research {
|
||||
void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) {
|
||||
const double kObjCoef[] = {10.0, 6.0, 4.0};
|
||||
const std::string kVarName[] = {"x1", "x2", "x3"};
|
||||
const double kObjCoef[] = { 10.0, 6.0, 4.0 };
|
||||
const std::string kVarName[] = { "x1", "x2", "x3" };
|
||||
const int numVars = 3;
|
||||
const int kNumConstraints = 3;
|
||||
const std::string kConstraintName[] = {"c1", "c2", "c3"};
|
||||
const double kConstraintCoef1[] = {1.0, 1.0, 1.0};
|
||||
const double kConstraintCoef2[] = {10.0, 4.0, 5.0};
|
||||
const double kConstraintCoef3[] = {2.0, 2.0, 6.0};
|
||||
const double* kConstraintCoef[] = {kConstraintCoef1, kConstraintCoef2,
|
||||
kConstraintCoef3};
|
||||
const double kConstraintUb[] = {100.0, 600.0, 300.0};
|
||||
const std::string kConstraintName[] = { "c1", "c2", "c3" };
|
||||
const double kConstraintCoef1[] = { 1.0, 1.0, 1.0 };
|
||||
const double kConstraintCoef2[] = { 10.0, 4.0, 5.0 };
|
||||
const double kConstraintCoef3[] = { 2.0, 2.0, 6.0 };
|
||||
const double *kConstraintCoef[] = { kConstraintCoef1, kConstraintCoef2,
|
||||
kConstraintCoef3 };
|
||||
const double kConstraintUb[] = { 100.0, 600.0, 300.0 };
|
||||
|
||||
const double infinity = MPSolver::infinity();
|
||||
MPModelProto model_proto;
|
||||
@@ -38,20 +38,20 @@ void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) {
|
||||
|
||||
// Create variables and objective function
|
||||
for (int j = 0; j < numVars; ++j) {
|
||||
MPVariableProto* x = model_proto.add_variable();
|
||||
x->set_name(kVarName[j]); // Could be skipped (optional).
|
||||
MPVariableProto *x = model_proto.add_variable();
|
||||
x->set_name(kVarName[j]); // Could be skipped (optional).
|
||||
x->set_lower_bound(0.0);
|
||||
x->set_upper_bound(infinity); // Could be skipped (default value).
|
||||
x->set_is_integer(false); // Could be skipped (default value).
|
||||
x->set_upper_bound(infinity); // Could be skipped (default value).
|
||||
x->set_is_integer(false); // Could be skipped (default value).
|
||||
x->set_objective_coefficient(kObjCoef[j]);
|
||||
}
|
||||
model_proto.set_maximize(true);
|
||||
|
||||
// Create constraints
|
||||
for (int i = 0; i < kNumConstraints; ++i) {
|
||||
MPConstraintProto* constraint_proto = model_proto.add_constraint();
|
||||
constraint_proto->set_name(kConstraintName[i]); // Could be skipped.
|
||||
constraint_proto->set_lower_bound(-infinity); // Could be skipped.
|
||||
MPConstraintProto *constraint_proto = model_proto.add_constraint();
|
||||
constraint_proto->set_name(kConstraintName[i]); // Could be skipped.
|
||||
constraint_proto->set_lower_bound(-infinity); // Could be skipped.
|
||||
constraint_proto->set_upper_bound(kConstraintUb[i]);
|
||||
for (int j = 0; j < numVars; ++j) {
|
||||
// These two lines may be skipped when the coefficient is zero.
|
||||
@@ -66,12 +66,12 @@ void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) {
|
||||
if (type == MPSolver::GLOP_LINEAR_PROGRAMMING) {
|
||||
model_request.set_solver_type(MPModelRequest::GLOP_LINEAR_PROGRAMMING);
|
||||
}
|
||||
#endif // USE_GLOP
|
||||
#endif // USE_GLOP
|
||||
#if defined(USE_CLP)
|
||||
if (type == MPSolver::CLP_LINEAR_PROGRAMMING) {
|
||||
model_request.set_solver_type(MPModelRequest::CLP_LINEAR_PROGRAMMING);
|
||||
}
|
||||
#endif // USE_CLP
|
||||
#endif // USE_CLP
|
||||
|
||||
MPSolutionResponse solution_response;
|
||||
MPSolver::SolveWithProto(model_request, &solution_response);
|
||||
@@ -90,15 +90,15 @@ void RunAllExamples() {
|
||||
#if defined(USE_GLOP)
|
||||
LOG(INFO) << "----- Running Max Example with GLOP -----";
|
||||
BuildLinearProgrammingMaxExample(MPSolver::GLOP_LINEAR_PROGRAMMING);
|
||||
#endif // USE_GLOP
|
||||
#endif // USE_GLOP
|
||||
#if defined(USE_CLP)
|
||||
LOG(INFO) << "----- Running Max Example with Coin LP -----";
|
||||
BuildLinearProgrammingMaxExample(MPSolver::CLP_LINEAR_PROGRAMMING);
|
||||
#endif // USE_CLP
|
||||
#endif // USE_CLP
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::RunAllExamples();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace sat {
|
||||
void MagicSquare(int size) {
|
||||
CpModelBuilder builder;
|
||||
|
||||
std::vector<std::vector<IntVar>> square(size);
|
||||
std::vector<std::vector<IntVar>> transposed(size);
|
||||
std::vector<std::vector<IntVar> > square(size);
|
||||
std::vector<std::vector<IntVar> > transposed(size);
|
||||
std::vector<IntVar> diag1;
|
||||
std::vector<IntVar> diag2;
|
||||
std::vector<IntVar> all_variables;
|
||||
@@ -70,7 +70,7 @@ void MagicSquare(int size) {
|
||||
builder.AddEquality(LinearExpr::Sum(diag2), sum);
|
||||
|
||||
Model model;
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
|
||||
const CpSolverResponse response = SolveCpModel(builder.Build(), &model);
|
||||
|
||||
@@ -89,12 +89,12 @@ void MagicSquare(int size) {
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::sat::MagicSquare(FLAGS_size);
|
||||
operations_research::sat::MagicSquare(absl::GetFlag(FLAGS_size));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,13 @@ void SolveMaxFlow() {
|
||||
// Can't use std::tuple<NodeIndex, NodeIndex, FlowQuantity>
|
||||
// Initialization list is not working on std:tuple cf. N4387
|
||||
// Arc are stored as {{begin_node, end_node}, capacity}
|
||||
std::vector<std::pair<std::pair<NodeIndex, NodeIndex>, FlowQuantity>> arcs = {
|
||||
{{0, 1}, 20}, {{0, 2}, 30}, {{0, 3}, 10}, {{1, 2}, 40}, {{1, 4}, 30},
|
||||
{{2, 3}, 10}, {{2, 4}, 20}, {{3, 2}, 5}, {{3, 4}, 20}};
|
||||
std::vector<std::pair<std::pair<NodeIndex, NodeIndex>, FlowQuantity> > arcs =
|
||||
{ { { 0, 1 }, 20 }, { { 0, 2 }, 30 }, { { 0, 3 }, 10 }, { { 1, 2 }, 40 },
|
||||
{ { 1, 4 }, 30 }, { { 2, 3 }, 10 }, { { 2, 4 }, 20 }, { { 3, 2 }, 5 },
|
||||
{ { 3, 4 }, 20 } };
|
||||
StarGraph graph(num_nodes, arcs.size());
|
||||
MaxFlow max_flow(&graph, 0, num_nodes - 1);
|
||||
for (const auto& it : arcs) {
|
||||
for (const auto &it : arcs) {
|
||||
ArcIndex arc = graph.AddArc(it.first.first, it.first.second);
|
||||
max_flow.SetArcCapacity(arc, it.second);
|
||||
}
|
||||
@@ -49,11 +50,11 @@ void SolveMaxFlow() {
|
||||
<< max_flow.Flow(i) << " / " << max_flow.Capacity(i);
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
absl::GetFlag(FLAGS_logtostderr) = 1;
|
||||
operations_research::SolveMaxFlow();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -24,17 +24,19 @@ struct Arc {
|
||||
|
||||
void SolveMinCostFlow() {
|
||||
// Define supply of each node.
|
||||
const std::vector<std::pair<NodeIndex, FlowQuantity>> supplies = {
|
||||
{0, 20}, {1, 0}, {2, 0}, {3, -5}, {4, -15}};
|
||||
const std::vector<std::pair<NodeIndex, FlowQuantity> > supplies = {
|
||||
{ 0, 20 }, { 1, 0 }, { 2, 0 }, { 3, -5 }, { 4, -15 }
|
||||
};
|
||||
|
||||
// Define each arc
|
||||
// Can't use std::tuple<NodeIndex, NodeIndex, FlowQuantity>
|
||||
// Initialization list is not working on std:tuple cf. N4387
|
||||
// Arc are stored as {{begin_node, end_node}, capacity}
|
||||
const std::vector<Arc> arcs = {
|
||||
{{0, 1}, 15, 4}, {{0, 2}, 8, 4}, {{1, 2}, 20, 2},
|
||||
{{1, 3}, 4, 2}, {{1, 4}, 10, 6}, {{2, 3}, 15, 1},
|
||||
{{2, 4}, 4, 3}, {{3, 4}, 20, 2}, {{4, 2}, 5, 3}};
|
||||
const std::vector<Arc> arcs = { { { 0, 1 }, 15, 4 }, { { 0, 2 }, 8, 4 },
|
||||
{ { 1, 2 }, 20, 2 }, { { 1, 3 }, 4, 2 },
|
||||
{ { 1, 4 }, 10, 6 }, { { 2, 3 }, 15, 1 },
|
||||
{ { 2, 4 }, 4, 3 }, { { 3, 4 }, 20, 2 },
|
||||
{ { 4, 2 }, 5, 3 } };
|
||||
|
||||
StarGraph graph(supplies.size(), arcs.size());
|
||||
MinCostFlow min_cost_flow(&graph);
|
||||
@@ -65,11 +67,11 @@ void SolveMinCostFlow() {
|
||||
<< " / " << min_cost_flow.UnitCost(i);
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
absl::GetFlag(FLAGS_logtostderr) = 1;
|
||||
operations_research::SolveMinCostFlow();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -44,10 +44,9 @@ DEFINE_bool(mps_display_full_path, true,
|
||||
"Displays the full path of the input file in the result line.");
|
||||
DEFINE_string(input, "", "File pattern for problems to be optimized.");
|
||||
DEFINE_string(params_file, "", "Path to a GlopParameters file in text format.");
|
||||
DEFINE_string(params, "",
|
||||
"GlopParameters in text format. If --params_file was "
|
||||
"also specified, the --params will be merged onto "
|
||||
"them (i.e. in case of conflicts, --params wins)");
|
||||
DEFINE_string(params, "", "GlopParameters in text format. If --params_file was "
|
||||
"also specified, the --params will be merged onto "
|
||||
"them (i.e. in case of conflicts, --params wins)");
|
||||
|
||||
using google::protobuf::TextFormat;
|
||||
using operations_research::FullProtocolMessageAsString;
|
||||
@@ -62,22 +61,22 @@ using operations_research::glop::ProblemStatus;
|
||||
using operations_research::glop::ToDouble;
|
||||
|
||||
// Parse glop parameters from the flags --params_file and --params.
|
||||
void ReadGlopParameters(GlopParameters* parameters) {
|
||||
if (!FLAGS_params_file.empty()) {
|
||||
void ReadGlopParameters(GlopParameters *parameters) {
|
||||
if (!absl::GetFlag(FLAGS_params_file).empty()) {
|
||||
std::string params;
|
||||
CHECK(TextFormat::ParseFromString(params, parameters)) << params;
|
||||
}
|
||||
if (!FLAGS_params.empty()) {
|
||||
CHECK(TextFormat::MergeFromString(FLAGS_params, parameters))
|
||||
<< FLAGS_params;
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
CHECK(TextFormat::MergeFromString(absl::GetFlag(FLAGS_params), parameters))
|
||||
<< absl::GetFlag(FLAGS_params);
|
||||
}
|
||||
if (FLAGS_mps_verbose_result) {
|
||||
if (absl::GetFlag(FLAGS_mps_verbose_result)) {
|
||||
printf("GlopParameters {\n%s}\n",
|
||||
FullProtocolMessageAsString(*parameters, 1).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int main(int argc, char *argv[]) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
GlopParameters parameters;
|
||||
@@ -86,9 +85,9 @@ int main(int argc, char* argv[]) {
|
||||
LinearProgram linear_program;
|
||||
std::vector<std::string> file_list;
|
||||
// Replace this with your favorite match function.
|
||||
file_list.push_back(FLAGS_input);
|
||||
file_list.push_back(absl::GetFlag(FLAGS_input));
|
||||
for (int i = 0; i < file_list.size(); ++i) {
|
||||
const std::string& file_name = file_list[i];
|
||||
const std::string &file_name = file_list[i];
|
||||
MPSReader mps_reader;
|
||||
operations_research::MPModelProto model_proto;
|
||||
if (absl::EndsWith(file_name, ".mps") ||
|
||||
@@ -103,7 +102,7 @@ int main(int argc, char* argv[]) {
|
||||
ReadFileToProto(file_name, &model_proto);
|
||||
MPModelProtoToLinearProgram(model_proto, &linear_program);
|
||||
}
|
||||
if (FLAGS_mps_dump_problem) {
|
||||
if (absl::GetFlag(FLAGS_mps_dump_problem)) {
|
||||
printf("%s", linear_program.Dump().c_str());
|
||||
}
|
||||
|
||||
@@ -115,19 +114,19 @@ int main(int argc, char* argv[]) {
|
||||
std::string status_string;
|
||||
double objective_value;
|
||||
double solving_time_in_sec = 0;
|
||||
if (FLAGS_mps_solve) {
|
||||
if (absl::GetFlag(FLAGS_mps_solve)) {
|
||||
ScopedWallTime timer(&solving_time_in_sec);
|
||||
solve_status = solver.Solve(linear_program);
|
||||
status_string = GetProblemStatusString(solve_status);
|
||||
objective_value = ToDouble(solver.GetObjectiveValue());
|
||||
}
|
||||
|
||||
if (FLAGS_mps_terse_result) {
|
||||
if (FLAGS_mps_display_full_path) {
|
||||
if (absl::GetFlag(FLAGS_mps_terse_result)) {
|
||||
if (absl::GetFlag(FLAGS_mps_display_full_path)) {
|
||||
printf("%s,", file_name.c_str());
|
||||
}
|
||||
printf("%s,", linear_program.name().c_str());
|
||||
if (FLAGS_mps_solve) {
|
||||
if (absl::GetFlag(FLAGS_mps_solve)) {
|
||||
printf("%15.15e,%s,%-6.4g,", objective_value, status_string.c_str(),
|
||||
solving_time_in_sec);
|
||||
}
|
||||
@@ -135,12 +134,12 @@ int main(int argc, char* argv[]) {
|
||||
linear_program.GetNonZeroStats().c_str());
|
||||
}
|
||||
|
||||
if (FLAGS_mps_verbose_result) {
|
||||
if (FLAGS_mps_display_full_path) {
|
||||
if (absl::GetFlag(FLAGS_mps_verbose_result)) {
|
||||
if (absl::GetFlag(FLAGS_mps_display_full_path)) {
|
||||
printf("%-45s: %s\n", "File path", file_name.c_str());
|
||||
}
|
||||
printf("%-45s: %s\n", "Problem name", linear_program.name().c_str());
|
||||
if (FLAGS_mps_solve) {
|
||||
if (absl::GetFlag(FLAGS_mps_solve)) {
|
||||
printf("%-45s: %15.15e\n", "Objective value", objective_value);
|
||||
printf("%-45s: %s\n", "Problem status", status_string.c_str());
|
||||
printf("%-45s: %-6.4g\n", "Solving time", solving_time_in_sec);
|
||||
|
||||
@@ -37,19 +37,19 @@ static const int kVolumeMin = 1156;
|
||||
static const int kVolumeMax = 1600;
|
||||
|
||||
// Data for a single bin problem
|
||||
static const int kItemsWeights[] = {1008, 2087, 5522, 5250, 5720,
|
||||
4998, 275, 3145, 12580, 382};
|
||||
static const int kItemsVolumes[] = {281, 307, 206, 111, 275,
|
||||
79, 23, 65, 261, 40};
|
||||
static const int kItemsWeights[] = { 1008, 2087, 5522, 5250, 5720, 4998, 275,
|
||||
3145, 12580, 382 };
|
||||
static const int kItemsVolumes[] = { 281, 307, 206, 111, 275, 79, 23, 65, 261,
|
||||
40 };
|
||||
static const int kNumItems = 10;
|
||||
|
||||
void MultiKnapsackSat(int scaling, const std::string& params) {
|
||||
void MultiKnapsackSat(int scaling, const std::string ¶ms) {
|
||||
CpModelBuilder builder;
|
||||
|
||||
const int num_items = scaling * kNumItems;
|
||||
const int num_bins = scaling;
|
||||
|
||||
std::vector<std::vector<BoolVar>> items_in_bins(num_bins);
|
||||
std::vector<std::vector<BoolVar> > items_in_bins(num_bins);
|
||||
for (int b = 0; b < num_bins; ++b) {
|
||||
for (int i = 0; i < num_items; ++i) {
|
||||
items_in_bins[b].push_back(builder.NewBoolVar());
|
||||
@@ -74,13 +74,16 @@ void MultiKnapsackSat(int scaling, const std::string& params) {
|
||||
// Constraints per bins.
|
||||
std::vector<IntVar> bin_weights;
|
||||
for (int b = 0; b < num_bins; ++b) {
|
||||
IntVar bin_weight = builder.NewIntVar({kWeightMin, kWeightMax});
|
||||
IntVar bin_weight = builder.NewIntVar({
|
||||
kWeightMin, kWeightMax
|
||||
});
|
||||
bin_weights.push_back(bin_weight);
|
||||
builder.AddEquality(LinearExpr::BooleanScalProd(items_in_bins[b], weights),
|
||||
bin_weight);
|
||||
builder.AddLinearConstraint(
|
||||
LinearExpr::BooleanScalProd(items_in_bins[b], volumes),
|
||||
{kVolumeMin, kVolumeMax});
|
||||
LinearExpr::BooleanScalProd(items_in_bins[b], volumes), {
|
||||
kVolumeMin, kVolumeMax
|
||||
});
|
||||
}
|
||||
|
||||
// Each item is selected at most one time.
|
||||
@@ -102,12 +105,13 @@ void MultiKnapsackSat(int scaling, const std::string& params) {
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
int main(int argc, char **argv) {
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::sat::MultiKnapsackSat(FLAGS_size, FLAGS_params);
|
||||
operations_research::sat::MultiKnapsackSat(absl::GetFlag(FLAGS_size),
|
||||
absl::GetFlag(FLAGS_params));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -45,9 +45,8 @@
|
||||
#include "ortools/util/time_limit.h"
|
||||
|
||||
// ----- Data Generator -----
|
||||
DEFINE_int32(clients, 0,
|
||||
"Number of network clients nodes. If equal to zero, "
|
||||
"then all backbones nodes are also client nodes.");
|
||||
DEFINE_int32(clients, 0, "Number of network clients nodes. If equal to zero, "
|
||||
"then all backbones nodes are also client nodes.");
|
||||
DEFINE_int32(backbones, 0, "Number of backbone nodes");
|
||||
DEFINE_int32(demands, 0, "Number of network demands.");
|
||||
DEFINE_int32(traffic_min, 0, "Min traffic of a demand.");
|
||||
@@ -91,7 +90,7 @@ static const int64 kDisconnectedDistance = -1LL;
|
||||
// (capacity(i->j) == capacity(j->i)).
|
||||
// Demands are not symmetrical.
|
||||
class NetworkRoutingData {
|
||||
public:
|
||||
public:
|
||||
NetworkRoutingData()
|
||||
: name_(""), num_nodes_(-1), max_capacity_(-1), fixed_charge_cost_(-1) {}
|
||||
|
||||
@@ -132,7 +131,7 @@ class NetworkRoutingData {
|
||||
void set_max_capacity(int max_capacity) { max_capacity_ = max_capacity; }
|
||||
void set_fixed_charge_cost(int cost) { fixed_charge_cost_ = cost; }
|
||||
|
||||
private:
|
||||
private:
|
||||
std::string name_;
|
||||
int num_nodes_;
|
||||
int max_capacity_;
|
||||
@@ -155,7 +154,7 @@ class NetworkRoutingData {
|
||||
// Each arc has a capacity of 'max_capacity'. Using an arc incurs a
|
||||
// fixed cost of 'fixed_charge_cost'.
|
||||
class NetworkRoutingDataBuilder {
|
||||
public:
|
||||
public:
|
||||
NetworkRoutingDataBuilder() : random_(0) {}
|
||||
|
||||
void BuildModelFromParameters(int num_clients, int num_backbones,
|
||||
@@ -192,7 +191,7 @@ class NetworkRoutingDataBuilder {
|
||||
max_backbone_degree, max_capacity, fixed_charge_cost, seed, data);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
void InitData(int size, int seed) {
|
||||
network_.clear();
|
||||
network_.resize(size);
|
||||
@@ -320,7 +319,7 @@ class NetworkRoutingDataBuilder {
|
||||
: random_.Uniform(num_clients) + num_backbones;
|
||||
}
|
||||
|
||||
std::vector<std::vector<bool>> network_;
|
||||
std::vector<std::vector<bool> > network_;
|
||||
std::vector<int> degrees_;
|
||||
MTRandom random_;
|
||||
};
|
||||
@@ -329,18 +328,17 @@ class NetworkRoutingDataBuilder {
|
||||
|
||||
// Useful data struct to hold demands.
|
||||
struct Demand {
|
||||
public:
|
||||
public:
|
||||
Demand(int the_source, int the_destination, int the_traffic)
|
||||
: source(the_source),
|
||||
destination(the_destination),
|
||||
traffic(the_traffic) {}
|
||||
: source(the_source), destination(the_destination), traffic(the_traffic) {
|
||||
}
|
||||
int source;
|
||||
int destination;
|
||||
int traffic;
|
||||
};
|
||||
|
||||
class NetworkRoutingSolver {
|
||||
public:
|
||||
public:
|
||||
typedef std::unordered_set<int> OnePath;
|
||||
|
||||
NetworkRoutingSolver() : num_nodes_(-1) {}
|
||||
@@ -364,8 +362,9 @@ class NetworkRoutingSolver {
|
||||
tmp_vars.push_back(node_vars[i]);
|
||||
tmp_vars.push_back(node_vars[i + 1]);
|
||||
tmp_vars.push_back(arc_vars[i]);
|
||||
TableConstraint table = cp_model.AddAllowedAssignments(
|
||||
{node_vars[i], node_vars[i + 1], arc_vars[i]});
|
||||
TableConstraint table = cp_model.AddAllowedAssignments({
|
||||
node_vars[i], node_vars[i + 1], arc_vars[i]
|
||||
});
|
||||
for (const auto &tuple : arcs_data_) {
|
||||
table.AddTuple(tuple);
|
||||
}
|
||||
@@ -382,7 +381,7 @@ class NetworkRoutingSolver {
|
||||
std::atomic<bool> stopped(false);
|
||||
model.GetOrCreate<TimeLimit>()->RegisterExternalBooleanAsLimit(&stopped);
|
||||
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse &r) {
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse & r) {
|
||||
const int path_id = all_paths_[demand_index].size();
|
||||
all_paths_[demand_index].resize(path_id + 1);
|
||||
for (int arc_index = 0; arc_index < max_length - 1; ++arc_index) {
|
||||
@@ -423,7 +422,9 @@ class NetworkRoutingSolver {
|
||||
}
|
||||
|
||||
void AddArcData(int64 source, int64 destination, int arc_id) {
|
||||
arcs_data_.push_back({source, destination, arc_id});
|
||||
arcs_data_.push_back({
|
||||
source, destination, arc_id
|
||||
});
|
||||
}
|
||||
|
||||
void InitArcInfo(const NetworkRoutingData &data) {
|
||||
@@ -444,7 +445,7 @@ class NetworkRoutingSolver {
|
||||
arc_capacity_.push_back(capacity);
|
||||
capacity_[i][j] = capacity;
|
||||
capacity_[j][i] = capacity;
|
||||
if (FLAGS_print_model) {
|
||||
if (absl::GetFlag(FLAGS_print_model)) {
|
||||
LOG(INFO) << "Arc " << i << " <-> " << j << " with capacity "
|
||||
<< capacity;
|
||||
}
|
||||
@@ -478,10 +479,11 @@ class NetworkRoutingSolver {
|
||||
for (int demand_index = 0; demand_index < num_demands; ++demand_index) {
|
||||
paths.clear();
|
||||
const Demand &demand = demands_array_[demand_index];
|
||||
CHECK(DijkstraShortestPath(
|
||||
num_nodes_, demand.source, demand.destination,
|
||||
[this](int x, int y) { return HasArc(x, y); }, kDisconnectedDistance,
|
||||
&paths));
|
||||
CHECK(DijkstraShortestPath(num_nodes_, demand.source, demand.destination,
|
||||
[this](int x, int y) {
|
||||
return HasArc(x, y);
|
||||
},
|
||||
kDisconnectedDistance, &paths));
|
||||
all_min_path_lengths_.push_back(paths.size() - 1);
|
||||
}
|
||||
|
||||
@@ -540,7 +542,7 @@ class NetworkRoutingSolver {
|
||||
if (capacity_[i][j] > 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return kDisconnectedDistance; // disconnected distance.
|
||||
return kDisconnectedDistance; // disconnected distance.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,7 +555,7 @@ class NetworkRoutingSolver {
|
||||
|
||||
// ----- Build Model -----
|
||||
CpModelBuilder cp_model;
|
||||
std::vector<std::vector<IntVar>> path_vars(num_demands);
|
||||
std::vector<std::vector<IntVar> > path_vars(num_demands);
|
||||
|
||||
// Node - Graph Constraint.
|
||||
for (int demand_index = 0; demand_index < num_demands; ++demand_index) {
|
||||
@@ -591,7 +593,12 @@ class NetworkRoutingSolver {
|
||||
const int64 capacity = arc_capacity_[arc_index];
|
||||
IntVar scaled_traffic =
|
||||
cp_model.NewIntVar(Domain(0, sum_of_traffic * 1000));
|
||||
cp_model.AddEquality(LinearExpr::ScalProd({traffic_var}, {1000}),
|
||||
cp_model.AddEquality(LinearExpr::ScalProd({
|
||||
traffic_var
|
||||
},
|
||||
{
|
||||
1000
|
||||
}),
|
||||
scaled_traffic);
|
||||
IntVar normalized_traffic =
|
||||
cp_model.NewIntVar(Domain(0, sum_of_traffic * 1000 / capacity));
|
||||
@@ -602,7 +609,7 @@ class NetworkRoutingSolver {
|
||||
normalized_traffic_vars[arc_index] = normalized_traffic;
|
||||
const BoolVar comfort = cp_model.NewBoolVar();
|
||||
const int64 safe_capacity =
|
||||
static_cast<int64>(capacity * FLAGS_comfort_zone);
|
||||
static_cast<int64>(capacity * absl::GetFlag(FLAGS_comfort_zone));
|
||||
cp_model.AddGreaterThan(traffic_var, safe_capacity)
|
||||
.OnlyEnforceIf(comfort);
|
||||
cp_model.AddLessOrEqual(traffic_var, safe_capacity)
|
||||
@@ -622,11 +629,11 @@ class NetworkRoutingSolver {
|
||||
cp_model.Minimize(objective_expr);
|
||||
|
||||
Model model;
|
||||
if (!FLAGS_params.empty()) {
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
}
|
||||
int num_solutions = 0;
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse &r) {
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse & r) {
|
||||
LOG(INFO) << "Solution " << num_solutions;
|
||||
const double objective_value = r.objective_value();
|
||||
const double percent = SolutionIntegerValue(r, max_usage_cost) / 10.0;
|
||||
@@ -648,32 +655,37 @@ class NetworkRoutingSolver {
|
||||
return response.objective_value();
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int count_arcs() const { return arcs_data_.size() / 2; }
|
||||
|
||||
std::vector<std::vector<int64>> arcs_data_;
|
||||
std::vector<std::vector<int64> > arcs_data_;
|
||||
std::vector<int> arc_capacity_;
|
||||
std::vector<Demand> demands_array_;
|
||||
int num_nodes_;
|
||||
std::vector<int64> all_min_path_lengths_;
|
||||
std::vector<std::vector<int>> capacity_;
|
||||
std::vector<std::vector<OnePath>> all_paths_;
|
||||
std::vector<std::vector<int> > capacity_;
|
||||
std::vector<std::vector<OnePath> > all_paths_;
|
||||
};
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::sat::NetworkRoutingData data;
|
||||
operations_research::sat::NetworkRoutingDataBuilder builder;
|
||||
builder.BuildModelFromParameters(
|
||||
FLAGS_clients, FLAGS_backbones, FLAGS_demands, FLAGS_traffic_min,
|
||||
FLAGS_traffic_max, FLAGS_min_client_degree, FLAGS_max_client_degree,
|
||||
FLAGS_min_backbone_degree, FLAGS_max_backbone_degree, FLAGS_max_capacity,
|
||||
FLAGS_fixed_charge_cost, FLAGS_seed, &data);
|
||||
absl::GetFlag(FLAGS_clients), absl::GetFlag(FLAGS_backbones),
|
||||
absl::GetFlag(FLAGS_demands), absl::GetFlag(FLAGS_traffic_min),
|
||||
absl::GetFlag(FLAGS_traffic_max), absl::GetFlag(FLAGS_min_client_degree),
|
||||
absl::GetFlag(FLAGS_max_client_degree),
|
||||
absl::GetFlag(FLAGS_min_backbone_degree),
|
||||
absl::GetFlag(FLAGS_max_backbone_degree),
|
||||
absl::GetFlag(FLAGS_max_capacity), absl::GetFlag(FLAGS_fixed_charge_cost),
|
||||
absl::GetFlag(FLAGS_seed), &data);
|
||||
operations_research::sat::NetworkRoutingSolver solver;
|
||||
solver.Init(data, FLAGS_extra_hops, FLAGS_max_paths);
|
||||
solver.Init(data, absl::GetFlag(FLAGS_extra_hops),
|
||||
absl::GetFlag(FLAGS_max_paths));
|
||||
LOG(INFO) << "Final cost = " << solver.Solve();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -37,20 +37,20 @@ DEFINE_int32(
|
||||
DEFINE_bool(use_symmetry, false, "Use Symmetry Breaking methods");
|
||||
DECLARE_bool(cp_disable_solve);
|
||||
|
||||
static const int kNumSolutions[] = {
|
||||
1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712, 365596, 2279184};
|
||||
static const int kNumSolutions[] = { 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680,
|
||||
14200, 73712, 365596, 2279184 };
|
||||
static const int kKnownSolutions = 15;
|
||||
|
||||
static const int kNumUniqueSolutions[] = {
|
||||
1, 0, 0, 1, 2, 1, 6, 12, 46, 92,
|
||||
341, 1787, 9233, 45752, 285053, 1846955, 11977939, 83263591, 621012754};
|
||||
static const int kNumUniqueSolutions[] = { 1, 0, 0, 1, 2, 1, 6, 12, 46, 92, 341,
|
||||
1787, 9233, 45752, 285053, 1846955,
|
||||
11977939, 83263591, 621012754 };
|
||||
static const int kKnownUniqueSolutions = 19;
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
class NQueenSymmetry : public SymmetryBreaker {
|
||||
public:
|
||||
NQueenSymmetry(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
NQueenSymmetry(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: solver_(s), vars_(vars), size_(vars.size()) {
|
||||
for (int i = 0; i < size_; ++i) {
|
||||
indices_[vars[i]] = i;
|
||||
@@ -58,133 +58,133 @@ class NQueenSymmetry : public SymmetryBreaker {
|
||||
}
|
||||
~NQueenSymmetry() override {}
|
||||
|
||||
protected:
|
||||
int Index(IntVar* const var) const {
|
||||
protected:
|
||||
int Index(IntVar *const var) const {
|
||||
return gtl::FindWithDefault(indices_, var, -1);
|
||||
}
|
||||
IntVar* Var(int index) const {
|
||||
IntVar *Var(int index) const {
|
||||
DCHECK_GE(index, 0);
|
||||
DCHECK_LT(index, size_);
|
||||
return vars_[index];
|
||||
}
|
||||
int size() const { return size_; }
|
||||
int symmetric(int index) const { return size_ - 1 - index; }
|
||||
Solver* const solver() const { return solver_; }
|
||||
Solver *const solver() const { return solver_; }
|
||||
|
||||
private:
|
||||
Solver* const solver_;
|
||||
const std::vector<IntVar*> vars_;
|
||||
std::map<const IntVar*, int> indices_;
|
||||
private:
|
||||
Solver *const solver_;
|
||||
const std::vector<IntVar *> vars_;
|
||||
std::map<const IntVar *, int> indices_;
|
||||
const int size_;
|
||||
};
|
||||
|
||||
// Symmetry vertical axis.
|
||||
class SX : public NQueenSymmetry {
|
||||
public:
|
||||
SX(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
SX(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~SX() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
const int index = Index(var);
|
||||
IntVar* const other_var = Var(symmetric(index));
|
||||
IntVar *const other_var = Var(symmetric(index));
|
||||
AddIntegerVariableEqualValueClause(other_var, value);
|
||||
}
|
||||
};
|
||||
|
||||
// Symmetry horizontal axis.
|
||||
class SY : public NQueenSymmetry {
|
||||
public:
|
||||
SY(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
SY(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~SY() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
AddIntegerVariableEqualValueClause(var, symmetric(value));
|
||||
}
|
||||
};
|
||||
|
||||
// Symmetry first diagonal axis.
|
||||
class SD1 : public NQueenSymmetry {
|
||||
public:
|
||||
SD1(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
SD1(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~SD1() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
const int index = Index(var);
|
||||
IntVar* const other_var = Var(value);
|
||||
IntVar *const other_var = Var(value);
|
||||
AddIntegerVariableEqualValueClause(other_var, index);
|
||||
}
|
||||
};
|
||||
|
||||
// Symmetry second diagonal axis.
|
||||
class SD2 : public NQueenSymmetry {
|
||||
public:
|
||||
SD2(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
SD2(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~SD2() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
const int index = Index(var);
|
||||
IntVar* const other_var = Var(symmetric(value));
|
||||
IntVar *const other_var = Var(symmetric(value));
|
||||
AddIntegerVariableEqualValueClause(other_var, symmetric(index));
|
||||
}
|
||||
};
|
||||
|
||||
// Rotate 1/4 turn.
|
||||
class R90 : public NQueenSymmetry {
|
||||
public:
|
||||
R90(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
R90(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~R90() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
const int index = Index(var);
|
||||
IntVar* const other_var = Var(value);
|
||||
IntVar *const other_var = Var(value);
|
||||
AddIntegerVariableEqualValueClause(other_var, symmetric(index));
|
||||
}
|
||||
};
|
||||
|
||||
// Rotate 1/2 turn.
|
||||
class R180 : public NQueenSymmetry {
|
||||
public:
|
||||
R180(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
R180(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~R180() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
const int index = Index(var);
|
||||
IntVar* const other_var = Var(symmetric(index));
|
||||
IntVar *const other_var = Var(symmetric(index));
|
||||
AddIntegerVariableEqualValueClause(other_var, symmetric(value));
|
||||
}
|
||||
};
|
||||
|
||||
// Rotate 3/4 turn.
|
||||
class R270 : public NQueenSymmetry {
|
||||
public:
|
||||
R270(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
public:
|
||||
R270(Solver *const s, const std::vector<IntVar *> &vars)
|
||||
: NQueenSymmetry(s, vars) {}
|
||||
~R270() override {}
|
||||
|
||||
void VisitSetVariableValue(IntVar* const var, int64 value) override {
|
||||
void VisitSetVariableValue(IntVar *const var, int64 value) override {
|
||||
const int index = Index(var);
|
||||
IntVar* const other_var = Var(symmetric(value));
|
||||
IntVar *const other_var = Var(symmetric(value));
|
||||
AddIntegerVariableEqualValueClause(other_var, index);
|
||||
}
|
||||
};
|
||||
|
||||
void CheckNumberOfSolutions(int size, int num_solutions) {
|
||||
if (FLAGS_use_symmetry) {
|
||||
if (absl::GetFlag(FLAGS_use_symmetry)) {
|
||||
if (size - 1 < kKnownUniqueSolutions) {
|
||||
CHECK_EQ(num_solutions, kNumUniqueSolutions[size - 1]);
|
||||
} else if (!FLAGS_cp_disable_solve) {
|
||||
} else if (!absl::GetFlag(FLAGS_cp_disable_solve)) {
|
||||
CHECK_GT(num_solutions, 0);
|
||||
}
|
||||
} else {
|
||||
if (size - 1 < kKnownSolutions) {
|
||||
CHECK_EQ(num_solutions, kNumSolutions[size - 1]);
|
||||
} else if (!FLAGS_cp_disable_solve) {
|
||||
} else if (!absl::GetFlag(FLAGS_cp_disable_solve)) {
|
||||
CHECK_GT(num_solutions, 0);
|
||||
}
|
||||
}
|
||||
@@ -195,14 +195,14 @@ void NQueens(int size) {
|
||||
Solver s("nqueens");
|
||||
|
||||
// model
|
||||
std::vector<IntVar*> queens;
|
||||
std::vector<IntVar *> queens;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
queens.push_back(
|
||||
s.MakeIntVar(0, size - 1, absl::StrFormat("queen%04d", i)));
|
||||
}
|
||||
s.AddConstraint(s.MakeAllDifferent(queens));
|
||||
|
||||
std::vector<IntVar*> vars(size);
|
||||
std::vector<IntVar *> vars(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
vars[i] = s.MakeSum(queens[i], i)->Var();
|
||||
}
|
||||
@@ -212,50 +212,55 @@ void NQueens(int size) {
|
||||
}
|
||||
s.AddConstraint(s.MakeAllDifferent(vars));
|
||||
|
||||
SolutionCollector* const solution_counter =
|
||||
SolutionCollector *const solution_counter =
|
||||
s.MakeAllSolutionCollector(nullptr);
|
||||
SolutionCollector* const collector = s.MakeAllSolutionCollector();
|
||||
SolutionCollector *const collector = s.MakeAllSolutionCollector();
|
||||
collector->Add(queens);
|
||||
std::vector<SearchMonitor*> monitors;
|
||||
std::vector<SearchMonitor *> monitors;
|
||||
monitors.push_back(solution_counter);
|
||||
monitors.push_back(collector);
|
||||
DecisionBuilder* const db = s.MakePhase(queens, Solver::CHOOSE_FIRST_UNBOUND,
|
||||
DecisionBuilder *const db = s.MakePhase(queens, Solver::CHOOSE_FIRST_UNBOUND,
|
||||
Solver::ASSIGN_MIN_VALUE);
|
||||
if (FLAGS_use_symmetry) {
|
||||
std::vector<SymmetryBreaker*> breakers;
|
||||
NQueenSymmetry* const sx = s.RevAlloc(new SX(&s, queens));
|
||||
if (absl::GetFlag(FLAGS_use_symmetry)) {
|
||||
std::vector<SymmetryBreaker *> breakers;
|
||||
NQueenSymmetry *const sx = s.RevAlloc(new SX(&s, queens));
|
||||
breakers.push_back(sx);
|
||||
NQueenSymmetry* const sy = s.RevAlloc(new SY(&s, queens));
|
||||
NQueenSymmetry *const sy = s.RevAlloc(new SY(&s, queens));
|
||||
breakers.push_back(sy);
|
||||
NQueenSymmetry* const sd1 = s.RevAlloc(new SD1(&s, queens));
|
||||
NQueenSymmetry *const sd1 = s.RevAlloc(new SD1(&s, queens));
|
||||
breakers.push_back(sd1);
|
||||
NQueenSymmetry* const sd2 = s.RevAlloc(new SD2(&s, queens));
|
||||
NQueenSymmetry *const sd2 = s.RevAlloc(new SD2(&s, queens));
|
||||
breakers.push_back(sd2);
|
||||
NQueenSymmetry* const r90 = s.RevAlloc(new R90(&s, queens));
|
||||
NQueenSymmetry *const r90 = s.RevAlloc(new R90(&s, queens));
|
||||
breakers.push_back(r90);
|
||||
NQueenSymmetry* const r180 = s.RevAlloc(new R180(&s, queens));
|
||||
NQueenSymmetry *const r180 = s.RevAlloc(new R180(&s, queens));
|
||||
breakers.push_back(r180);
|
||||
NQueenSymmetry* const r270 = s.RevAlloc(new R270(&s, queens));
|
||||
NQueenSymmetry *const r270 = s.RevAlloc(new R270(&s, queens));
|
||||
breakers.push_back(r270);
|
||||
SearchMonitor* const symmetry_manager = s.MakeSymmetryManager(breakers);
|
||||
SearchMonitor *const symmetry_manager = s.MakeSymmetryManager(breakers);
|
||||
monitors.push_back(symmetry_manager);
|
||||
}
|
||||
|
||||
for (int loop = 0; loop < FLAGS_nb_loops; ++loop) {
|
||||
s.Solve(db, monitors); // go!
|
||||
for (int loop = 0; loop < absl::GetFlag(FLAGS_nb_loops); ++loop) {
|
||||
s.Solve(db, monitors); // go!
|
||||
CheckNumberOfSolutions(size, solution_counter->solution_count());
|
||||
}
|
||||
|
||||
const int num_solutions = solution_counter->solution_count();
|
||||
if (num_solutions > 0 && size < kKnownSolutions) {
|
||||
int print_max = FLAGS_print_all ? num_solutions : FLAGS_print ? 1 : 0;
|
||||
int print_max = absl::GetFlag(FLAGS_print_all) ? num_solutions
|
||||
: absl::GetFlag(FLAGS_print)
|
||||
? 1
|
||||
: 0;
|
||||
for (int n = 0; n < print_max; ++n) {
|
||||
printf("--- solution #%d\n", n);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const int pos = static_cast<int>(collector->Value(n, queens[i]));
|
||||
for (int k = 0; k < pos; ++k) printf(" . ");
|
||||
for (int k = 0; k < pos; ++k)
|
||||
printf(" . ");
|
||||
printf("%2d ", i);
|
||||
for (int k = pos + 1; k < size; ++k) printf(" . ");
|
||||
for (int k = pos + 1; k < size; ++k)
|
||||
printf(" . ");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@@ -263,12 +268,12 @@ void NQueens(int size) {
|
||||
printf("========= number of solutions:%d\n", num_solutions);
|
||||
absl::PrintF(" number of failures: %d\n", s.failures());
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_size != 0) {
|
||||
operations_research::NQueens(FLAGS_size);
|
||||
if (absl::GetFlag(FLAGS_size) != 0) {
|
||||
operations_research::NQueens(absl::GetFlag(FLAGS_size));
|
||||
} else {
|
||||
for (int n = 1; n < 12; ++n) {
|
||||
operations_research::NQueens(n);
|
||||
|
||||
@@ -33,17 +33,17 @@ namespace sat {
|
||||
// The format is described here:
|
||||
// http://www.cril.univ-artois.fr/PB12/format.pdf
|
||||
class OpbReader {
|
||||
public:
|
||||
public:
|
||||
OpbReader() {}
|
||||
|
||||
// Loads the given opb filename into the given problem.
|
||||
bool Load(const std::string& filename, LinearBooleanProblem* problem) {
|
||||
bool Load(const std::string &filename, LinearBooleanProblem *problem) {
|
||||
problem->Clear();
|
||||
problem->set_name(ExtractProblemName(filename));
|
||||
|
||||
num_variables_ = 0;
|
||||
int num_lines = 0;
|
||||
for (const std::string& line : FileLines(filename)) {
|
||||
for (const std::string &line : FileLines(filename)) {
|
||||
++num_lines;
|
||||
ProcessNewLine(problem, line);
|
||||
}
|
||||
@@ -54,17 +54,17 @@ class OpbReader {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Since the problem name is not stored in the cnf format, we infer it from
|
||||
// the file name.
|
||||
static std::string ExtractProblemName(const std::string& filename) {
|
||||
static std::string ExtractProblemName(const std::string &filename) {
|
||||
const int found = filename.find_last_of("/");
|
||||
const std::string problem_name =
|
||||
found != std::string::npos ? filename.substr(found + 1) : filename;
|
||||
return problem_name;
|
||||
}
|
||||
|
||||
void ProcessNewLine(LinearBooleanProblem* problem, const std::string& line) {
|
||||
void ProcessNewLine(LinearBooleanProblem *problem, const std::string &line) {
|
||||
const std::vector<std::string> words =
|
||||
absl::StrSplit(line, absl::ByAnyChar(" ;"), absl::SkipEmpty());
|
||||
if (words.empty() || words[0].empty() || words[0][0] == '*') {
|
||||
@@ -72,10 +72,11 @@ class OpbReader {
|
||||
}
|
||||
|
||||
if (words[0] == "min:") {
|
||||
LinearObjective* objective = problem->mutable_objective();
|
||||
LinearObjective *objective = problem->mutable_objective();
|
||||
for (int i = 1; i < words.size(); ++i) {
|
||||
const std::string& word = words[i];
|
||||
if (word.empty() || word[0] == ';') continue;
|
||||
const std::string &word = words[i];
|
||||
if (word.empty() || word[0] == ';')
|
||||
continue;
|
||||
if (word[0] == 'x') {
|
||||
int literal;
|
||||
CHECK(absl::SimpleAtoi(word.substr(1), &literal));
|
||||
@@ -93,9 +94,9 @@ class OpbReader {
|
||||
}
|
||||
return;
|
||||
}
|
||||
LinearBooleanConstraint* constraint = problem->add_constraints();
|
||||
LinearBooleanConstraint *constraint = problem->add_constraints();
|
||||
for (int i = 0; i < words.size(); ++i) {
|
||||
const std::string& word = words[i];
|
||||
const std::string &word = words[i];
|
||||
CHECK(!word.empty());
|
||||
if (word == ">=") {
|
||||
CHECK_LT(i + 1, words.size());
|
||||
@@ -132,7 +133,7 @@ class OpbReader {
|
||||
DISALLOW_COPY_AND_ASSIGN(OpbReader);
|
||||
};
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_SAT_OPB_READER_H_
|
||||
#endif // OR_TOOLS_SAT_OPB_READER_H_
|
||||
|
||||
@@ -38,13 +38,11 @@ DEFINE_bool(assignment_optimize_layout, true,
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
template <typename GraphType>
|
||||
class LinearSumAssignment;
|
||||
template <typename GraphType> class LinearSumAssignment;
|
||||
|
||||
template <typename GraphType>
|
||||
class DimacsAssignmentParser {
|
||||
public:
|
||||
explicit DimacsAssignmentParser(const std::string& filename)
|
||||
template <typename GraphType> class DimacsAssignmentParser {
|
||||
public:
|
||||
explicit DimacsAssignmentParser(const std::string &filename)
|
||||
: filename_(filename), graph_builder_(nullptr), assignment_(nullptr) {}
|
||||
|
||||
// Reads an assignment problem description from the given file in
|
||||
@@ -60,31 +58,28 @@ class DimacsAssignmentParser {
|
||||
// representation back to the caller, the caller lacks a good way to
|
||||
// free the underlying graph (which isn't owned by the
|
||||
// LinearAssignment instance).
|
||||
LinearSumAssignment<GraphType>* Parse(std::string* error_message,
|
||||
GraphType** graph);
|
||||
LinearSumAssignment<GraphType> *Parse(std::string *error_message,
|
||||
GraphType **graph);
|
||||
|
||||
private:
|
||||
void ParseProblemLine(const std::string& line);
|
||||
private:
|
||||
void ParseProblemLine(const std::string &line);
|
||||
|
||||
void ParseNodeLine(const std::string& line);
|
||||
void ParseNodeLine(const std::string &line);
|
||||
|
||||
void ParseArcLine(const std::string& line);
|
||||
void ParseArcLine(const std::string &line);
|
||||
|
||||
void ParseOneLine(const std::string& line);
|
||||
void ParseOneLine(const std::string &line);
|
||||
|
||||
std::string filename_;
|
||||
|
||||
struct ErrorTrackingState {
|
||||
ErrorTrackingState()
|
||||
: bad(false),
|
||||
nodes_described(false),
|
||||
reason(nullptr),
|
||||
num_left_nodes(0),
|
||||
num_arcs(0) {}
|
||||
: bad(false), nodes_described(false), reason(nullptr),
|
||||
num_left_nodes(0), num_arcs(0) {}
|
||||
|
||||
bool bad;
|
||||
bool nodes_described;
|
||||
const char* reason;
|
||||
const char *reason;
|
||||
NodeIndex num_left_nodes;
|
||||
ArcIndex num_arcs;
|
||||
std::unique_ptr<std::string> bad_line;
|
||||
@@ -92,18 +87,18 @@ class DimacsAssignmentParser {
|
||||
|
||||
ErrorTrackingState state_;
|
||||
|
||||
AnnotatedGraphBuildManager<GraphType>* graph_builder_;
|
||||
AnnotatedGraphBuildManager<GraphType> *graph_builder_;
|
||||
|
||||
LinearSumAssignment<GraphType>* assignment_;
|
||||
LinearSumAssignment<GraphType> *assignment_;
|
||||
};
|
||||
|
||||
// Implementation is below here.
|
||||
template <typename GraphType>
|
||||
void DimacsAssignmentParser<GraphType>::ParseProblemLine(
|
||||
const std::string& line) {
|
||||
static const char* kIncorrectProblemLine =
|
||||
void
|
||||
DimacsAssignmentParser<GraphType>::ParseProblemLine(const std::string &line) {
|
||||
static const char *kIncorrectProblemLine =
|
||||
"Incorrect assignment problem line.";
|
||||
static const char* kAssignmentProblemType = "asn";
|
||||
static const char *kAssignmentProblemType = "asn";
|
||||
char problem_type[4];
|
||||
NodeIndex num_nodes;
|
||||
ArcIndex num_arcs;
|
||||
@@ -120,11 +115,11 @@ void DimacsAssignmentParser<GraphType>::ParseProblemLine(
|
||||
|
||||
state_.num_arcs = num_arcs;
|
||||
graph_builder_ = new AnnotatedGraphBuildManager<GraphType>(
|
||||
num_nodes, num_arcs, FLAGS_assignment_optimize_layout);
|
||||
num_nodes, num_arcs, absl::GetFlag(FLAGS_assignment_optimize_layout));
|
||||
}
|
||||
|
||||
template <typename GraphType>
|
||||
void DimacsAssignmentParser<GraphType>::ParseNodeLine(const std::string& line) {
|
||||
void DimacsAssignmentParser<GraphType>::ParseNodeLine(const std::string &line) {
|
||||
NodeIndex node_id;
|
||||
if (sscanf(line.c_str(), "%*c%d", &node_id) != 1) {
|
||||
state_.bad = true;
|
||||
@@ -142,7 +137,7 @@ void DimacsAssignmentParser<GraphType>::ParseNodeLine(const std::string& line) {
|
||||
}
|
||||
|
||||
template <typename GraphType>
|
||||
void DimacsAssignmentParser<GraphType>::ParseArcLine(const std::string& line) {
|
||||
void DimacsAssignmentParser<GraphType>::ParseArcLine(const std::string &line) {
|
||||
if (graph_builder_ == nullptr) {
|
||||
state_.bad = true;
|
||||
state_.reason =
|
||||
@@ -165,44 +160,45 @@ void DimacsAssignmentParser<GraphType>::ParseArcLine(const std::string& line) {
|
||||
state_.bad_line.reset(new std::string(line));
|
||||
}
|
||||
ArcIndex arc = graph_builder_->AddArc(tail - 1, head - 1);
|
||||
assignment_->SetArcCost(arc, FLAGS_assignment_maximize_cost ? -cost : cost);
|
||||
assignment_->SetArcCost(
|
||||
arc, absl::GetFlag(FLAGS_assignment_maximize_cost) ? -cost : cost);
|
||||
}
|
||||
|
||||
// Parameters out of style-guide order because this function is used
|
||||
// as a callback that varies the input line.
|
||||
template <typename GraphType>
|
||||
void DimacsAssignmentParser<GraphType>::ParseOneLine(const std::string& line) {
|
||||
void DimacsAssignmentParser<GraphType>::ParseOneLine(const std::string &line) {
|
||||
if (state_.bad) {
|
||||
return;
|
||||
}
|
||||
switch (line[0]) {
|
||||
case 'p': {
|
||||
// Problem-specification line
|
||||
ParseProblemLine(line);
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
// Comment; do nothing.
|
||||
return;
|
||||
}
|
||||
case 'n': {
|
||||
// Node line defining a node on the left side
|
||||
ParseNodeLine(line);
|
||||
break;
|
||||
}
|
||||
case 'a': {
|
||||
ParseArcLine(line);
|
||||
break;
|
||||
}
|
||||
case '0':
|
||||
case '\n':
|
||||
break;
|
||||
default: {
|
||||
state_.bad = true;
|
||||
state_.reason = "Unknown line type in the input.";
|
||||
state_.bad_line.reset(new std::string(line));
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
// Problem-specification line
|
||||
ParseProblemLine(line);
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
// Comment; do nothing.
|
||||
return;
|
||||
}
|
||||
case 'n': {
|
||||
// Node line defining a node on the left side
|
||||
ParseNodeLine(line);
|
||||
break;
|
||||
}
|
||||
case 'a': {
|
||||
ParseArcLine(line);
|
||||
break;
|
||||
}
|
||||
case '0':
|
||||
case '\n':
|
||||
break;
|
||||
default: {
|
||||
state_.bad = true;
|
||||
state_.reason = "Unknown line type in the input.";
|
||||
state_.bad_line.reset(new std::string(line));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,12 +216,13 @@ void DimacsAssignmentParser<GraphType>::ParseOneLine(const std::string& line) {
|
||||
// free the underlying graph (which isn't owned by the
|
||||
// LinearAssignment instance).
|
||||
template <typename GraphType>
|
||||
LinearSumAssignment<GraphType>* DimacsAssignmentParser<GraphType>::Parse(
|
||||
std::string* error_message, GraphType** graph_handle) {
|
||||
LinearSumAssignment<GraphType> *
|
||||
DimacsAssignmentParser<GraphType>::Parse(std::string *error_message,
|
||||
GraphType **graph_handle) {
|
||||
CHECK(error_message != nullptr);
|
||||
CHECK(graph_handle != nullptr);
|
||||
|
||||
for (const std::string& line : FileLines(filename_)) {
|
||||
for (const std::string &line : FileLines(filename_)) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -243,7 +240,7 @@ LinearSumAssignment<GraphType>* DimacsAssignmentParser<GraphType>::Parse(
|
||||
}
|
||||
std::unique_ptr<PermutationCycleHandler<ArcIndex> > cycle_handler(
|
||||
assignment_->ArcAnnotationCycleHandler());
|
||||
GraphType* graph = graph_builder_->Graph(cycle_handler.get());
|
||||
GraphType *graph = graph_builder_->Graph(cycle_handler.get());
|
||||
if (graph == nullptr) {
|
||||
*error_message = "unable to create compact static graph";
|
||||
return nullptr;
|
||||
@@ -257,6 +254,6 @@ LinearSumAssignment<GraphType>* DimacsAssignmentParser<GraphType>::Parse(
|
||||
return assignment_;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_EXAMPLES_PARSE_DIMACS_ASSIGNMENT_H_
|
||||
#endif // OR_TOOLS_EXAMPLES_PARSE_DIMACS_ASSIGNMENT_H_
|
||||
|
||||
@@ -76,7 +76,7 @@ typedef std::vector<std::pair<int, int> > Coordinates;
|
||||
|
||||
// Returns the scaled Euclidean distance between two nodes, coords holding the
|
||||
// coordinates of the nodes.
|
||||
int64 Travel(const Coordinates* const coords,
|
||||
int64 Travel(const Coordinates *const coords,
|
||||
RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) {
|
||||
DCHECK(coords != nullptr);
|
||||
@@ -89,7 +89,7 @@ int64 Travel(const Coordinates* const coords,
|
||||
|
||||
// Returns the scaled service time at a given node, service_times holding the
|
||||
// service times.
|
||||
int64 ServiceTime(const std::vector<int64>* const service_times,
|
||||
int64 ServiceTime(const std::vector<int64> *const service_times,
|
||||
RoutingIndexManager::NodeIndex node) {
|
||||
return kScalingFactor * service_times->at(node.value());
|
||||
}
|
||||
@@ -98,9 +98,9 @@ int64 ServiceTime(const std::vector<int64>* const service_times,
|
||||
// holding the coordinates of the nodes and service_times holding the service
|
||||
// times.
|
||||
// The service time is the time spent to execute a delivery or a pickup.
|
||||
int64 TravelPlusServiceTime(const RoutingIndexManager& manager,
|
||||
const Coordinates* const coords,
|
||||
const std::vector<int64>* const service_times,
|
||||
int64 TravelPlusServiceTime(const RoutingIndexManager &manager,
|
||||
const Coordinates *const coords,
|
||||
const std::vector<int64> *const service_times,
|
||||
int64 from_index, int64 to_index) {
|
||||
const RoutingIndexManager::NodeIndex from = manager.IndexToNode(from_index);
|
||||
const RoutingIndexManager::NodeIndex to = manager.IndexToNode(to_index);
|
||||
@@ -113,14 +113,14 @@ int64 TravelPlusServiceTime(const RoutingIndexManager& manager,
|
||||
// - Number of used vehicles,
|
||||
// - Total schedule duration.
|
||||
// TODO(user): add total waiting time.
|
||||
std::vector<IntVar*> GetTabuVars(std::vector<IntVar*> existing_vars,
|
||||
operations_research::RoutingModel* routing) {
|
||||
Solver* const solver = routing->solver();
|
||||
std::vector<IntVar*> vars(std::move(existing_vars));
|
||||
std::vector<IntVar *> GetTabuVars(std::vector<IntVar *> existing_vars,
|
||||
operations_research::RoutingModel *routing) {
|
||||
Solver *const solver = routing->solver();
|
||||
std::vector<IntVar *> vars(std::move(existing_vars));
|
||||
vars.push_back(routing->CostVar());
|
||||
|
||||
IntVar* used_vehicles = solver->MakeIntVar(0, routing->vehicles());
|
||||
std::vector<IntVar*> is_used_vars;
|
||||
IntVar *used_vehicles = solver->MakeIntVar(0, routing->vehicles());
|
||||
std::vector<IntVar *> is_used_vars;
|
||||
// Number of vehicle used
|
||||
is_used_vars.reserve(routing->vehicles());
|
||||
for (int v = 0; v < routing->vehicles(); v++) {
|
||||
@@ -135,14 +135,14 @@ std::vector<IntVar*> GetTabuVars(std::vector<IntVar*> existing_vars,
|
||||
}
|
||||
|
||||
// Outputs a solution to the current model in a std::string.
|
||||
std::string VerboseOutput(const RoutingModel& routing,
|
||||
const RoutingIndexManager& manager,
|
||||
const Assignment& assignment,
|
||||
const Coordinates& coords,
|
||||
const std::vector<int64>& service_times) {
|
||||
std::string VerboseOutput(const RoutingModel &routing,
|
||||
const RoutingIndexManager &manager,
|
||||
const Assignment &assignment,
|
||||
const Coordinates &coords,
|
||||
const std::vector<int64> &service_times) {
|
||||
std::string output;
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time");
|
||||
const RoutingDimension& load_dimension = routing.GetDimensionOrDie("demand");
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie("time");
|
||||
const RoutingDimension &load_dimension = routing.GetDimensionOrDie("demand");
|
||||
for (int i = 0; i < routing.vehicles(); ++i) {
|
||||
absl::StrAppendFormat(&output, "Vehicle %d: ", i);
|
||||
int64 index = routing.Start(i);
|
||||
@@ -152,13 +152,13 @@ std::string VerboseOutput(const RoutingModel& routing,
|
||||
while (!routing.IsEnd(index)) {
|
||||
absl::StrAppendFormat(&output, "%d ",
|
||||
manager.IndexToNode(index).value());
|
||||
const IntVar* vehicle = routing.VehicleVar(index);
|
||||
const IntVar *vehicle = routing.VehicleVar(index);
|
||||
absl::StrAppendFormat(&output, "Vehicle(%d) ",
|
||||
assignment.Value(vehicle));
|
||||
const IntVar* arrival = time_dimension.CumulVar(index);
|
||||
const IntVar *arrival = time_dimension.CumulVar(index);
|
||||
absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival),
|
||||
assignment.Max(arrival));
|
||||
const IntVar* load = load_dimension.CumulVar(index);
|
||||
const IntVar *load = load_dimension.CumulVar(index);
|
||||
absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load),
|
||||
assignment.Max(load));
|
||||
const int64 next_index = assignment.Value(routing.NextVar(index));
|
||||
@@ -169,12 +169,12 @@ std::string VerboseOutput(const RoutingModel& routing,
|
||||
index = next_index;
|
||||
}
|
||||
output.append("Route end ");
|
||||
const IntVar* vehicle = routing.VehicleVar(index);
|
||||
const IntVar *vehicle = routing.VehicleVar(index);
|
||||
absl::StrAppendFormat(&output, "Vehicle(%d) ", assignment.Value(vehicle));
|
||||
const IntVar* arrival = time_dimension.CumulVar(index);
|
||||
const IntVar *arrival = time_dimension.CumulVar(index);
|
||||
absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival),
|
||||
assignment.Max(arrival));
|
||||
const IntVar* load = load_dimension.CumulVar(index);
|
||||
const IntVar *load = load_dimension.CumulVar(index);
|
||||
absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load),
|
||||
assignment.Max(load));
|
||||
}
|
||||
@@ -187,27 +187,28 @@ namespace {
|
||||
// An inefficient but convenient method to parse a whitespace-separated list
|
||||
// of integers. Returns true iff the input std::string was entirely valid and
|
||||
// parsed.
|
||||
bool SafeParseInt64Array(const std::string& str,
|
||||
std::vector<int64>* parsed_int) {
|
||||
bool SafeParseInt64Array(const std::string &str,
|
||||
std::vector<int64> *parsed_int) {
|
||||
std::istringstream input(str);
|
||||
int64 x;
|
||||
parsed_int->clear();
|
||||
while (input >> x) parsed_int->push_back(x);
|
||||
while (input >> x)
|
||||
parsed_int->push_back(x);
|
||||
return input.eof();
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
// Builds and solves a model from a file in the format defined by Li & Lim
|
||||
// (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/).
|
||||
bool LoadAndSolve(const std::string& pdp_file,
|
||||
const RoutingModelParameters& model_parameters,
|
||||
const RoutingSearchParameters& search_parameters) {
|
||||
bool LoadAndSolve(const std::string &pdp_file,
|
||||
const RoutingModelParameters &model_parameters,
|
||||
const RoutingSearchParameters &search_parameters) {
|
||||
// Load all the lines of the file in RAM (it shouldn't be too large anyway).
|
||||
std::vector<std::string> lines;
|
||||
{
|
||||
std::string contents;
|
||||
CHECK_OK(file::GetContents(pdp_file, &contents, file::Defaults()));
|
||||
const int64 kMaxInputFileSize = 1 << 30; // 1GB
|
||||
const int64 kMaxInputFileSize = 1 << 30; // 1GB
|
||||
if (contents.size() >= kMaxInputFileSize) {
|
||||
LOG(WARNING) << "Input file '" << pdp_file << "' is too large (>"
|
||||
<< kMaxInputFileSize << " bytes).";
|
||||
@@ -227,8 +228,9 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
LOG(WARNING) << "Malformed header: " << lines[0];
|
||||
return false;
|
||||
}
|
||||
const int num_vehicles =
|
||||
FLAGS_pdp_force_vehicles > 0 ? FLAGS_pdp_force_vehicles : parsed_int[0];
|
||||
const int num_vehicles = absl::GetFlag(FLAGS_pdp_force_vehicles) > 0
|
||||
? absl::GetFlag(FLAGS_pdp_force_vehicles)
|
||||
: parsed_int[0];
|
||||
const int64 capacity = parsed_int[1];
|
||||
// We do not care about the 'speed' field, in third position.
|
||||
|
||||
@@ -281,26 +283,28 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
RoutingModel routing(manager, model_parameters);
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&coords, &manager](int64 i, int64 j) {
|
||||
return Travel(const_cast<const Coordinates*>(&coords),
|
||||
manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
return Travel(const_cast<const Coordinates *>(&coords),
|
||||
manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
RoutingTransitCallback2 demand_evaluator = [&](int64 from_index,
|
||||
int64 to_index) {
|
||||
RoutingTransitCallback2 demand_evaluator =
|
||||
[&](int64 from_index, int64 to_index) {
|
||||
return demands[manager.IndexToNode(from_index).value()];
|
||||
};
|
||||
}
|
||||
;
|
||||
routing.AddDimension(routing.RegisterTransitCallback(demand_evaluator), 0,
|
||||
capacity, /*fix_start_cumul_to_zero=*/true, "demand");
|
||||
RoutingTransitCallback2 time_evaluator = [&](int64 from_index,
|
||||
int64 to_index) {
|
||||
capacity, /*fix_start_cumul_to_zero=*/ true, "demand");
|
||||
RoutingTransitCallback2 time_evaluator =
|
||||
[&](int64 from_index, int64 to_index) {
|
||||
return TravelPlusServiceTime(manager, &coords, &service_times, from_index,
|
||||
to_index);
|
||||
};
|
||||
}
|
||||
;
|
||||
routing.AddDimension(routing.RegisterTransitCallback(time_evaluator),
|
||||
kScalingFactor * horizon, kScalingFactor * horizon,
|
||||
/*fix_start_cumul_to_zero=*/true, "time");
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time");
|
||||
Solver* const solver = routing.solver();
|
||||
/*fix_start_cumul_to_zero=*/ true, "time");
|
||||
const RoutingDimension &time_dimension = routing.GetDimensionOrDie("time");
|
||||
Solver *const solver = routing.solver();
|
||||
for (int node = 0; node < num_nodes; ++node) {
|
||||
const int64 index =
|
||||
manager.NodeToIndex(RoutingIndexManager::NodeIndex(node));
|
||||
@@ -314,7 +318,7 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
routing.AddPickupAndDelivery(index,
|
||||
manager.NodeToIndex(deliveries[node]));
|
||||
}
|
||||
IntVar* const cumul = time_dimension.CumulVar(index);
|
||||
IntVar *const cumul = time_dimension.CumulVar(index);
|
||||
cumul->SetMin(kScalingFactor * open_times[node]);
|
||||
cumul->SetMax(kScalingFactor * close_times[node]);
|
||||
}
|
||||
@@ -325,13 +329,13 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
// This will be used as one of the Tabu criteria.
|
||||
// This is done here and not in GetTabuVarsCallback as it requires calling
|
||||
// AddVariableMinimizedByFinalizer and this method must be called early.
|
||||
std::vector<IntVar*> end_cumuls;
|
||||
std::vector<IntVar*> start_cumuls;
|
||||
std::vector<IntVar *> end_cumuls;
|
||||
std::vector<IntVar *> start_cumuls;
|
||||
for (int i = 0; i < routing.vehicles(); ++i) {
|
||||
end_cumuls.push_back(time_dimension.CumulVar(routing.End(i)));
|
||||
start_cumuls.push_back(time_dimension.CumulVar(routing.Start(i)));
|
||||
}
|
||||
IntVar* total_time = solver->MakeIntVar(0, 99999999, "total");
|
||||
IntVar *total_time = solver->MakeIntVar(0, 99999999, "total");
|
||||
solver->AddConstraint(solver->MakeEquality(
|
||||
solver->MakeDifference(solver->MakeSum(end_cumuls),
|
||||
solver->MakeSum(start_cumuls)),
|
||||
@@ -340,9 +344,13 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
routing.AddVariableMinimizedByFinalizer(total_time);
|
||||
|
||||
RoutingModel::GetTabuVarsCallback tabu_var_callback =
|
||||
[total_time](RoutingModel* model) {
|
||||
return GetTabuVars({total_time}, model);
|
||||
};
|
||||
[total_time](RoutingModel * model) {
|
||||
return GetTabuVars({
|
||||
total_time
|
||||
},
|
||||
model);
|
||||
}
|
||||
;
|
||||
routing.SetTabuVarsCallback(tabu_var_callback);
|
||||
}
|
||||
|
||||
@@ -357,12 +365,12 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
// Solve pickup and delivery problem.
|
||||
SimpleCycleTimer timer;
|
||||
timer.Start();
|
||||
const Assignment* assignment = routing.SolveWithParameters(search_parameters);
|
||||
const Assignment *assignment = routing.SolveWithParameters(search_parameters);
|
||||
timer.Stop();
|
||||
LOG(INFO) << routing.solver()->LocalSearchProfile();
|
||||
if (nullptr != assignment) {
|
||||
LOG(INFO) << VerboseOutput(routing, manager, *assignment, coords,
|
||||
service_times);
|
||||
LOG(INFO)
|
||||
<< VerboseOutput(routing, manager, *assignment, coords, service_times);
|
||||
LOG(INFO) << "Cost: " << assignment->ObjectiveValue();
|
||||
int skipped_nodes = 0;
|
||||
for (int node = 0; node < routing.Size(); node++) {
|
||||
@@ -385,22 +393,22 @@ bool LoadAndSolve(const std::string& pdp_file,
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
int main(int argc, char **argv) {
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::RoutingModelParameters model_parameters =
|
||||
operations_research::DefaultRoutingModelParameters();
|
||||
model_parameters.set_reduce_vehicle_cost_model(
|
||||
FLAGS_reduce_vehicle_cost_model);
|
||||
absl::GetFlag(FLAGS_reduce_vehicle_cost_model));
|
||||
operations_research::RoutingSearchParameters search_parameters =
|
||||
operations_research::DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, &search_parameters));
|
||||
if (!operations_research::LoadAndSolve(FLAGS_pdp_file, model_parameters,
|
||||
search_parameters)) {
|
||||
LOG(INFO) << "Error solving " << FLAGS_pdp_file;
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), &search_parameters));
|
||||
if (!operations_research::LoadAndSolve(absl::GetFlag(FLAGS_pdp_file),
|
||||
model_parameters, search_parameters)) {
|
||||
LOG(INFO) << "Error solving " << absl::GetFlag(FLAGS_pdp_file);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
template <typename GraphType>
|
||||
class LinearSumAssignment;
|
||||
template <typename GraphType> class LinearSumAssignment;
|
||||
|
||||
// Given a LinearSumAssigment object representing an assignment problem
|
||||
// description, outputs the problem in DIMACS format in the output file.
|
||||
@@ -39,12 +38,12 @@ class LinearSumAssignment;
|
||||
// http://lpsolve.sourceforge.net/5.5/DIMACS_asn.htm
|
||||
template <typename GraphType>
|
||||
void PrintDimacsAssignmentProblem(
|
||||
const LinearSumAssignment<GraphType>& assignment,
|
||||
const TailArrayManager<GraphType>& tail_array_manager,
|
||||
const std::string& output_filename) {
|
||||
File* output;
|
||||
const LinearSumAssignment<GraphType> &assignment,
|
||||
const TailArrayManager<GraphType> &tail_array_manager,
|
||||
const std::string &output_filename) {
|
||||
File *output;
|
||||
CHECK_OK(file::Open(output_filename, "w", &output, file::Defaults()));
|
||||
const GraphType& graph(assignment.Graph());
|
||||
const GraphType &graph(assignment.Graph());
|
||||
std::string output_line =
|
||||
absl::StrFormat("p asn %d %d\n", graph.num_nodes(), graph.num_arcs());
|
||||
CHECK_OK(file::WriteString(output, output_line, file::Defaults()));
|
||||
@@ -67,6 +66,6 @@ void PrintDimacsAssignmentProblem(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_EXAMPLES_PRINT_DIMACS_ASSIGNMENT_H_
|
||||
#endif // OR_TOOLS_EXAMPLES_PRINT_DIMACS_ASSIGNMENT_H_
|
||||
|
||||
@@ -44,11 +44,10 @@ DEFINE_int32(tsp_random_forbidden_connections, 0,
|
||||
"Number of random forbidden connections.");
|
||||
DEFINE_bool(tsp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DEFINE_string(routing_search_parameters,
|
||||
"local_search_operators {"
|
||||
" use_path_lns:BOOL_TRUE"
|
||||
" use_inactive_lns:BOOL_TRUE"
|
||||
"}",
|
||||
DEFINE_string(routing_search_parameters, "local_search_operators {"
|
||||
" use_path_lns:BOOL_TRUE"
|
||||
" use_inactive_lns:BOOL_TRUE"
|
||||
"}",
|
||||
"Text proto RoutingSearchParameters (possibly partial) that will "
|
||||
"override the DefaultRoutingSearchParameters()");
|
||||
|
||||
@@ -56,7 +55,7 @@ namespace operations_research {
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
if (FLAGS_tsp_use_deterministic_random_seed) {
|
||||
if (absl::GetFlag(FLAGS_tsp_use_deterministic_random_seed)) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
@@ -69,12 +68,12 @@ int32 GetSeed() {
|
||||
int64 MyDistance(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) {
|
||||
// Put your distance code here.
|
||||
return (from + to).value(); // for instance
|
||||
return (from + to).value(); // for instance
|
||||
}
|
||||
|
||||
// Random matrix.
|
||||
class RandomMatrix {
|
||||
public:
|
||||
public:
|
||||
explicit RandomMatrix(int size) : size_(size) {}
|
||||
void Initialize() {
|
||||
matrix_ = absl::make_unique<int64[]>(size_ * size_);
|
||||
@@ -95,7 +94,7 @@ class RandomMatrix {
|
||||
return matrix_[MatrixIndex(from, to)];
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int64 MatrixIndex(RoutingIndexManager::NodeIndex from,
|
||||
RoutingIndexManager::NodeIndex to) const {
|
||||
return (from * size_ + to).value();
|
||||
@@ -105,44 +104,46 @@ class RandomMatrix {
|
||||
};
|
||||
|
||||
void Tsp() {
|
||||
if (FLAGS_tsp_size > 0) {
|
||||
// TSP of size FLAGS_tsp_size.
|
||||
if (absl::GetFlag(FLAGS_tsp_size) > 0) {
|
||||
// TSP of size absl::GetFlag(FLAGS_tsp_size).
|
||||
// Second argument = 1 to build a single tour (it's a TSP).
|
||||
// Nodes are indexed from 0 to FLAGS_tsp_size - 1, by default the start of
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_tsp_size) - 1, by default
|
||||
// the start of
|
||||
// the route is node 0.
|
||||
RoutingIndexManager manager(FLAGS_tsp_size, 1,
|
||||
RoutingIndexManager manager(absl::GetFlag(FLAGS_tsp_size), 1,
|
||||
RoutingIndexManager::NodeIndex(0));
|
||||
RoutingModel routing(manager);
|
||||
RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
FLAGS_routing_search_parameters, ¶meters));
|
||||
absl::GetFlag(FLAGS_routing_search_parameters), ¶meters));
|
||||
|
||||
// Setting the cost function.
|
||||
// Put a permanent callback to the distance accessor here. The callback
|
||||
// has the following signature: ResultCallback2<int64, int64, int64>.
|
||||
// The two arguments are the from and to node inidices.
|
||||
RandomMatrix matrix(FLAGS_tsp_size);
|
||||
if (FLAGS_tsp_use_random_matrix) {
|
||||
RandomMatrix matrix(absl::GetFlag(FLAGS_tsp_size));
|
||||
if (absl::GetFlag(FLAGS_tsp_use_random_matrix)) {
|
||||
matrix.Initialize();
|
||||
const int vehicle_cost = routing.RegisterTransitCallback(
|
||||
[&matrix, &manager](int64 i, int64 j) {
|
||||
return matrix.Distance(manager.IndexToNode(i),
|
||||
manager.IndexToNode(j));
|
||||
});
|
||||
return matrix.Distance(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
} else {
|
||||
const int vehicle_cost =
|
||||
routing.RegisterTransitCallback([&manager](int64 i, int64 j) {
|
||||
return MyDistance(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
return MyDistance(manager.IndexToNode(i), manager.IndexToNode(j));
|
||||
});
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
|
||||
}
|
||||
// Forbid node connections (randomly).
|
||||
ACMRandom randomizer(GetSeed());
|
||||
int64 forbidden_connections = 0;
|
||||
while (forbidden_connections < FLAGS_tsp_random_forbidden_connections) {
|
||||
const int64 from = randomizer.Uniform(FLAGS_tsp_size - 1);
|
||||
const int64 to = randomizer.Uniform(FLAGS_tsp_size - 1) + 1;
|
||||
while (forbidden_connections <
|
||||
absl::GetFlag(FLAGS_tsp_random_forbidden_connections)) {
|
||||
const int64 from = randomizer.Uniform(absl::GetFlag(FLAGS_tsp_size) - 1);
|
||||
const int64 to =
|
||||
randomizer.Uniform(absl::GetFlag(FLAGS_tsp_size) - 1) + 1;
|
||||
if (routing.NextVar(from)->Contains(to)) {
|
||||
LOG(INFO) << "Forbidding connection " << from << " -> " << to;
|
||||
routing.NextVar(from)->RemoveValue(to);
|
||||
@@ -150,7 +151,7 @@ void Tsp() {
|
||||
}
|
||||
}
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
const Assignment *solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != nullptr) {
|
||||
// Solution cost.
|
||||
LOG(INFO) << "Cost " << solution->ObjectiveValue();
|
||||
@@ -173,9 +174,9 @@ void Tsp() {
|
||||
LOG(INFO) << "Specify an instance size greater than 0.";
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
operations_research::Tsp();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
struct LinearBooleanProblemWrapper {
|
||||
explicit LinearBooleanProblemWrapper(LinearBooleanProblem* p) : problem(p) {}
|
||||
explicit LinearBooleanProblemWrapper(LinearBooleanProblem *p) : problem(p) {}
|
||||
|
||||
void SetNumVariables(int num) { problem->set_num_variables(num); }
|
||||
void SetOriginalNumVariables(int num) {
|
||||
@@ -48,7 +48,7 @@ struct LinearBooleanProblemWrapper {
|
||||
}
|
||||
|
||||
void AddConstraint(absl::Span<const int> clause) {
|
||||
LinearBooleanConstraint* constraint = problem->add_constraints();
|
||||
LinearBooleanConstraint *constraint = problem->add_constraints();
|
||||
constraint->mutable_literals()->Reserve(clause.size());
|
||||
constraint->mutable_coefficients()->Reserve(clause.size());
|
||||
constraint->set_lower_bound(1);
|
||||
@@ -68,15 +68,15 @@ struct LinearBooleanProblemWrapper {
|
||||
problem->mutable_objective()->set_offset(offset);
|
||||
}
|
||||
|
||||
LinearBooleanProblem* problem;
|
||||
LinearBooleanProblem *problem;
|
||||
};
|
||||
|
||||
struct CpModelProtoWrapper {
|
||||
explicit CpModelProtoWrapper(CpModelProto* p) : problem(p) {}
|
||||
explicit CpModelProtoWrapper(CpModelProto *p) : problem(p) {}
|
||||
|
||||
void SetNumVariables(int num) {
|
||||
for (int i = 0; i < num; ++i) {
|
||||
IntegerVariableProto* variable = problem->add_variables();
|
||||
IntegerVariableProto *variable = problem->add_variables();
|
||||
variable->add_domain(0);
|
||||
variable->add_domain(1);
|
||||
}
|
||||
@@ -93,7 +93,7 @@ struct CpModelProtoWrapper {
|
||||
}
|
||||
|
||||
void AddConstraint(absl::Span<const int> clause) {
|
||||
auto* constraint = problem->add_constraints()->mutable_bool_or();
|
||||
auto *constraint = problem->add_constraints()->mutable_bool_or();
|
||||
constraint->mutable_literals()->Reserve(clause.size());
|
||||
for (const int literal : clause) {
|
||||
constraint->add_literals(LiteralToRef(literal));
|
||||
@@ -110,7 +110,7 @@ struct CpModelProtoWrapper {
|
||||
problem->mutable_objective()->set_offset(offset);
|
||||
}
|
||||
|
||||
CpModelProto* problem;
|
||||
CpModelProto *problem;
|
||||
};
|
||||
|
||||
// This class loads a file in cnf file format into a SatProblem.
|
||||
@@ -119,7 +119,7 @@ struct CpModelProtoWrapper {
|
||||
//
|
||||
// It also support the wcnf input format for partial weighted max-sat problems.
|
||||
class SatCnfReader {
|
||||
public:
|
||||
public:
|
||||
SatCnfReader() : interpret_cnf_as_max_sat_(false) {}
|
||||
|
||||
// If called with true, then a cnf file will be converted to the max-sat
|
||||
@@ -127,22 +127,22 @@ class SatCnfReader {
|
||||
void InterpretCnfAsMaxSat(bool v) { interpret_cnf_as_max_sat_ = v; }
|
||||
|
||||
// Loads the given cnf filename into the given proto.
|
||||
bool Load(const std::string& filename, LinearBooleanProblem* problem) {
|
||||
bool Load(const std::string &filename, LinearBooleanProblem *problem) {
|
||||
problem->Clear();
|
||||
problem->set_name(ExtractProblemName(filename));
|
||||
LinearBooleanProblemWrapper wrapper(problem);
|
||||
return LoadInternal(filename, &wrapper);
|
||||
}
|
||||
bool Load(const std::string& filename, CpModelProto* problem) {
|
||||
bool Load(const std::string &filename, CpModelProto *problem) {
|
||||
problem->Clear();
|
||||
problem->set_name(ExtractProblemName(filename));
|
||||
CpModelProtoWrapper wrapper(problem);
|
||||
return LoadInternal(filename, &wrapper);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
template <class Problem>
|
||||
bool LoadInternal(const std::string& filename, Problem* problem) {
|
||||
bool LoadInternal(const std::string &filename, Problem *problem) {
|
||||
positive_literal_to_weight_.clear();
|
||||
objective_offset_ = 0;
|
||||
is_wcnf_ = false;
|
||||
@@ -154,7 +154,7 @@ class SatCnfReader {
|
||||
num_slack_variables_ = 0;
|
||||
|
||||
int num_lines = 0;
|
||||
for (const std::string& line : FileLines(filename)) {
|
||||
for (const std::string &line : FileLines(filename)) {
|
||||
++num_lines;
|
||||
ProcessNewLine(line, problem);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ class SatCnfReader {
|
||||
|
||||
// Since the problem name is not stored in the cnf format, we infer it from
|
||||
// the file name.
|
||||
static std::string ExtractProblemName(const std::string& filename) {
|
||||
static std::string ExtractProblemName(const std::string &filename) {
|
||||
const int found = filename.find_last_of("/");
|
||||
const std::string problem_name =
|
||||
found != std::string::npos ? filename.substr(found + 1) : filename;
|
||||
@@ -201,7 +201,7 @@ class SatCnfReader {
|
||||
return value;
|
||||
}
|
||||
|
||||
void ProcessHeader(const std::string& line) {
|
||||
void ProcessHeader(const std::string &line) {
|
||||
static const char kWordDelimiters[] = " ";
|
||||
words_ = absl::StrSplit(line, kWordDelimiters, absl::SkipEmpty());
|
||||
|
||||
@@ -220,9 +220,11 @@ class SatCnfReader {
|
||||
}
|
||||
|
||||
template <class Problem>
|
||||
void ProcessNewLine(const std::string& line, Problem* problem) {
|
||||
if (line.empty() || end_marker_seen_) return;
|
||||
if (line[0] == 'c') return;
|
||||
void ProcessNewLine(const std::string &line, Problem *problem) {
|
||||
if (line.empty() || end_marker_seen_)
|
||||
return;
|
||||
if (line[0] == 'c')
|
||||
return;
|
||||
if (line[0] == '%') {
|
||||
end_marker_seen_ = true;
|
||||
return;
|
||||
@@ -251,13 +253,14 @@ class SatCnfReader {
|
||||
} else {
|
||||
if (signed_value == 0) {
|
||||
end_marker_seen = true;
|
||||
break; // end of clause.
|
||||
break; // end of clause.
|
||||
}
|
||||
tmp_clause_.push_back(signed_value);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
if (!end_marker_seen) return;
|
||||
if (!end_marker_seen)
|
||||
return;
|
||||
|
||||
if (weight == hard_weight_) {
|
||||
++num_added_clauses_;
|
||||
@@ -294,11 +297,13 @@ class SatCnfReader {
|
||||
objective_offset_ += weight;
|
||||
}
|
||||
|
||||
if (FLAGS_wcnf_use_strong_slack) {
|
||||
if (absl::GetFlag(FLAGS_wcnf_use_strong_slack)) {
|
||||
// Add the binary implications slack_literal true => all the other
|
||||
// clause literals are false.
|
||||
for (int i = 0; i + 1 < tmp_clause_.size(); ++i) {
|
||||
problem->AddConstraint({-slack_literal, -tmp_clause_[i]});
|
||||
problem->AddConstraint({
|
||||
-slack_literal, -tmp_clause_[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +339,7 @@ class SatCnfReader {
|
||||
DISALLOW_COPY_AND_ASSIGN(SatCnfReader);
|
||||
};
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_SAT_SAT_CNF_READER_H_
|
||||
#endif // OR_TOOLS_SAT_SAT_CNF_READER_H_
|
||||
|
||||
@@ -129,17 +129,18 @@ namespace {
|
||||
|
||||
// Returns a trivial best bound. The best bound corresponds to the lower bound
|
||||
// (resp. upper bound) in case of a minimization (resp. maximization) problem.
|
||||
double GetScaledTrivialBestBound(const LinearBooleanProblem& problem) {
|
||||
double GetScaledTrivialBestBound(const LinearBooleanProblem &problem) {
|
||||
Coefficient best_bound(0);
|
||||
const LinearObjective& objective = problem.objective();
|
||||
const LinearObjective &objective = problem.objective();
|
||||
for (const int64 value : objective.coefficients()) {
|
||||
if (value < 0) best_bound += Coefficient(value);
|
||||
if (value < 0)
|
||||
best_bound += Coefficient(value);
|
||||
}
|
||||
return AddOffsetAndScaleObjectiveValue(problem, best_bound);
|
||||
}
|
||||
|
||||
bool LoadBooleanProblem(const std::string& filename,
|
||||
LinearBooleanProblem* problem, CpModelProto* cp_model) {
|
||||
bool LoadBooleanProblem(const std::string &filename,
|
||||
LinearBooleanProblem *problem, CpModelProto *cp_model) {
|
||||
if (absl::EndsWith(filename, ".opb") ||
|
||||
absl::EndsWith(filename, ".opb.bz2")) {
|
||||
OpbReader reader;
|
||||
@@ -151,11 +152,12 @@ bool LoadBooleanProblem(const std::string& filename,
|
||||
absl::EndsWith(filename, ".wcnf") ||
|
||||
absl::EndsWith(filename, ".wcnf.gz")) {
|
||||
SatCnfReader reader;
|
||||
if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_qmaxsat ||
|
||||
FLAGS_core_enc) {
|
||||
if (absl::GetFlag(FLAGS_fu_malik) || absl::GetFlag(FLAGS_linear_scan) ||
|
||||
absl::GetFlag(FLAGS_wpm1) || absl::GetFlag(FLAGS_qmaxsat) ||
|
||||
absl::GetFlag(FLAGS_core_enc)) {
|
||||
reader.InterpretCnfAsMaxSat(true);
|
||||
}
|
||||
if (FLAGS_use_cp_model) {
|
||||
if (absl::GetFlag(FLAGS_use_cp_model)) {
|
||||
if (!reader.Load(filename, cp_model)) {
|
||||
LOG(FATAL) << "Cannot load file '" << filename << "'.";
|
||||
}
|
||||
@@ -164,7 +166,7 @@ bool LoadBooleanProblem(const std::string& filename,
|
||||
LOG(FATAL) << "Cannot load file '" << filename << "'.";
|
||||
}
|
||||
}
|
||||
} else if (FLAGS_use_cp_model) {
|
||||
} else if (absl::GetFlag(FLAGS_use_cp_model)) {
|
||||
LOG(INFO) << "Reading a CpModelProto.";
|
||||
*cp_model = ReadFileToProtoOrDie<CpModelProto>(filename);
|
||||
} else {
|
||||
@@ -174,12 +176,13 @@ bool LoadBooleanProblem(const std::string& filename,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SolutionString(const LinearBooleanProblem& problem,
|
||||
const std::vector<bool>& assignment) {
|
||||
std::string SolutionString(const LinearBooleanProblem &problem,
|
||||
const std::vector<bool> &assignment) {
|
||||
std::string output;
|
||||
BooleanVariable limit(problem.original_num_variables());
|
||||
for (BooleanVariable index(0); index < limit; ++index) {
|
||||
if (index > 0) output += " ";
|
||||
if (index > 0)
|
||||
output += " ";
|
||||
absl::StrAppend(&output,
|
||||
Literal(index, assignment[index.value()]).SignedValue());
|
||||
}
|
||||
@@ -190,15 +193,15 @@ std::string SolutionString(const LinearBooleanProblem& problem,
|
||||
// here.
|
||||
int Run() {
|
||||
SatParameters parameters;
|
||||
if (FLAGS_input.empty()) {
|
||||
if (absl::GetFlag(FLAGS_input).empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
}
|
||||
|
||||
// Parse the --params flag.
|
||||
if (!FLAGS_params.empty()) {
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(FLAGS_params,
|
||||
¶meters))
|
||||
<< FLAGS_params;
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
CHECK(google::protobuf::TextFormat::MergeFromString(
|
||||
absl::GetFlag(FLAGS_params), ¶meters))
|
||||
<< absl::GetFlag(FLAGS_params);
|
||||
}
|
||||
|
||||
// Initialize the solver.
|
||||
@@ -208,12 +211,12 @@ int Run() {
|
||||
// Read the problem.
|
||||
LinearBooleanProblem problem;
|
||||
CpModelProto cp_model;
|
||||
if (!LoadBooleanProblem(FLAGS_input, &problem, &cp_model)) {
|
||||
if (!LoadBooleanProblem(absl::GetFlag(FLAGS_input), &problem, &cp_model)) {
|
||||
CpSolverResponse response;
|
||||
response.set_status(CpSolverStatus::MODEL_INVALID);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (FLAGS_use_cp_model && cp_model.variables_size() == 0) {
|
||||
if (absl::GetFlag(FLAGS_use_cp_model) && cp_model.variables_size() == 0) {
|
||||
LOG(INFO) << "Converting to CpModelProto ...";
|
||||
cp_model = BooleanProblemToCpModelproto(problem);
|
||||
}
|
||||
@@ -221,28 +224,31 @@ int Run() {
|
||||
// TODO(user): clean this hack. Ideally LinearBooleanProblem should be
|
||||
// completely replaced by the more general CpModelProto.
|
||||
if (!cp_model.variables().empty()) {
|
||||
problem.Clear(); // We no longer need it, release memory.
|
||||
problem.Clear(); // We no longer need it, release memory.
|
||||
Model model;
|
||||
model.Add(NewSatParameters(parameters));
|
||||
const CpSolverResponse response = SolveCpModel(cp_model, &model);
|
||||
|
||||
if (!FLAGS_output.empty()) {
|
||||
if (absl::EndsWith(FLAGS_output, ".txt")) {
|
||||
CHECK_OK(file::SetTextProto(FLAGS_output, response, file::Defaults()));
|
||||
if (!absl::GetFlag(FLAGS_output).empty()) {
|
||||
if (absl::EndsWith(absl::GetFlag(FLAGS_output), ".txt")) {
|
||||
CHECK_OK(file::SetTextProto(absl::GetFlag(FLAGS_output), response,
|
||||
file::Defaults()));
|
||||
} else {
|
||||
CHECK_OK(
|
||||
file::SetBinaryProto(FLAGS_output, response, file::Defaults()));
|
||||
CHECK_OK(file::SetBinaryProto(absl::GetFlag(FLAGS_output), response,
|
||||
file::Defaults()));
|
||||
}
|
||||
}
|
||||
|
||||
// The SAT competition requires a particular exit code and since we don't
|
||||
// really use it for any other purpose, we comply.
|
||||
if (response.status() == CpSolverStatus::FEASIBLE) return 10;
|
||||
if (response.status() == CpSolverStatus::INFEASIBLE) return 20;
|
||||
if (response.status() == CpSolverStatus::FEASIBLE)
|
||||
return 10;
|
||||
if (response.status() == CpSolverStatus::INFEASIBLE)
|
||||
return 20;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (FLAGS_strict_validity) {
|
||||
if (absl::GetFlag(FLAGS_strict_validity)) {
|
||||
const absl::Status status = ValidateBooleanProblem(problem);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Invalid Boolean problem: " << status.message();
|
||||
@@ -260,14 +266,14 @@ int Run() {
|
||||
// Probing.
|
||||
SatPostsolver probing_postsolver(problem.num_variables());
|
||||
LinearBooleanProblem original_problem;
|
||||
if (FLAGS_probing) {
|
||||
if (absl::GetFlag(FLAGS_probing)) {
|
||||
// TODO(user): This is nice for testing, but consumes memory.
|
||||
original_problem = problem;
|
||||
ProbeAndSimplifyProblem(&probing_postsolver, &problem);
|
||||
}
|
||||
|
||||
// Load the problem into the solver.
|
||||
if (FLAGS_reduce_memory_usage) {
|
||||
if (absl::GetFlag(FLAGS_reduce_memory_usage)) {
|
||||
if (!LoadAndConsumeBooleanProblem(&problem, solver.get())) {
|
||||
LOG(INFO) << "UNSAT when loading the problem.";
|
||||
}
|
||||
@@ -276,16 +282,19 @@ int Run() {
|
||||
LOG(INFO) << "UNSAT when loading the problem.";
|
||||
}
|
||||
}
|
||||
auto strtoint64 = [](const std::string& word) {
|
||||
auto strtoint64 = [](const std::string & word) {
|
||||
int64 value = 0;
|
||||
if (!word.empty()) CHECK(absl::SimpleAtoi(word, &value));
|
||||
if (!word.empty())
|
||||
CHECK(absl::SimpleAtoi(word, &value));
|
||||
return value;
|
||||
};
|
||||
if (!AddObjectiveConstraint(problem, !FLAGS_lower_bound.empty(),
|
||||
Coefficient(strtoint64(FLAGS_lower_bound)),
|
||||
!FLAGS_upper_bound.empty(),
|
||||
Coefficient(strtoint64(FLAGS_upper_bound)),
|
||||
solver.get())) {
|
||||
}
|
||||
;
|
||||
if (!AddObjectiveConstraint(
|
||||
problem, !absl::GetFlag(FLAGS_lower_bound).empty(),
|
||||
Coefficient(strtoint64(absl::GetFlag(FLAGS_lower_bound))),
|
||||
!absl::GetFlag(FLAGS_upper_bound).empty(),
|
||||
Coefficient(strtoint64(absl::GetFlag(FLAGS_upper_bound))),
|
||||
solver.get())) {
|
||||
LOG(INFO) << "UNSAT when setting the objective constraint.";
|
||||
}
|
||||
|
||||
@@ -293,11 +302,11 @@ int Run() {
|
||||
//
|
||||
// TODO(user): To make this compatible with presolve, we just need to run
|
||||
// it after the presolve step.
|
||||
if (FLAGS_use_symmetry) {
|
||||
CHECK(!FLAGS_reduce_memory_usage) << "incompatible";
|
||||
CHECK(!FLAGS_presolve) << "incompatible";
|
||||
if (absl::GetFlag(FLAGS_use_symmetry)) {
|
||||
CHECK(!absl::GetFlag(FLAGS_reduce_memory_usage)) << "incompatible";
|
||||
CHECK(!absl::GetFlag(FLAGS_presolve)) << "incompatible";
|
||||
LOG(INFO) << "Finding symmetries of the problem.";
|
||||
std::vector<std::unique_ptr<SparsePermutation>> generators;
|
||||
std::vector<std::unique_ptr<SparsePermutation> > generators;
|
||||
FindLinearBooleanProblemSymmetries(problem, &generators);
|
||||
std::unique_ptr<SymmetryPropagator> propagator(new SymmetryPropagator);
|
||||
for (int i = 0; i < generators.size(); ++i) {
|
||||
@@ -310,28 +319,31 @@ int Run() {
|
||||
// Optimize?
|
||||
std::vector<bool> solution;
|
||||
SatSolver::Status result = SatSolver::LIMIT_REACHED;
|
||||
if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_qmaxsat ||
|
||||
FLAGS_core_enc) {
|
||||
if (FLAGS_randomize > 0 && (FLAGS_linear_scan || FLAGS_qmaxsat)) {
|
||||
CHECK(!FLAGS_reduce_memory_usage) << "incompatible";
|
||||
result = SolveWithRandomParameters(STDOUT_LOG, problem, FLAGS_randomize,
|
||||
solver.get(), &solution);
|
||||
if (absl::GetFlag(FLAGS_fu_malik) || absl::GetFlag(FLAGS_linear_scan) ||
|
||||
absl::GetFlag(FLAGS_wpm1) || absl::GetFlag(FLAGS_qmaxsat) ||
|
||||
absl::GetFlag(FLAGS_core_enc)) {
|
||||
if (absl::GetFlag(FLAGS_randomize) > 0 &&
|
||||
(absl::GetFlag(FLAGS_linear_scan) || absl::GetFlag(FLAGS_qmaxsat))) {
|
||||
CHECK(!absl::GetFlag(FLAGS_reduce_memory_usage)) << "incompatible";
|
||||
result = SolveWithRandomParameters(
|
||||
STDOUT_LOG, problem, absl::GetFlag(FLAGS_randomize), solver.get(),
|
||||
&solution);
|
||||
}
|
||||
if (result == SatSolver::LIMIT_REACHED) {
|
||||
if (FLAGS_qmaxsat) {
|
||||
if (absl::GetFlag(FLAGS_qmaxsat)) {
|
||||
solver = absl::make_unique<SatSolver>();
|
||||
solver->SetParameters(parameters);
|
||||
CHECK(LoadBooleanProblem(problem, solver.get()));
|
||||
result = SolveWithCardinalityEncoding(STDOUT_LOG, problem, solver.get(),
|
||||
&solution);
|
||||
} else if (FLAGS_core_enc) {
|
||||
} else if (absl::GetFlag(FLAGS_core_enc)) {
|
||||
result = SolveWithCardinalityEncodingAndCore(STDOUT_LOG, problem,
|
||||
solver.get(), &solution);
|
||||
} else if (FLAGS_fu_malik) {
|
||||
} else if (absl::GetFlag(FLAGS_fu_malik)) {
|
||||
result = SolveWithFuMalik(STDOUT_LOG, problem, solver.get(), &solution);
|
||||
} else if (FLAGS_wpm1) {
|
||||
} else if (absl::GetFlag(FLAGS_wpm1)) {
|
||||
result = SolveWithWPM1(STDOUT_LOG, problem, solver.get(), &solution);
|
||||
} else if (FLAGS_linear_scan) {
|
||||
} else if (absl::GetFlag(FLAGS_linear_scan)) {
|
||||
result =
|
||||
SolveWithLinearScan(STDOUT_LOG, problem, solver.get(), &solution);
|
||||
}
|
||||
@@ -340,7 +352,7 @@ int Run() {
|
||||
// Only solve the decision version.
|
||||
parameters.set_log_search_progress(true);
|
||||
solver->SetParameters(parameters);
|
||||
if (FLAGS_presolve) {
|
||||
if (absl::GetFlag(FLAGS_presolve)) {
|
||||
std::unique_ptr<TimeLimit> time_limit =
|
||||
TimeLimit::FromParameters(parameters);
|
||||
result = SolveWithPresolve(&solver, time_limit.get(), &solution, nullptr);
|
||||
@@ -358,14 +370,15 @@ int Run() {
|
||||
|
||||
// Print the solution status.
|
||||
if (result == SatSolver::FEASIBLE) {
|
||||
if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_core_enc) {
|
||||
if (absl::GetFlag(FLAGS_fu_malik) || absl::GetFlag(FLAGS_linear_scan) ||
|
||||
absl::GetFlag(FLAGS_wpm1) || absl::GetFlag(FLAGS_core_enc)) {
|
||||
printf("s OPTIMUM FOUND\n");
|
||||
CHECK(!solution.empty());
|
||||
const Coefficient objective = ComputeObjectiveValue(problem, solution);
|
||||
scaled_best_bound = AddOffsetAndScaleObjectiveValue(problem, objective);
|
||||
|
||||
// Postsolve.
|
||||
if (FLAGS_probing) {
|
||||
if (absl::GetFlag(FLAGS_probing)) {
|
||||
solution = probing_postsolver.PostsolveSolution(solution);
|
||||
problem = original_problem;
|
||||
}
|
||||
@@ -375,18 +388,20 @@ int Run() {
|
||||
|
||||
// Check and output the solution.
|
||||
CHECK(IsAssignmentValid(problem, solution));
|
||||
if (FLAGS_output_cnf_solution) {
|
||||
if (absl::GetFlag(FLAGS_output_cnf_solution)) {
|
||||
printf("v %s\n", SolutionString(problem, solution).c_str());
|
||||
}
|
||||
if (!FLAGS_output.empty()) {
|
||||
CHECK(!FLAGS_reduce_memory_usage) << "incompatible";
|
||||
if (!absl::GetFlag(FLAGS_output).empty()) {
|
||||
CHECK(!absl::GetFlag(FLAGS_reduce_memory_usage)) << "incompatible";
|
||||
if (result == SatSolver::FEASIBLE) {
|
||||
StoreAssignment(solver->Assignment(), problem.mutable_assignment());
|
||||
}
|
||||
if (absl::EndsWith(FLAGS_output, ".txt")) {
|
||||
CHECK_OK(file::SetTextProto(FLAGS_output, problem, file::Defaults()));
|
||||
if (absl::EndsWith(absl::GetFlag(FLAGS_output), ".txt")) {
|
||||
CHECK_OK(file::SetTextProto(absl::GetFlag(FLAGS_output), problem,
|
||||
file::Defaults()));
|
||||
} else {
|
||||
CHECK_OK(file::SetBinaryProto(FLAGS_output, problem, file::Defaults()));
|
||||
CHECK_OK(file::SetBinaryProto(absl::GetFlag(FLAGS_output), problem,
|
||||
file::Defaults()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,24 +434,26 @@ int Run() {
|
||||
|
||||
// The SAT competition requires a particular exit code and since we don't
|
||||
// really use it for any other purpose, we comply.
|
||||
if (result == SatSolver::FEASIBLE) return 10;
|
||||
if (result == SatSolver::INFEASIBLE) return 20;
|
||||
if (result == SatSolver::FEASIBLE)
|
||||
return 10;
|
||||
if (result == SatSolver::INFEASIBLE)
|
||||
return 20;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
static const char kUsage[] =
|
||||
"Usage: see flags.\n"
|
||||
"This program solves a given Boolean linear problem.";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
// By default, we want to show how the solver progress. Note that this needs
|
||||
// to be set before InitGoogle() which has the nice side-effect of allowing
|
||||
// the user to override it.
|
||||
// absl::SetFlag(&FLAGS_vmodule, "*cp_model*=1");
|
||||
// absl::SetFlag(&absl::GetFlag(FLAGS_vmodule), "*cp_model*=1");
|
||||
gflags::SetUsageMessage(kUsage);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
@@ -45,7 +45,7 @@ DEFINE_string(params, "", "Sat parameters in text proto format.");
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
class ShiftMinimizationParser {
|
||||
public:
|
||||
public:
|
||||
struct Job {
|
||||
int start;
|
||||
int end;
|
||||
@@ -57,17 +57,15 @@ class ShiftMinimizationParser {
|
||||
};
|
||||
|
||||
ShiftMinimizationParser()
|
||||
: load_status_(NOT_STARTED),
|
||||
declared_num_jobs_(0),
|
||||
declared_num_workers_(0),
|
||||
num_workers_read_(0) {}
|
||||
: load_status_(NOT_STARTED), declared_num_jobs_(0),
|
||||
declared_num_workers_(0), num_workers_read_(0) {}
|
||||
|
||||
const std::vector<Job>& jobs() const { return jobs_; }
|
||||
const std::vector<std::vector<int>>& possible_jobs_per_worker() const {
|
||||
const std::vector<Job> &jobs() const { return jobs_; }
|
||||
const std::vector<std::vector<int> > &possible_jobs_per_worker() const {
|
||||
return possible_jobs_per_worker_;
|
||||
}
|
||||
const std::vector<std::vector<Assignment>>& possible_assignments_per_job()
|
||||
const {
|
||||
const std::vector<std::vector<Assignment> > &
|
||||
possible_assignments_per_job() const {
|
||||
return possible_assignments_per_job_;
|
||||
}
|
||||
|
||||
@@ -78,14 +76,14 @@ class ShiftMinimizationParser {
|
||||
// <start> <end> // Repeated n times
|
||||
// Qualifications = <k>
|
||||
// c: job_1 .. job_c // repeated k times (a counter and job ids after).
|
||||
bool LoadFile(const std::string& file_name) {
|
||||
bool LoadFile(const std::string &file_name) {
|
||||
if (load_status_ != NOT_STARTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
load_status_ = STARTED;
|
||||
|
||||
for (const std::string& line :
|
||||
for (const std::string &line :
|
||||
FileLines(file_name, FileLineIterator::REMOVE_LINEFEED |
|
||||
FileLineIterator::REMOVE_INLINE_CR)) {
|
||||
ProcessLine(line);
|
||||
@@ -98,16 +96,21 @@ class ShiftMinimizationParser {
|
||||
declared_num_workers_ == num_workers_read_;
|
||||
}
|
||||
|
||||
private:
|
||||
enum LoadStatus { NOT_STARTED, STARTED, JOBS_SEEN, WORKERS_SEEN };
|
||||
private:
|
||||
enum LoadStatus {
|
||||
NOT_STARTED,
|
||||
STARTED,
|
||||
JOBS_SEEN,
|
||||
WORKERS_SEEN
|
||||
};
|
||||
|
||||
int strtoint32(const std::string& word) {
|
||||
int strtoint32(const std::string &word) {
|
||||
int result;
|
||||
CHECK(absl::SimpleAtoi(word, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessLine(const std::string& line) {
|
||||
void ProcessLine(const std::string &line) {
|
||||
if (line.empty() || line[0] == '#') {
|
||||
return;
|
||||
}
|
||||
@@ -116,78 +119,81 @@ class ShiftMinimizationParser {
|
||||
absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
|
||||
|
||||
switch (load_status_) {
|
||||
case NOT_STARTED: {
|
||||
LOG(FATAL) << "Wrong status: NOT_STARTED";
|
||||
break;
|
||||
case NOT_STARTED: {
|
||||
LOG(FATAL) << "Wrong status: NOT_STARTED";
|
||||
break;
|
||||
}
|
||||
case STARTED: {
|
||||
if (words.size() == 3 && words[0] == "Type") {
|
||||
CHECK_EQ(1, strtoint32(words[2]));
|
||||
} else if (words.size() == 3 && words[0] == "Jobs") {
|
||||
declared_num_jobs_ = strtoint32(words[2]);
|
||||
possible_assignments_per_job_.resize(declared_num_jobs_);
|
||||
load_status_ = JOBS_SEEN;
|
||||
} else {
|
||||
LOG(FATAL) << "Wrong state STARTED with line " << line;
|
||||
}
|
||||
case STARTED: {
|
||||
if (words.size() == 3 && words[0] == "Type") {
|
||||
CHECK_EQ(1, strtoint32(words[2]));
|
||||
} else if (words.size() == 3 && words[0] == "Jobs") {
|
||||
declared_num_jobs_ = strtoint32(words[2]);
|
||||
possible_assignments_per_job_.resize(declared_num_jobs_);
|
||||
load_status_ = JOBS_SEEN;
|
||||
} else {
|
||||
LOG(FATAL) << "Wrong state STARTED with line " << line;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case JOBS_SEEN: {
|
||||
if (words.size() == 2) {
|
||||
jobs_.push_back({
|
||||
strtoint32(words[0]), strtoint32(words[1])
|
||||
});
|
||||
} else if (words.size() == 3 && words[0] == "Qualifications") {
|
||||
declared_num_workers_ = strtoint32(words[2]);
|
||||
possible_jobs_per_worker_.resize(declared_num_workers_);
|
||||
load_status_ = WORKERS_SEEN;
|
||||
} else {
|
||||
LOG(FATAL) << "Wrong state JOBS_SEEN with line " << line;
|
||||
}
|
||||
case JOBS_SEEN: {
|
||||
if (words.size() == 2) {
|
||||
jobs_.push_back({strtoint32(words[0]), strtoint32(words[1])});
|
||||
} else if (words.size() == 3 && words[0] == "Qualifications") {
|
||||
declared_num_workers_ = strtoint32(words[2]);
|
||||
possible_jobs_per_worker_.resize(declared_num_workers_);
|
||||
load_status_ = WORKERS_SEEN;
|
||||
} else {
|
||||
LOG(FATAL) << "Wrong state JOBS_SEEN with line " << line;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WORKERS_SEEN: {
|
||||
CHECK_EQ(strtoint32(words[0]), words.size() - 1);
|
||||
for (int i = 1; i < words.size(); ++i) {
|
||||
const int job = strtoint32(words[i]);
|
||||
const int pos = possible_jobs_per_worker_[num_workers_read_].size();
|
||||
possible_jobs_per_worker_[num_workers_read_].push_back(job);
|
||||
possible_assignments_per_job_[job].push_back(
|
||||
{num_workers_read_, pos});
|
||||
}
|
||||
num_workers_read_++;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case WORKERS_SEEN: {
|
||||
CHECK_EQ(strtoint32(words[0]), words.size() - 1);
|
||||
for (int i = 1; i < words.size(); ++i) {
|
||||
const int job = strtoint32(words[i]);
|
||||
const int pos = possible_jobs_per_worker_[num_workers_read_].size();
|
||||
possible_jobs_per_worker_[num_workers_read_].push_back(job);
|
||||
possible_assignments_per_job_[job].push_back({
|
||||
num_workers_read_, pos
|
||||
});
|
||||
}
|
||||
num_workers_read_++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Job> jobs_;
|
||||
std::vector<std::vector<int>> possible_jobs_per_worker_;
|
||||
std::vector<std::vector<Assignment>> possible_assignments_per_job_;
|
||||
std::vector<std::vector<int> > possible_jobs_per_worker_;
|
||||
std::vector<std::vector<Assignment> > possible_assignments_per_job_;
|
||||
LoadStatus load_status_;
|
||||
int declared_num_jobs_;
|
||||
int declared_num_workers_;
|
||||
int num_workers_read_;
|
||||
};
|
||||
|
||||
bool Overlaps(const ShiftMinimizationParser::Job& j1,
|
||||
const ShiftMinimizationParser::Job& j2) {
|
||||
bool Overlaps(const ShiftMinimizationParser::Job &j1,
|
||||
const ShiftMinimizationParser::Job &j2) {
|
||||
// TODO(user): Are end date inclusive or exclusive? To check.
|
||||
// For now, we assume that they are exclusive.
|
||||
return !(j1.start > j2.end || j2.start > j1.end);
|
||||
}
|
||||
|
||||
void LoadAndSolve(const std::string& file_name) {
|
||||
void LoadAndSolve(const std::string &file_name) {
|
||||
ShiftMinimizationParser parser;
|
||||
CHECK(parser.LoadFile(file_name));
|
||||
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const int num_workers = parser.possible_jobs_per_worker().size();
|
||||
const std::vector<ShiftMinimizationParser::Job>& jobs = parser.jobs();
|
||||
const std::vector<ShiftMinimizationParser::Job> &jobs = parser.jobs();
|
||||
const int num_jobs = jobs.size();
|
||||
|
||||
std::vector<BoolVar> active_workers(num_workers);
|
||||
std::vector<std::vector<BoolVar>> worker_job_vars(num_workers);
|
||||
std::vector<std::vector<BoolVar>> possible_workers_per_job(num_jobs);
|
||||
std::vector<std::vector<BoolVar> > worker_job_vars(num_workers);
|
||||
std::vector<std::vector<BoolVar> > possible_workers_per_job(num_jobs);
|
||||
|
||||
for (int w = 0; w < num_workers; ++w) {
|
||||
// Status variables for workers, are they active or not?
|
||||
@@ -195,7 +201,7 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
|
||||
// Job-Worker variable. worker_job_vars[w][i] is true iff worker w
|
||||
// performs it's ith possible job.
|
||||
const std::vector<int>& possible = parser.possible_jobs_per_worker()[w];
|
||||
const std::vector<int> &possible = parser.possible_jobs_per_worker()[w];
|
||||
for (int p : possible) {
|
||||
const BoolVar var = cp_model.NewBoolVar();
|
||||
worker_job_vars[w].push_back(var);
|
||||
@@ -210,14 +216,16 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
if (Overlaps(jobs[job1], jobs[job2])) {
|
||||
const BoolVar v1 = worker_job_vars[w][i];
|
||||
const BoolVar v2 = worker_job_vars[w][j];
|
||||
cp_model.AddBoolOr({Not(v1), Not(v2)});
|
||||
cp_model.AddBoolOr({
|
||||
Not(v1), Not(v2)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maintain active_workers variable.
|
||||
cp_model.AddBoolOr(worker_job_vars[w]).OnlyEnforceIf(active_workers[w]);
|
||||
for (const BoolVar& var : worker_job_vars[w]) {
|
||||
for (const BoolVar &var : worker_job_vars[w]) {
|
||||
cp_model.AddImplication(var, active_workers[w]);
|
||||
}
|
||||
}
|
||||
@@ -235,7 +243,7 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
// then the number of active workers on these jobs is equal to the number of
|
||||
// active jobs.
|
||||
std::set<int> time_points;
|
||||
std::set<std::vector<int>> visited_job_lists;
|
||||
std::set<std::vector<int> > visited_job_lists;
|
||||
|
||||
for (int j = 0; j < num_jobs; ++j) {
|
||||
time_points.insert(parser.jobs()[j].start);
|
||||
@@ -250,7 +258,7 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
// Collect all jobs that intersects with this time point.
|
||||
std::vector<int> intersecting_jobs;
|
||||
for (int j = 0; j < num_jobs; ++j) {
|
||||
const ShiftMinimizationParser::Job& job = parser.jobs()[j];
|
||||
const ShiftMinimizationParser::Job &job = parser.jobs()[j];
|
||||
// Assumption: End date are inclusive.
|
||||
if (t >= job.start && t <= job.end) {
|
||||
intersecting_jobs.push_back(j);
|
||||
@@ -258,13 +266,14 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
}
|
||||
|
||||
// Check that we have not already visited this exact set of candidate jobs.
|
||||
if (gtl::ContainsKey(visited_job_lists, intersecting_jobs)) continue;
|
||||
if (gtl::ContainsKey(visited_job_lists, intersecting_jobs))
|
||||
continue;
|
||||
visited_job_lists.insert(intersecting_jobs);
|
||||
|
||||
// Collect the relevant worker job vars.
|
||||
std::vector<BoolVar> overlapping_worker_jobs;
|
||||
for (int j : intersecting_jobs) {
|
||||
for (const auto& p : parser.possible_assignments_per_job()[j]) {
|
||||
for (const auto &p : parser.possible_assignments_per_job()[j]) {
|
||||
const BoolVar var = worker_job_vars[p.worker_id][p.job_index];
|
||||
overlapping_worker_jobs.push_back(var);
|
||||
}
|
||||
@@ -292,21 +301,21 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
|
||||
// Solve.
|
||||
Model model;
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
|
||||
const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model);
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
}
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
int main(int argc, char **argv) {
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_input.empty()) {
|
||||
if (absl::GetFlag(FLAGS_input).empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
}
|
||||
|
||||
operations_research::sat::LoadAndSolve(FLAGS_input);
|
||||
operations_research::sat::LoadAndSolve(absl::GetFlag(FLAGS_input));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -21,35 +21,34 @@
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/model.h"
|
||||
|
||||
const std::vector<std::vector<int>> tiny = {{3, 3, 1}};
|
||||
const std::vector<std::vector<int> > tiny = { { 3, 3, 1 } };
|
||||
|
||||
const std::vector<std::vector<int>> small = {
|
||||
{3, 2, -1, 3}, {-1, -1, -1, 2}, {3, -1, -1, -1}, {3, -1, 3, 1}};
|
||||
const std::vector<std::vector<int> > small = {
|
||||
{ 3, 2, -1, 3 }, { -1, -1, -1, 2 }, { 3, -1, -1, -1 }, { 3, -1, 3, 1 }
|
||||
};
|
||||
|
||||
const std::vector<std::vector<int>> medium = {
|
||||
{-1, 0, -1, 1, -1, -1, 1, -1}, {-1, 3, -1, -1, 2, 3, -1, 2},
|
||||
{-1, -1, 0, -1, -1, -1, -1, 0}, {-1, 3, -1, -1, 0, -1, -1, -1},
|
||||
{-1, -1, -1, 3, -1, -1, 0, -1}, {1, -1, -1, -1, -1, 3, -1, -1},
|
||||
{3, -1, 1, 3, -1, -1, 3, -1}, {-1, 0, -1, -1, 3, -1, 3, -1}};
|
||||
const std::vector<std::vector<int> > medium = {
|
||||
{ -1, 0, -1, 1, -1, -1, 1, -1 }, { -1, 3, -1, -1, 2, 3, -1, 2 },
|
||||
{ -1, -1, 0, -1, -1, -1, -1, 0 }, { -1, 3, -1, -1, 0, -1, -1, -1 },
|
||||
{ -1, -1, -1, 3, -1, -1, 0, -1 }, { 1, -1, -1, -1, -1, 3, -1, -1 },
|
||||
{ 3, -1, 1, 3, -1, -1, 3, -1 }, { -1, 0, -1, -1, 3, -1, 3, -1 }
|
||||
};
|
||||
|
||||
const std::vector<std::vector<int>> big = {
|
||||
{3, -1, -1, -1, 2, -1, 1, -1, 1, 2},
|
||||
{1, -1, 0, -1, 3, -1, 2, 0, -1, -1},
|
||||
{-1, 3, -1, -1, -1, -1, -1, -1, 3, -1},
|
||||
{2, 0, -1, 3, -1, 2, 3, -1, -1, -1},
|
||||
{-1, -1, -1, 1, 1, 1, -1, -1, 3, 3},
|
||||
{2, 3, -1, -1, 2, 2, 3, -1, -1, -1},
|
||||
{-1, -1, -1, 1, 2, -1, 2, -1, 3, 3},
|
||||
{-1, 2, -1, -1, -1, -1, -1, -1, 2, -1},
|
||||
{-1, -1, 1, 1, -1, 2, -1, 1, -1, 3},
|
||||
{3, 3, -1, 1, -1, 2, -1, -1, -1, 2}};
|
||||
const std::vector<std::vector<int> > big = {
|
||||
{ 3, -1, -1, -1, 2, -1, 1, -1, 1, 2 }, { 1, -1, 0, -1, 3, -1, 2, 0, -1, -1 },
|
||||
{ -1, 3, -1, -1, -1, -1, -1, -1, 3, -1 },
|
||||
{ 2, 0, -1, 3, -1, 2, 3, -1, -1, -1 }, { -1, -1, -1, 1, 1, 1, -1, -1, 3, 3 },
|
||||
{ 2, 3, -1, -1, 2, 2, 3, -1, -1, -1 }, { -1, -1, -1, 1, 2, -1, 2, -1, 3, 3 },
|
||||
{ -1, 2, -1, -1, -1, -1, -1, -1, 2, -1 },
|
||||
{ -1, -1, 1, 1, -1, 2, -1, 1, -1, 3 }, { 3, 3, -1, 1, -1, 2, -1, -1, -1, 2 }
|
||||
};
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void PrintSolution(const std::vector<std::vector<int>> &data,
|
||||
const std::vector<std::vector<bool>> &h_arcs,
|
||||
const std::vector<std::vector<bool>> &v_arcs) {
|
||||
void PrintSolution(const std::vector<std::vector<int> > &data,
|
||||
const std::vector<std::vector<bool> > &h_arcs,
|
||||
const std::vector<std::vector<bool> > &v_arcs) {
|
||||
const int num_rows = data.size();
|
||||
const int num_columns = data[0].size();
|
||||
|
||||
@@ -83,7 +82,7 @@ void PrintSolution(const std::vector<std::vector<int>> &data,
|
||||
std::cout << last_line << std::endl;
|
||||
}
|
||||
|
||||
void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
void SlitherLink(const std::vector<std::vector<int> > &data) {
|
||||
const int num_rows = data.size();
|
||||
const int num_columns = data[0].size();
|
||||
|
||||
@@ -91,23 +90,26 @@ void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
const int num_horizontal_arcs = num_columns * (num_rows + 1);
|
||||
const int num_vertical_arcs = (num_rows) * (num_columns + 1);
|
||||
|
||||
auto undirected_horizontal_arc = [=](int x, int y) {
|
||||
auto undirected_horizontal_arc = [ = ](int x, int y) {
|
||||
CHECK_LT(x, num_columns);
|
||||
CHECK_LT(y, num_rows + 1);
|
||||
return x + (num_columns * y);
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
auto undirected_vertical_arc = [=](int x, int y) {
|
||||
auto undirected_vertical_arc = [ = ](int x, int y) {
|
||||
CHECK_LT(x, num_columns + 1);
|
||||
CHECK_LT(y, num_rows);
|
||||
return x + (num_columns + 1) * y;
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
auto node_index = [=](int x, int y) {
|
||||
auto node_index = [ = ](int x, int y) {
|
||||
CHECK_LT(x, num_columns + 1);
|
||||
CHECK_LT(y, num_rows + 1);
|
||||
return x + y * (num_columns + 1);
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
CpModelBuilder builder;
|
||||
|
||||
@@ -154,7 +156,8 @@ void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
|
||||
for (int x = 0; x < num_columns; ++x) {
|
||||
for (int y = 0; y < num_rows; ++y) {
|
||||
if (data[y][x] == -1) continue;
|
||||
if (data[y][x] == -1)
|
||||
continue;
|
||||
std::vector<BoolVar> neighbors;
|
||||
const int top_arc = undirected_horizontal_arc(x, y);
|
||||
neighbors.push_back(horizontal_arcs[2 * top_arc]);
|
||||
@@ -175,31 +178,43 @@ void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
// Special rule on corners: value == 3 implies 2 corner arcs used.
|
||||
if (data[0][0] == 3) {
|
||||
const int h_arc = undirected_horizontal_arc(0, 0);
|
||||
builder.AddBoolOr(
|
||||
{horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]
|
||||
});
|
||||
const int v_arc = undirected_vertical_arc(0, 0);
|
||||
builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]
|
||||
});
|
||||
}
|
||||
if (data[0][num_columns - 1] == 3) {
|
||||
const int h_arc = undirected_horizontal_arc(num_columns - 1, 0);
|
||||
builder.AddBoolOr(
|
||||
{horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]
|
||||
});
|
||||
const int v_arc = undirected_vertical_arc(num_columns, 0);
|
||||
builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]
|
||||
});
|
||||
}
|
||||
if (data[num_rows - 1][0] == 3) {
|
||||
const int h_arc = undirected_horizontal_arc(0, num_rows);
|
||||
builder.AddBoolOr(
|
||||
{horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]
|
||||
});
|
||||
const int v_arc = undirected_vertical_arc(0, num_rows - 1);
|
||||
builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]
|
||||
});
|
||||
}
|
||||
if (data[num_rows - 1][num_columns - 1] == 3) {
|
||||
const int h_arc = undirected_horizontal_arc(num_columns - 1, num_rows);
|
||||
builder.AddBoolOr(
|
||||
{horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]
|
||||
});
|
||||
const int v_arc = undirected_vertical_arc(num_columns, num_rows - 1);
|
||||
builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]});
|
||||
builder.AddBoolOr({
|
||||
vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]
|
||||
});
|
||||
}
|
||||
|
||||
// Topology rule: Border arcs are oriented in one direction.
|
||||
@@ -219,7 +234,7 @@ void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
|
||||
const CpSolverResponse response = Solve(builder.Build());
|
||||
|
||||
std::vector<std::vector<bool>> h_arcs(num_rows + 1);
|
||||
std::vector<std::vector<bool> > h_arcs(num_rows + 1);
|
||||
for (int y = 0; y < num_rows + 1; ++y) {
|
||||
for (int x = 0; x < num_columns; ++x) {
|
||||
const int arc = undirected_horizontal_arc(x, y);
|
||||
@@ -229,7 +244,7 @@ void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<bool>> v_arcs(num_columns + 1);
|
||||
std::vector<std::vector<bool> > v_arcs(num_columns + 1);
|
||||
for (int y = 0; y < num_rows; ++y) {
|
||||
for (int x = 0; x < num_columns + 1; ++x) {
|
||||
const int arc = undirected_vertical_arc(x, y);
|
||||
@@ -243,8 +258,8 @@ void SlitherLink(const std::vector<std::vector<int>> &data) {
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
std::cout << "Tiny problem" << std::endl;
|
||||
|
||||
@@ -63,7 +63,7 @@ DEFINE_string(dump_model, "", "If non-empty, dumps MPModelProto there.");
|
||||
DEFINE_string(dump_request, "", "If non-empty, dumps MPModelRequest there.");
|
||||
DEFINE_string(dump_response, "", "If non-empty, dumps MPModelResponse there.");
|
||||
|
||||
DECLARE_bool(verify_solution); // Defined in ./linear_solver.cc
|
||||
DECLARE_bool(verify_solution); // Defined in ./linear_solver.cc
|
||||
|
||||
static const char kUsageStr[] =
|
||||
"Run MPSolver on the given input file. Many formats are supported: \n"
|
||||
@@ -81,26 +81,28 @@ namespace {
|
||||
bool Run() {
|
||||
// Create the solver and set its parameters.
|
||||
MPSolver::OptimizationProblemType type;
|
||||
CHECK(MPSolver::ParseSolverType(FLAGS_solver, &type))
|
||||
<< "Unsupported --solver: " << FLAGS_solver;
|
||||
CHECK(MPSolver::ParseSolverType(absl::GetFlag(FLAGS_solver), &type))
|
||||
<< "Unsupported --solver: " << absl::GetFlag(FLAGS_solver);
|
||||
|
||||
// Load the problem into an MPModelProto.
|
||||
MPModelProto model_proto;
|
||||
MPModelRequest request_proto;
|
||||
if (absl::EndsWith(FLAGS_input, ".mps") ||
|
||||
absl::EndsWith(FLAGS_input, ".mps.gz")) {
|
||||
CHECK_OK(glop::MPSReader().ParseFile(FLAGS_input, &model_proto))
|
||||
<< "Error while parsing the mps file '" << FLAGS_input << "'.";
|
||||
if (absl::EndsWith(absl::GetFlag(FLAGS_input), ".mps") ||
|
||||
absl::EndsWith(absl::GetFlag(FLAGS_input), ".mps.gz")) {
|
||||
CHECK_OK(
|
||||
glop::MPSReader().ParseFile(absl::GetFlag(FLAGS_input), &model_proto))
|
||||
<< "Error while parsing the mps file '" << absl::GetFlag(FLAGS_input)
|
||||
<< "'.";
|
||||
} else {
|
||||
ReadFileToProto(FLAGS_input, &model_proto);
|
||||
ReadFileToProto(FLAGS_input, &request_proto);
|
||||
ReadFileToProto(absl::GetFlag(FLAGS_input), &model_proto);
|
||||
ReadFileToProto(absl::GetFlag(FLAGS_input), &request_proto);
|
||||
// If the input proto is in binary format, both ReadFileToProto could return
|
||||
// true. Instead use the actual number of variables found to test the
|
||||
// correct format of the input.
|
||||
const bool is_model_proto = model_proto.variable_size() > 0;
|
||||
const bool is_request_proto = request_proto.model().variable_size() > 0;
|
||||
if (!is_model_proto && !is_request_proto) {
|
||||
LOG(FATAL) << "Failed to parse '" << FLAGS_input
|
||||
LOG(FATAL) << "Failed to parse '" << absl::GetFlag(FLAGS_input)
|
||||
<< "' as an MPModelProto or an MPModelRequest.";
|
||||
} else {
|
||||
CHECK(!(is_model_proto && is_request_proto));
|
||||
@@ -112,54 +114,55 @@ bool Run() {
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("%-12s: '%s'\n", "File", FLAGS_input.c_str());
|
||||
printf("%-12s: '%s'\n", "File", absl::GetFlag(FLAGS_input).c_str());
|
||||
|
||||
// Detect format to dump protos.
|
||||
operations_research::ProtoWriteFormat write_format;
|
||||
if (FLAGS_dump_format == "text") {
|
||||
if (absl::GetFlag(FLAGS_dump_format) == "text") {
|
||||
write_format = ProtoWriteFormat::kProtoText;
|
||||
} else if (FLAGS_dump_format == "binary") {
|
||||
} else if (absl::GetFlag(FLAGS_dump_format) == "binary") {
|
||||
write_format = ProtoWriteFormat::kProtoBinary;
|
||||
} else if (FLAGS_dump_format == "json") {
|
||||
} else if (absl::GetFlag(FLAGS_dump_format) == "json") {
|
||||
write_format = ProtoWriteFormat::kJson;
|
||||
} else {
|
||||
LOG(FATAL) << "Unsupported --dump_format: " << FLAGS_dump_format;
|
||||
LOG(FATAL)
|
||||
<< "Unsupported --dump_format: " << absl::GetFlag(FLAGS_dump_format);
|
||||
}
|
||||
|
||||
// Create the solver, we use the name of the model as the solver name.
|
||||
MPSolver solver(model_proto.name(), type);
|
||||
const absl::Status set_num_threads_status =
|
||||
solver.SetNumThreads(FLAGS_num_threads);
|
||||
solver.SetNumThreads(absl::GetFlag(FLAGS_num_threads));
|
||||
if (set_num_threads_status.ok()) {
|
||||
LOG(INFO) << "Set number of threads to " << FLAGS_num_threads << ".";
|
||||
LOG(INFO) << "Set number of threads to " << absl::GetFlag(FLAGS_num_threads)
|
||||
<< ".";
|
||||
} else {
|
||||
LOG(ERROR) << "Failed to set number of threads due to: "
|
||||
<< set_num_threads_status.message() << ". Using 1 as default.";
|
||||
}
|
||||
solver.EnableOutput();
|
||||
if (!FLAGS_params_file.empty()) {
|
||||
if (!absl::GetFlag(FLAGS_params_file).empty()) {
|
||||
std::string file_contents;
|
||||
CHECK_OK(
|
||||
file::GetContents(FLAGS_params_file, &file_contents, file::Defaults()))
|
||||
CHECK_OK(file::GetContents(absl::GetFlag(FLAGS_params_file), &file_contents,
|
||||
file::Defaults()))
|
||||
<< "Could not read parameters file.";
|
||||
CHECK(solver.SetSolverSpecificParametersAsString(file_contents));
|
||||
} else if (!FLAGS_params.empty()) {
|
||||
CHECK(solver.SetSolverSpecificParametersAsString(FLAGS_params))
|
||||
<< "Wrong --params format.";
|
||||
} else if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
CHECK(solver.SetSolverSpecificParametersAsString(
|
||||
absl::GetFlag(FLAGS_params))) << "Wrong --params format.";
|
||||
}
|
||||
absl::PrintF(
|
||||
"%-12s: %s\n", "Solver",
|
||||
MPModelRequest::SolverType_Name(
|
||||
static_cast<MPModelRequest::SolverType>(solver.ProblemType()))
|
||||
.c_str());
|
||||
MPModelRequest::SolverType_Name(static_cast<MPModelRequest::SolverType>(
|
||||
solver.ProblemType())).c_str());
|
||||
|
||||
// Load the proto into the solver.
|
||||
std::string error_message;
|
||||
|
||||
// If requested, save the model to file.
|
||||
if (!FLAGS_dump_model.empty()) {
|
||||
CHECK(WriteProtoToFile(FLAGS_dump_model, model_proto, write_format,
|
||||
FLAGS_dump_gzip));
|
||||
if (!absl::GetFlag(FLAGS_dump_model).empty()) {
|
||||
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_model), model_proto,
|
||||
write_format, absl::GetFlag(FLAGS_dump_gzip)));
|
||||
}
|
||||
|
||||
const MPSolverResponseStatus status =
|
||||
@@ -170,8 +173,8 @@ bool Run() {
|
||||
static_cast<int64>(1000.0 * request_proto.solver_time_limit_seconds()));
|
||||
}
|
||||
// Note, the underlying MPSolver treats time limit equal to 0 as no limit.
|
||||
if (FLAGS_time_limit_ms >= 0) {
|
||||
solver.set_time_limit(FLAGS_time_limit_ms);
|
||||
if (absl::GetFlag(FLAGS_time_limit_ms) >= 0) {
|
||||
solver.set_time_limit(absl::GetFlag(FLAGS_time_limit_ms));
|
||||
}
|
||||
if (status != MPSOLVER_MODEL_IS_VALID) {
|
||||
LOG(ERROR) << MPSolverResponseStatus_Name(status) << ": " << error_message;
|
||||
@@ -189,7 +192,7 @@ bool Run() {
|
||||
solving_time = absl::Now() - time_before;
|
||||
|
||||
// If requested, re-create a corresponding MPModelRequest and save it to file.
|
||||
if (!FLAGS_dump_request.empty()) {
|
||||
if (!absl::GetFlag(FLAGS_dump_request).empty()) {
|
||||
operations_research::MPModelRequest request;
|
||||
request.set_solver_type(
|
||||
static_cast<MPModelRequest::SolverType>(solver.ProblemType()));
|
||||
@@ -197,21 +200,21 @@ bool Run() {
|
||||
request.set_solver_specific_parameters(
|
||||
solver.GetSolverSpecificParametersAsString());
|
||||
*request.mutable_model() = model_proto;
|
||||
CHECK(WriteProtoToFile(FLAGS_dump_request, request, write_format,
|
||||
FLAGS_dump_gzip));
|
||||
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_request), request,
|
||||
write_format, absl::GetFlag(FLAGS_dump_gzip)));
|
||||
}
|
||||
|
||||
const bool has_solution =
|
||||
solve_status == MPSolver::OPTIMAL || solve_status == MPSolver::FEASIBLE;
|
||||
|
||||
// If requested, get the MPModelResponse and save it to file.
|
||||
if (!FLAGS_dump_response.empty() && has_solution) {
|
||||
if (!absl::GetFlag(FLAGS_dump_response).empty() && has_solution) {
|
||||
operations_research::MPSolutionResponse response;
|
||||
solver.FillSolutionResponseProto(&response);
|
||||
CHECK(WriteProtoToFile(FLAGS_dump_response, response, write_format,
|
||||
FLAGS_dump_gzip));
|
||||
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_response), response,
|
||||
write_format, absl::GetFlag(FLAGS_dump_gzip)));
|
||||
}
|
||||
if (!FLAGS_output_csv.empty() && has_solution) {
|
||||
if (!absl::GetFlag(FLAGS_output_csv).empty() && has_solution) {
|
||||
operations_research::MPSolutionResponse result;
|
||||
solver.FillSolutionResponseProto(&result);
|
||||
std::string csv_file;
|
||||
@@ -219,21 +222,21 @@ bool Run() {
|
||||
csv_file += absl::StrFormat("%s,%e\n", model_proto.variable(i).name(),
|
||||
result.variable_value(i));
|
||||
}
|
||||
CHECK_OK(file::SetContents(FLAGS_output_csv, csv_file, file::Defaults()));
|
||||
CHECK_OK(file::SetContents(absl::GetFlag(FLAGS_output_csv), csv_file,
|
||||
file::Defaults()));
|
||||
}
|
||||
// If --verify_solution is true, we already verified it. If not, we add
|
||||
// a verification step here.
|
||||
if (has_solution && !FLAGS_verify_solution) {
|
||||
if (has_solution && !absl::GetFlag(FLAGS_verify_solution)) {
|
||||
LOG(INFO) << "Verifying the solution";
|
||||
solver.VerifySolution(/*tolerance=*/param.GetDoubleParam(
|
||||
solver.VerifySolution(/*tolerance=*/ param.GetDoubleParam(
|
||||
MPSolverParameters::PRIMAL_TOLERANCE),
|
||||
/*log_errors=*/true);
|
||||
/*log_errors=*/ true);
|
||||
}
|
||||
|
||||
absl::PrintF("%-12s: %s\n", "Status",
|
||||
MPSolverResponseStatus_Name(
|
||||
static_cast<MPSolverResponseStatus>(solve_status))
|
||||
.c_str());
|
||||
static_cast<MPSolverResponseStatus>(solve_status)).c_str());
|
||||
absl::PrintF("%-12s: %15.15e\n", "Objective",
|
||||
has_solution ? solver.Objective().Value() : 0.0);
|
||||
absl::PrintF("%-12s: %15.15e\n", "BestBound",
|
||||
@@ -247,13 +250,13 @@ bool Run() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace operations_research
|
||||
} // namespace
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true);
|
||||
CHECK(!FLAGS_input.empty()) << "--input is required";
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/ true);
|
||||
CHECK(!absl::GetFlag(FLAGS_input).empty()) << "--input is required";
|
||||
operations_research::Run();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -61,21 +61,20 @@ void FirstModel(int num_teams) {
|
||||
CpModelBuilder builder;
|
||||
|
||||
// Calendar variables.
|
||||
std::vector<std::vector<IntVar>> opponents(num_teams);
|
||||
std::vector<std::vector<BoolVar>> home_aways(num_teams);
|
||||
std::vector<std::vector<IntVar>> signed_opponents(num_teams);
|
||||
std::vector<std::vector<IntVar> > opponents(num_teams);
|
||||
std::vector<std::vector<BoolVar> > home_aways(num_teams);
|
||||
std::vector<std::vector<IntVar> > signed_opponents(num_teams);
|
||||
|
||||
for (int t = 0; t < num_teams; ++t) {
|
||||
for (int d = 0; d < num_days; ++d) {
|
||||
Domain opponent_domain(0, num_teams - 1);
|
||||
Domain signed_opponent_domain(0, 2 * num_teams - 1);
|
||||
IntVar opp = builder.NewIntVar(opponent_domain)
|
||||
.WithName(absl::StrCat("opponent_", t, "_", d));
|
||||
.WithName(absl::StrCat("opponent_", t, "_", d));
|
||||
BoolVar home =
|
||||
builder.NewBoolVar().WithName(absl::StrCat("home_aways", t, "_", d));
|
||||
IntVar signed_opp =
|
||||
builder.NewIntVar(signed_opponent_domain)
|
||||
.WithName(absl::StrCat("signed_opponent_", t, "_", d));
|
||||
IntVar signed_opp = builder.NewIntVar(signed_opponent_domain)
|
||||
.WithName(absl::StrCat("signed_opponent_", t, "_", d));
|
||||
|
||||
opponents[t].push_back(opp);
|
||||
home_aways[t].push_back(home);
|
||||
@@ -109,7 +108,10 @@ void FirstModel(int num_teams) {
|
||||
IntVar second_home = builder.NewBoolVar();
|
||||
builder.AddVariableElement(day_opponents[first_team], day_home_aways,
|
||||
second_home);
|
||||
builder.AddEquality(LinearExpr::Sum({first_home, second_home}), 1);
|
||||
builder.AddEquality(LinearExpr::Sum({
|
||||
first_home, second_home
|
||||
}),
|
||||
1);
|
||||
}
|
||||
|
||||
builder.AddEquality(LinearExpr::Sum(day_home_aways), num_teams / 2);
|
||||
@@ -136,11 +138,13 @@ void FirstModel(int num_teams) {
|
||||
|
||||
// Forbid sequence of 3 homes or 3 aways.
|
||||
for (int start = 0; start < num_days - 2; ++start) {
|
||||
builder.AddBoolOr({home_aways[t][start], home_aways[t][start + 1],
|
||||
home_aways[t][start + 2]});
|
||||
builder.AddBoolOr({Not(home_aways[t][start]),
|
||||
Not(home_aways[t][start + 1]),
|
||||
Not(home_aways[t][start + 2])});
|
||||
builder.AddBoolOr({
|
||||
home_aways[t][start], home_aways[t][start + 1], home_aways[t][start + 2]
|
||||
});
|
||||
builder.AddBoolOr({
|
||||
Not(home_aways[t][start]), Not(home_aways[t][start + 1]),
|
||||
Not(home_aways[t][start + 2])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +154,12 @@ void FirstModel(int num_teams) {
|
||||
for (int d = 0; d < num_days - 1; ++d) {
|
||||
BoolVar break_var =
|
||||
builder.NewBoolVar().WithName(absl::StrCat("break_", t, "_", d));
|
||||
builder.AddBoolOr(
|
||||
{Not(home_aways[t][d]), Not(home_aways[t][d + 1]), break_var});
|
||||
builder.AddBoolOr({home_aways[t][d], home_aways[t][d + 1], break_var});
|
||||
builder.AddBoolOr({
|
||||
Not(home_aways[t][d]), Not(home_aways[t][d + 1]), break_var
|
||||
});
|
||||
builder.AddBoolOr({
|
||||
home_aways[t][d], home_aways[t][d + 1], break_var
|
||||
});
|
||||
breaks.push_back(break_var);
|
||||
}
|
||||
}
|
||||
@@ -160,8 +167,8 @@ void FirstModel(int num_teams) {
|
||||
builder.Minimize(LinearExpr::BooleanSum(breaks));
|
||||
|
||||
Model model;
|
||||
if (!FLAGS_params.empty()) {
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
}
|
||||
|
||||
const CpSolverResponse response = SolveCpModel(builder.Build(), &model);
|
||||
@@ -193,7 +200,7 @@ void SecondModel(int num_teams) {
|
||||
CpModelBuilder builder;
|
||||
|
||||
// Does team i receive team j at home on day d?
|
||||
std::vector<std::vector<std::vector<BoolVar>>> fixtures(num_days);
|
||||
std::vector<std::vector<std::vector<BoolVar> > > fixtures(num_days);
|
||||
for (int d = 0; d < num_days; ++d) {
|
||||
fixtures[d].resize(num_teams);
|
||||
for (int i = 0; i < num_teams; ++i) {
|
||||
@@ -209,7 +216,7 @@ void SecondModel(int num_teams) {
|
||||
}
|
||||
|
||||
// Is team t at home on day d?
|
||||
std::vector<std::vector<BoolVar>> at_home(num_days);
|
||||
std::vector<std::vector<BoolVar> > at_home(num_days);
|
||||
for (int d = 0; d < num_days; ++d) {
|
||||
for (int t = 0; t < num_teams; ++t) {
|
||||
at_home[d].push_back(builder.NewBoolVar());
|
||||
@@ -221,7 +228,8 @@ void SecondModel(int num_teams) {
|
||||
for (int team = 0; team < num_teams; ++team) {
|
||||
std::vector<BoolVar> possible_opponents;
|
||||
for (int other = 0; other < num_teams; ++other) {
|
||||
if (team == other) continue;
|
||||
if (team == other)
|
||||
continue;
|
||||
possible_opponents.push_back(fixtures[d][team][other]);
|
||||
possible_opponents.push_back(fixtures[d][other][team]);
|
||||
}
|
||||
@@ -232,7 +240,8 @@ void SecondModel(int num_teams) {
|
||||
// Each fixture happens once per season.
|
||||
for (int team = 0; team < num_teams; ++team) {
|
||||
for (int other = 0; other < num_teams; ++other) {
|
||||
if (team == other) continue;
|
||||
if (team == other)
|
||||
continue;
|
||||
std::vector<BoolVar> possible_days;
|
||||
for (int d = 0; d < num_days; ++d) {
|
||||
possible_days.push_back(fixtures[d][team][other]);
|
||||
@@ -244,7 +253,8 @@ void SecondModel(int num_teams) {
|
||||
// Meet each opponent once per season.
|
||||
for (int team = 0; team < num_teams; ++team) {
|
||||
for (int other = 0; other < num_teams; ++other) {
|
||||
if (team == other) continue;
|
||||
if (team == other)
|
||||
continue;
|
||||
std::vector<BoolVar> first_half;
|
||||
std::vector<BoolVar> second_half;
|
||||
for (int d = 0; d < matches_per_day; ++d) {
|
||||
@@ -262,7 +272,8 @@ void SecondModel(int num_teams) {
|
||||
for (int d = 0; d < num_days; ++d) {
|
||||
for (int team = 0; team < num_teams; ++team) {
|
||||
for (int other = 0; other < num_teams; ++other) {
|
||||
if (team == other) continue;
|
||||
if (team == other)
|
||||
continue;
|
||||
builder.AddImplication(fixtures[d][team][other], at_home[d][team]);
|
||||
builder.AddImplication(fixtures[d][team][other],
|
||||
Not(at_home[d][other]));
|
||||
@@ -273,10 +284,13 @@ void SecondModel(int num_teams) {
|
||||
// Forbid sequence of 3 homes or 3 aways.
|
||||
for (int team = 0; team < num_teams; ++team) {
|
||||
for (int d = 0; d < num_days - 2; ++d) {
|
||||
builder.AddBoolOr(
|
||||
{at_home[d][team], at_home[d + 1][team], at_home[d + 2][team]});
|
||||
builder.AddBoolOr({Not(at_home[d][team]), Not(at_home[d + 1][team]),
|
||||
Not(at_home[d + 2][team])});
|
||||
builder.AddBoolOr({
|
||||
at_home[d][team], at_home[d + 1][team], at_home[d + 2][team]
|
||||
});
|
||||
builder.AddBoolOr({
|
||||
Not(at_home[d][team]), Not(at_home[d + 1][team]),
|
||||
Not(at_home[d + 2][team])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,13 +299,18 @@ void SecondModel(int num_teams) {
|
||||
for (int t = 0; t < num_teams; ++t) {
|
||||
for (int d = 0; d < num_days - 1; ++d) {
|
||||
BoolVar break_var = builder.NewBoolVar();
|
||||
builder.AddBoolOr(
|
||||
{Not(at_home[d][t]), Not(at_home[d + 1][t]), break_var});
|
||||
builder.AddBoolOr({at_home[d][t], at_home[d + 1][t], break_var});
|
||||
builder.AddBoolOr(
|
||||
{Not(at_home[d][t]), at_home[d + 1][t], Not(break_var)});
|
||||
builder.AddBoolOr(
|
||||
{at_home[d][t], Not(at_home[d + 1][t]), Not(break_var)});
|
||||
builder.AddBoolOr({
|
||||
Not(at_home[d][t]), Not(at_home[d + 1][t]), break_var
|
||||
});
|
||||
builder.AddBoolOr({
|
||||
at_home[d][t], at_home[d + 1][t], break_var
|
||||
});
|
||||
builder.AddBoolOr({
|
||||
Not(at_home[d][t]), at_home[d + 1][t], Not(break_var)
|
||||
});
|
||||
builder.AddBoolOr({
|
||||
at_home[d][t], Not(at_home[d + 1][t]), Not(break_var)
|
||||
});
|
||||
breaks.push_back(break_var);
|
||||
}
|
||||
}
|
||||
@@ -301,16 +320,16 @@ void SecondModel(int num_teams) {
|
||||
builder.Minimize(LinearExpr::BooleanSum(breaks));
|
||||
|
||||
Model model;
|
||||
if (!FLAGS_params.empty()) {
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
if (!absl::GetFlag(FLAGS_params).empty()) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
}
|
||||
|
||||
const CpSolverResponse response = SolveCpModel(builder.Build(), &model);
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
static const char kUsage[] =
|
||||
"Usage: see flags.\nThis program runs a sports scheduling problem."
|
||||
@@ -319,8 +338,9 @@ static const char kUsage[] =
|
||||
int main(int argc, char **argv) {
|
||||
gflags::SetUsageMessage(kUsage);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_EQ(0, FLAGS_num_teams % 2) << "The number of teams must be even";
|
||||
CHECK_GE(FLAGS_num_teams, 2) << "At least 2 teams";
|
||||
operations_research::sat::SecondModel(FLAGS_num_teams);
|
||||
CHECK_EQ(0, absl::GetFlag(FLAGS_num_teams) % 2)
|
||||
<< "The number of teams must be even";
|
||||
CHECK_GE(absl::GetFlag(FLAGS_num_teams), 2) << "At least 2 teams";
|
||||
operations_research::sat::SecondModel(absl::GetFlag(FLAGS_num_teams));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -17,16 +17,13 @@
|
||||
namespace operations_research {
|
||||
void RunStiglerDietExample() {
|
||||
// Nutrient minimums.
|
||||
std::vector<std::pair<std::string, double>> nutrients = {
|
||||
{"Calories (kcal)", 3.0},
|
||||
{"Protein (g)", 70.0},
|
||||
{"Calcium (g)", 0.8},
|
||||
{"Iron (mg)", 12.0},
|
||||
{"Vitamin A (kIU)", 5.0},
|
||||
{"Thiamine (Vitamin B1) (mg)", 1.8},
|
||||
{"Riboflavin (Vitamin B2) (mg)", 2.7},
|
||||
{"Niacin (mg)", 18.0},
|
||||
{"Ascorbic Acid (Vitamin C) (mg)", 75.0}};
|
||||
std::vector<std::pair<std::string, double> > nutrients = {
|
||||
{ "Calories (kcal)", 3.0 }, { "Protein (g)", 70.0 }, { "Calcium (g)", 0.8 },
|
||||
{ "Iron (mg)", 12.0 }, { "Vitamin A (kIU)", 5.0 },
|
||||
{ "Thiamine (Vitamin B1) (mg)", 1.8 },
|
||||
{ "Riboflavin (Vitamin B2) (mg)", 2.7 }, { "Niacin (mg)", 18.0 },
|
||||
{ "Ascorbic Acid (Vitamin C) (mg)", 75.0 }
|
||||
};
|
||||
|
||||
struct Commodity {
|
||||
// Commodity name
|
||||
@@ -48,200 +45,146 @@ void RunStiglerDietExample() {
|
||||
};
|
||||
|
||||
std::vector<Commodity> data = {
|
||||
{"Wheat Flour (Enriched)",
|
||||
"10 lb.",
|
||||
36,
|
||||
{44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0}},
|
||||
{"Macaroni", "1 lb.", 14.1, {11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0}},
|
||||
{"Wheat Cereal (Enriched)",
|
||||
"28 oz.",
|
||||
24.2,
|
||||
{11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0}},
|
||||
{"Corn Flakes", "8 oz.", 7.1, {11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0}},
|
||||
{"Corn Meal",
|
||||
"1 lb.",
|
||||
4.6,
|
||||
{36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0}},
|
||||
{"Hominy Grits",
|
||||
"24 oz.",
|
||||
8.5,
|
||||
{28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0}},
|
||||
{"Rice", "1 lb.", 7.5, {21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0}},
|
||||
{"Rolled Oats", "1 lb.", 7.1, {25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0}},
|
||||
{"White Bread (Enriched)",
|
||||
"1 lb.",
|
||||
7.9,
|
||||
{15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0}},
|
||||
{"Whole Wheat Bread",
|
||||
"1 lb.",
|
||||
9.1,
|
||||
{12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0}},
|
||||
{"Rye Bread", "1 lb.", 9.1, {12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0}},
|
||||
{"Pound Cake", "1 lb.", 24.8, {8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0}},
|
||||
{"Soda Crackers", "1 lb.", 15.1, {12.5, 288, 0.5, 50, 0, 0, 0, 0, 0}},
|
||||
{"Milk", "1 qt.", 11, {6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177}},
|
||||
{"Evaporated Milk (can)",
|
||||
"14.5 oz.",
|
||||
6.7,
|
||||
{8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60}},
|
||||
{"Butter", "1 lb.", 30.8, {10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0}},
|
||||
{"Oleomargarine", "1 lb.", 16.1, {20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0}},
|
||||
{"Eggs", "1 doz.", 32.6, {2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0}},
|
||||
{"Cheese (Cheddar)",
|
||||
"1 lb.",
|
||||
24.2,
|
||||
{7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0}},
|
||||
{"Cream", "1/2 pt.", 14.1, {3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17}},
|
||||
{"Peanut Butter",
|
||||
"1 lb.",
|
||||
17.9,
|
||||
{15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0}},
|
||||
{"Mayonnaise", "1/2 pt.", 16.7, {8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0}},
|
||||
{"Crisco", "1 lb.", 20.3, {20.1, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"Lard", "1 lb.", 9.8, {41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0}},
|
||||
{"Sirloin Steak",
|
||||
"1 lb.",
|
||||
39.6,
|
||||
{2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0}},
|
||||
{"Round Steak", "1 lb.", 36.4, {2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0}},
|
||||
{"Rib Roast", "1 lb.", 29.2, {3.4, 213, 0.1, 33, 0, 0, 2, 0, 0}},
|
||||
{"Chuck Roast", "1 lb.", 22.6, {3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0}},
|
||||
{"Plate", "1 lb.", 14.6, {8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0}},
|
||||
{"Liver (Beef)",
|
||||
"1 lb.",
|
||||
26.8,
|
||||
{2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525}},
|
||||
{"Leg of Lamb", "1 lb.", 27.6, {3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0}},
|
||||
{"Lamb Chops (Rib)",
|
||||
"1 lb.",
|
||||
36.6,
|
||||
{3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0}},
|
||||
{"Pork Chops", "1 lb.", 30.7, {3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0}},
|
||||
{"Pork Loin Roast",
|
||||
"1 lb.",
|
||||
24.2,
|
||||
{4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0}},
|
||||
{"Bacon", "1 lb.", 25.6, {10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0}},
|
||||
{"Ham, smoked", "1 lb.", 27.4, {6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0}},
|
||||
{"Salt Pork", "1 lb.", 16, {18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0}},
|
||||
{"Roasting Chicken",
|
||||
"1 lb.",
|
||||
30.3,
|
||||
{1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46}},
|
||||
{"Veal Cutlets", "1 lb.", 42.3, {1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0}},
|
||||
{"Salmon, Pink (can)",
|
||||
"16 oz.",
|
||||
13,
|
||||
{5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0}},
|
||||
{"Apples", "1 lb.", 4.4, {5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544}},
|
||||
{"Bananas", "1 lb.", 6.1, {4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498}},
|
||||
{"Lemons", "1 doz.", 26, {1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952}},
|
||||
{"Oranges", "1 doz.", 30.9, {2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998}},
|
||||
{"Green Beans", "1 lb.", 7.1, {2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862}},
|
||||
{"Cabbage", "1 lb.", 3.7, {2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369}},
|
||||
{"Carrots", "1 bunch", 4.7, {2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608}},
|
||||
{"Celery", "1 stalk", 7.3, {0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313}},
|
||||
{"Lettuce", "1 head", 8.2, {0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449}},
|
||||
{"Onions", "1 lb.", 3.6, {5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184}},
|
||||
{"Potatoes",
|
||||
"15 lb.",
|
||||
34,
|
||||
{14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522}},
|
||||
{"Spinach", "1 lb.", 8.1, {1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755}},
|
||||
{"Sweet Potatoes",
|
||||
"1 lb.",
|
||||
5.1,
|
||||
{9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912}},
|
||||
{"Peaches (can)",
|
||||
"No. 2 1/2",
|
||||
16.8,
|
||||
{3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196}},
|
||||
{"Pears (can)",
|
||||
"No. 2 1/2",
|
||||
20.4,
|
||||
{3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81}},
|
||||
{"Pineapple (can)",
|
||||
"No. 2 1/2",
|
||||
21.3,
|
||||
{2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399}},
|
||||
{"Asparagus (can)",
|
||||
"No. 2",
|
||||
27.7,
|
||||
{0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272}},
|
||||
{"Green Beans (can)",
|
||||
"No. 2",
|
||||
10,
|
||||
{1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431}},
|
||||
{"Pork and Beans (can)",
|
||||
"16 oz.",
|
||||
7.1,
|
||||
{7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0}},
|
||||
{"Corn (can)", "No. 2", 10.4, {5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218}},
|
||||
{"Peas (can)",
|
||||
"No. 2",
|
||||
13.8,
|
||||
{2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370}},
|
||||
{"Tomatoes (can)",
|
||||
"No. 2",
|
||||
8.6,
|
||||
{1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253}},
|
||||
{"Tomato Soup (can)",
|
||||
"10 1/2 oz.",
|
||||
7.6,
|
||||
{1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862}},
|
||||
{"Peaches, Dried",
|
||||
"1 lb.",
|
||||
15.7,
|
||||
{8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57}},
|
||||
{"Prunes, Dried",
|
||||
"1 lb.",
|
||||
9,
|
||||
{12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257}},
|
||||
{"Raisins, Dried",
|
||||
"15 oz.",
|
||||
9.4,
|
||||
{13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136}},
|
||||
{"Peas, Dried",
|
||||
"1 lb.",
|
||||
7.9,
|
||||
{20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0}},
|
||||
{"Lima Beans, Dried",
|
||||
"1 lb.",
|
||||
8.9,
|
||||
{17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0}},
|
||||
{"Navy Beans, Dried",
|
||||
"1 lb.",
|
||||
5.9,
|
||||
{26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0}},
|
||||
{"Coffee", "1 lb.", 22.4, {0, 0, 0, 0, 0, 4, 5.1, 50, 0}},
|
||||
{"Tea", "1/4 lb.", 17.4, {0, 0, 0, 0, 0, 0, 2.3, 42, 0}},
|
||||
{"Cocoa", "8 oz.", 8.6, {8.7, 237, 3, 72, 0, 2, 11.9, 40, 0}},
|
||||
{"Chocolate", "8 oz.", 16.2, {8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0}},
|
||||
{"Sugar", "10 lb.", 51.7, {34.9, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"Corn Syrup", "24 oz.", 13.7, {14.7, 0, 0.5, 74, 0, 0, 0, 5, 0}},
|
||||
{"Molasses", "18 oz.", 13.6, {9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0}},
|
||||
{"Strawberry Preserves",
|
||||
"1 lb.",
|
||||
20.5,
|
||||
{6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0}}};
|
||||
{ "Wheat Flour (Enriched)", "10 lb.", 36,
|
||||
{ 44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0 } },
|
||||
{ "Macaroni", "1 lb.", 14.1, { 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0 } },
|
||||
{ "Wheat Cereal (Enriched)", "28 oz.", 24.2,
|
||||
{ 11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0 } },
|
||||
{ "Corn Flakes", "8 oz.", 7.1,
|
||||
{ 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0 } },
|
||||
{ "Corn Meal", "1 lb.", 4.6,
|
||||
{ 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0 } },
|
||||
{ "Hominy Grits", "24 oz.", 8.5,
|
||||
{ 28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0 } },
|
||||
{ "Rice", "1 lb.", 7.5, { 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0 } },
|
||||
{ "Rolled Oats", "1 lb.", 7.1,
|
||||
{ 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0 } },
|
||||
{ "White Bread (Enriched)", "1 lb.", 7.9,
|
||||
{ 15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0 } },
|
||||
{ "Whole Wheat Bread", "1 lb.", 9.1,
|
||||
{ 12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0 } },
|
||||
{ "Rye Bread", "1 lb.", 9.1, { 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0 } },
|
||||
{ "Pound Cake", "1 lb.", 24.8, { 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0 } },
|
||||
{ "Soda Crackers", "1 lb.", 15.1, { 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0 } },
|
||||
{ "Milk", "1 qt.", 11, { 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177 } },
|
||||
{ "Evaporated Milk (can)", "14.5 oz.", 6.7,
|
||||
{ 8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60 } },
|
||||
{ "Butter", "1 lb.", 30.8, { 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0 } },
|
||||
{ "Oleomargarine", "1 lb.", 16.1,
|
||||
{ 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0 } },
|
||||
{ "Eggs", "1 doz.", 32.6, { 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0 } },
|
||||
{ "Cheese (Cheddar)", "1 lb.", 24.2,
|
||||
{ 7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0 } },
|
||||
{ "Cream", "1/2 pt.", 14.1, { 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17 } },
|
||||
{ "Peanut Butter", "1 lb.", 17.9,
|
||||
{ 15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0 } },
|
||||
{ "Mayonnaise", "1/2 pt.", 16.7, { 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0 } },
|
||||
{ "Crisco", "1 lb.", 20.3, { 20.1, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
{ "Lard", "1 lb.", 9.8, { 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0 } },
|
||||
{ "Sirloin Steak", "1 lb.", 39.6,
|
||||
{ 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0 } },
|
||||
{ "Round Steak", "1 lb.", 36.4,
|
||||
{ 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0 } },
|
||||
{ "Rib Roast", "1 lb.", 29.2, { 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0 } },
|
||||
{ "Chuck Roast", "1 lb.", 22.6, { 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0 } },
|
||||
{ "Plate", "1 lb.", 14.6, { 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0 } },
|
||||
{ "Liver (Beef)", "1 lb.", 26.8,
|
||||
{ 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525 } },
|
||||
{ "Leg of Lamb", "1 lb.", 27.6, { 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0 } },
|
||||
{ "Lamb Chops (Rib)", "1 lb.", 36.6,
|
||||
{ 3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0 } },
|
||||
{ "Pork Chops", "1 lb.", 30.7, { 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0 } },
|
||||
{ "Pork Loin Roast", "1 lb.", 24.2,
|
||||
{ 4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0 } },
|
||||
{ "Bacon", "1 lb.", 25.6, { 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0 } },
|
||||
{ "Ham, smoked", "1 lb.", 27.4, { 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0 } },
|
||||
{ "Salt Pork", "1 lb.", 16, { 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0 } },
|
||||
{ "Roasting Chicken", "1 lb.", 30.3,
|
||||
{ 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46 } },
|
||||
{ "Veal Cutlets", "1 lb.", 42.3,
|
||||
{ 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0 } },
|
||||
{ "Salmon, Pink (can)", "16 oz.", 13,
|
||||
{ 5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0 } },
|
||||
{ "Apples", "1 lb.", 4.4, { 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544 } },
|
||||
{ "Bananas", "1 lb.", 6.1, { 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498 } },
|
||||
{ "Lemons", "1 doz.", 26, { 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952 } },
|
||||
{ "Oranges", "1 doz.", 30.9,
|
||||
{ 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998 } },
|
||||
{ "Green Beans", "1 lb.", 7.1,
|
||||
{ 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862 } },
|
||||
{ "Cabbage", "1 lb.", 3.7, { 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369 } },
|
||||
{ "Carrots", "1 bunch", 4.7,
|
||||
{ 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608 } },
|
||||
{ "Celery", "1 stalk", 7.3, { 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313 } },
|
||||
{ "Lettuce", "1 head", 8.2,
|
||||
{ 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449 } },
|
||||
{ "Onions", "1 lb.", 3.6, { 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184 } },
|
||||
{ "Potatoes", "15 lb.", 34,
|
||||
{ 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522 } },
|
||||
{ "Spinach", "1 lb.", 8.1,
|
||||
{ 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755 } },
|
||||
{ "Sweet Potatoes", "1 lb.", 5.1,
|
||||
{ 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912 } },
|
||||
{ "Peaches (can)", "No. 2 1/2", 16.8,
|
||||
{ 3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196 } },
|
||||
{ "Pears (can)", "No. 2 1/2", 20.4,
|
||||
{ 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81 } },
|
||||
{ "Pineapple (can)", "No. 2 1/2", 21.3,
|
||||
{ 2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399 } },
|
||||
{ "Asparagus (can)", "No. 2", 27.7,
|
||||
{ 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272 } },
|
||||
{ "Green Beans (can)", "No. 2", 10,
|
||||
{ 1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431 } },
|
||||
{ "Pork and Beans (can)", "16 oz.", 7.1,
|
||||
{ 7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0 } },
|
||||
{ "Corn (can)", "No. 2", 10.4,
|
||||
{ 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218 } },
|
||||
{ "Peas (can)", "No. 2", 13.8,
|
||||
{ 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370 } },
|
||||
{ "Tomatoes (can)", "No. 2", 8.6,
|
||||
{ 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253 } },
|
||||
{ "Tomato Soup (can)", "10 1/2 oz.", 7.6,
|
||||
{ 1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862 } },
|
||||
{ "Peaches, Dried", "1 lb.", 15.7,
|
||||
{ 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57 } },
|
||||
{ "Prunes, Dried", "1 lb.", 9,
|
||||
{ 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257 } },
|
||||
{ "Raisins, Dried", "15 oz.", 9.4,
|
||||
{ 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136 } },
|
||||
{ "Peas, Dried", "1 lb.", 7.9,
|
||||
{ 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0 } },
|
||||
{ "Lima Beans, Dried", "1 lb.", 8.9,
|
||||
{ 17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0 } },
|
||||
{ "Navy Beans, Dried", "1 lb.", 5.9,
|
||||
{ 26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0 } },
|
||||
{ "Coffee", "1 lb.", 22.4, { 0, 0, 0, 0, 0, 4, 5.1, 50, 0 } },
|
||||
{ "Tea", "1/4 lb.", 17.4, { 0, 0, 0, 0, 0, 0, 2.3, 42, 0 } },
|
||||
{ "Cocoa", "8 oz.", 8.6, { 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0 } },
|
||||
{ "Chocolate", "8 oz.", 16.2, { 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0 } },
|
||||
{ "Sugar", "10 lb.", 51.7, { 34.9, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
{ "Corn Syrup", "24 oz.", 13.7, { 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0 } },
|
||||
{ "Molasses", "18 oz.", 13.6, { 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0 } },
|
||||
{ "Strawberry Preserves", "1 lb.", 20.5,
|
||||
{ 6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0 } }
|
||||
};
|
||||
|
||||
// Instantiate the solver
|
||||
MPSolver solver("StiglerDietExample", MPSolver::GLOP_LINEAR_PROGRAMMING);
|
||||
|
||||
// Declare an array to hold our nutritional data.
|
||||
std::vector<MPVariable*> food;
|
||||
std::vector<MPVariable *> food;
|
||||
|
||||
// Objective: minimize the sum of (price-normalized) foods.
|
||||
MPObjective* const objective = solver.MutableObjective();
|
||||
MPObjective *const objective = solver.MutableObjective();
|
||||
const double infinity = solver.infinity();
|
||||
for (const Commodity& commodity : data) {
|
||||
for (const Commodity &commodity : data) {
|
||||
food.push_back(solver.MakeNumVar(0.0, infinity, commodity.name));
|
||||
objective->SetCoefficient(food.back(), 1);
|
||||
}
|
||||
objective->SetMinimization();
|
||||
|
||||
// Create the constraints, one per nutrient.
|
||||
std::vector<MPConstraint*> constraints;
|
||||
std::vector<MPConstraint *> constraints;
|
||||
for (std::size_t i = 0; i < nutrients.size(); ++i) {
|
||||
constraints.push_back(
|
||||
solver.MakeRowConstraint(nutrients[i].second, infinity));
|
||||
@@ -290,11 +233,11 @@ void RunStiglerDietExample() {
|
||||
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
|
||||
LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations";
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
absl::GetFlag(FLAGS_logtostderr) = 1;
|
||||
operations_research::RunStiglerDietExample();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
// No attempt is made to force integrality.
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring> // strlen
|
||||
#include <cstring> // strlen
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -77,170 +77,162 @@ struct Instance {
|
||||
int max_boxes;
|
||||
int width;
|
||||
int height;
|
||||
const char* grid;
|
||||
const char *grid;
|
||||
};
|
||||
|
||||
Instance kInstances[] = {{4, 22, 6,
|
||||
"..@@@@@..............."
|
||||
"..@@@@@@........@@@..."
|
||||
".....@@@@@......@@@..."
|
||||
".......@@@@@@@@@@@@..."
|
||||
".........@@@@@........"
|
||||
".........@@@@@........"},
|
||||
{3, 13, 10,
|
||||
"............."
|
||||
"............."
|
||||
"............."
|
||||
"...@@@@......"
|
||||
"...@@@@......"
|
||||
"...@@@@......"
|
||||
".......@@@..."
|
||||
".......@@@..."
|
||||
".......@@@..."
|
||||
"............."},
|
||||
{4, 13, 9,
|
||||
"............."
|
||||
"..@.@.@......"
|
||||
"...@.@.@....."
|
||||
"..@.@.@......"
|
||||
"..@.@.@......"
|
||||
"...@.@.@....."
|
||||
"....@.@......"
|
||||
"..........@@@"
|
||||
"..........@@@"},
|
||||
{4, 13, 9,
|
||||
".........@..."
|
||||
".........@..."
|
||||
"@@@@@@@@@@..."
|
||||
"..@......@..."
|
||||
"..@......@..."
|
||||
"..@......@..."
|
||||
"..@@@@@@@@@@@"
|
||||
"..@.........."
|
||||
"..@.........."},
|
||||
{7, 25, 14,
|
||||
"........................."
|
||||
"..@@@@@@@@@@@@@@@@@@@@..."
|
||||
"..@@@@@@@@@@@@@@@@@@@@..."
|
||||
"..@@.................@..."
|
||||
"..@@.................@..."
|
||||
"..@@.......@@@.......@.@."
|
||||
"..@@.......@@@.......@..."
|
||||
"..@@...@@@@@@@@@@@@@@@..."
|
||||
"..@@...@@@@@@@@@@@@@@@..."
|
||||
"..@@.......@@@.......@..."
|
||||
"..@@.......@@@.......@..."
|
||||
"..@@.................@..."
|
||||
"..@@.................@..."
|
||||
"........................."},
|
||||
{6, 25, 16,
|
||||
"........................."
|
||||
"......@@@@@@@@@@@@@......"
|
||||
"........................."
|
||||
".....@..........@........"
|
||||
".....@..........@........"
|
||||
".....@......@............"
|
||||
".....@......@.@@@@@@@...."
|
||||
".....@......@............"
|
||||
".....@......@.@@@@@@@...."
|
||||
".....@......@............"
|
||||
"....@@@@....@............"
|
||||
"....@@@@....@............"
|
||||
"..@@@@@@....@............"
|
||||
"..@@@.......@............"
|
||||
"..@@@...................."
|
||||
"..@@@@@@@@@@@@@@@@@@@@@@@"},
|
||||
{5, 40, 18,
|
||||
"........................................"
|
||||
"........................................"
|
||||
"...@@@@@@..............................."
|
||||
"...@@@@@@..............................."
|
||||
"...@@@@@@..............................."
|
||||
"...@@@@@@.........@@@@@@@@@@............"
|
||||
"...@@@@@@.........@@@@@@@@@@............"
|
||||
"..................@@@@@@@@@@............"
|
||||
"..................@@@@@@@@@@............"
|
||||
".............@@@@@@@@@@@@@@@............"
|
||||
".............@@@@@@@@@@@@@@@............"
|
||||
"........@@@@@@@@@@@@...................."
|
||||
"........@@@@@@@@@@@@...................."
|
||||
"........@@@@@@.........................."
|
||||
"........@@@@@@.........................."
|
||||
"........................................"
|
||||
"........................................"
|
||||
"........................................"},
|
||||
{8, 40, 18,
|
||||
"........................................"
|
||||
"..@@.@.@.@.............................."
|
||||
"..@@.@.@.@...............@.............."
|
||||
"..@@.@.@.@............@................."
|
||||
"..@@.@.@.@.............................."
|
||||
"..@@.@.@.@.................@............"
|
||||
"..@@.@..................@..............."
|
||||
"..@@.@.................................."
|
||||
"..@@.@.................................."
|
||||
"..@@.@................@@@@.............."
|
||||
"..@@.@..............@@@@@@@@............"
|
||||
"..@@.@.................................."
|
||||
"..@@.@..............@@@@@@@@............"
|
||||
"..@@.@.................................."
|
||||
"..@@.@................@@@@.............."
|
||||
"..@@.@.................................."
|
||||
"..@@.@.................................."
|
||||
"........................................"},
|
||||
{10, 40, 19,
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@...........@@@@@@@@@@@............."
|
||||
"@@@@@...........@@@@@@@@@@@............."
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"...............@@@@@@@@@@@@@@..........."
|
||||
"...............@@@@@@@@@@@@@@..........."
|
||||
".......@@@@@@@@@@@@@@@@@@@@@@..........."
|
||||
".......@@@@@@@@@........................"
|
||||
"........................................"
|
||||
"........................................"
|
||||
"........................................"},
|
||||
{10, 40, 25,
|
||||
"...................@...................."
|
||||
"...............@@@@@@@@@................"
|
||||
"............@@@.........@@@............."
|
||||
"...........@...............@............"
|
||||
"..........@.................@..........."
|
||||
".........@...................@.........."
|
||||
".........@...................@.........."
|
||||
".........@.....@@......@@....@.........."
|
||||
"........@.....@@@@....@@@@....@........."
|
||||
"........@.....................@........."
|
||||
"........@.....................@........."
|
||||
"........@..........@@.........@........."
|
||||
".......@@..........@@.........@@........"
|
||||
"........@.....................@........."
|
||||
"........@.....................@........."
|
||||
"........@......@@@@@@@@@......@........."
|
||||
"........@......@@@@@@@@@......@........."
|
||||
".........@...................@.........."
|
||||
".........@...................@.........."
|
||||
".........@...................@.........."
|
||||
"..........@.................@..........."
|
||||
"...........@...............@............"
|
||||
"............@@@.........@@@............."
|
||||
"...............@@@@@@@@@................"
|
||||
"...................@...................."}};
|
||||
Instance kInstances[] = {
|
||||
{ 4, 22, 6, "..@@@@@..............."
|
||||
"..@@@@@@........@@@..."
|
||||
".....@@@@@......@@@..."
|
||||
".......@@@@@@@@@@@@..."
|
||||
".........@@@@@........"
|
||||
".........@@@@@........" },
|
||||
{ 3, 13, 10, "............."
|
||||
"............."
|
||||
"............."
|
||||
"...@@@@......"
|
||||
"...@@@@......"
|
||||
"...@@@@......"
|
||||
".......@@@..."
|
||||
".......@@@..."
|
||||
".......@@@..."
|
||||
"............." },
|
||||
{ 4, 13, 9, "............."
|
||||
"..@.@.@......"
|
||||
"...@.@.@....."
|
||||
"..@.@.@......"
|
||||
"..@.@.@......"
|
||||
"...@.@.@....."
|
||||
"....@.@......"
|
||||
"..........@@@"
|
||||
"..........@@@" },
|
||||
{ 4, 13, 9, ".........@..."
|
||||
".........@..."
|
||||
"@@@@@@@@@@..."
|
||||
"..@......@..."
|
||||
"..@......@..."
|
||||
"..@......@..."
|
||||
"..@@@@@@@@@@@"
|
||||
"..@.........."
|
||||
"..@.........." },
|
||||
{ 7, 25, 14, "........................."
|
||||
"..@@@@@@@@@@@@@@@@@@@@..."
|
||||
"..@@@@@@@@@@@@@@@@@@@@..."
|
||||
"..@@.................@..."
|
||||
"..@@.................@..."
|
||||
"..@@.......@@@.......@.@."
|
||||
"..@@.......@@@.......@..."
|
||||
"..@@...@@@@@@@@@@@@@@@..."
|
||||
"..@@...@@@@@@@@@@@@@@@..."
|
||||
"..@@.......@@@.......@..."
|
||||
"..@@.......@@@.......@..."
|
||||
"..@@.................@..."
|
||||
"..@@.................@..."
|
||||
"........................." },
|
||||
{ 6, 25, 16, "........................."
|
||||
"......@@@@@@@@@@@@@......"
|
||||
"........................."
|
||||
".....@..........@........"
|
||||
".....@..........@........"
|
||||
".....@......@............"
|
||||
".....@......@.@@@@@@@...."
|
||||
".....@......@............"
|
||||
".....@......@.@@@@@@@...."
|
||||
".....@......@............"
|
||||
"....@@@@....@............"
|
||||
"....@@@@....@............"
|
||||
"..@@@@@@....@............"
|
||||
"..@@@.......@............"
|
||||
"..@@@...................."
|
||||
"..@@@@@@@@@@@@@@@@@@@@@@@" },
|
||||
{ 5, 40, 18, "........................................"
|
||||
"........................................"
|
||||
"...@@@@@@..............................."
|
||||
"...@@@@@@..............................."
|
||||
"...@@@@@@..............................."
|
||||
"...@@@@@@.........@@@@@@@@@@............"
|
||||
"...@@@@@@.........@@@@@@@@@@............"
|
||||
"..................@@@@@@@@@@............"
|
||||
"..................@@@@@@@@@@............"
|
||||
".............@@@@@@@@@@@@@@@............"
|
||||
".............@@@@@@@@@@@@@@@............"
|
||||
"........@@@@@@@@@@@@...................."
|
||||
"........@@@@@@@@@@@@...................."
|
||||
"........@@@@@@.........................."
|
||||
"........@@@@@@.........................."
|
||||
"........................................"
|
||||
"........................................"
|
||||
"........................................" },
|
||||
{ 8, 40, 18, "........................................"
|
||||
"..@@.@.@.@.............................."
|
||||
"..@@.@.@.@...............@.............."
|
||||
"..@@.@.@.@............@................."
|
||||
"..@@.@.@.@.............................."
|
||||
"..@@.@.@.@.................@............"
|
||||
"..@@.@..................@..............."
|
||||
"..@@.@.................................."
|
||||
"..@@.@.................................."
|
||||
"..@@.@................@@@@.............."
|
||||
"..@@.@..............@@@@@@@@............"
|
||||
"..@@.@.................................."
|
||||
"..@@.@..............@@@@@@@@............"
|
||||
"..@@.@.................................."
|
||||
"..@@.@................@@@@.............."
|
||||
"..@@.@.................................."
|
||||
"..@@.@.................................."
|
||||
"........................................" },
|
||||
{ 10, 40, 19, "@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@..................................."
|
||||
"@@@@@...........@@@@@@@@@@@............."
|
||||
"@@@@@...........@@@@@@@@@@@............."
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"....................@@@@................"
|
||||
"...............@@@@@@@@@@@@@@..........."
|
||||
"...............@@@@@@@@@@@@@@..........."
|
||||
".......@@@@@@@@@@@@@@@@@@@@@@..........."
|
||||
".......@@@@@@@@@........................"
|
||||
"........................................"
|
||||
"........................................"
|
||||
"........................................" },
|
||||
{ 10, 40, 25, "...................@...................."
|
||||
"...............@@@@@@@@@................"
|
||||
"............@@@.........@@@............."
|
||||
"...........@...............@............"
|
||||
"..........@.................@..........."
|
||||
".........@...................@.........."
|
||||
".........@...................@.........."
|
||||
".........@.....@@......@@....@.........."
|
||||
"........@.....@@@@....@@@@....@........."
|
||||
"........@.....................@........."
|
||||
"........@.....................@........."
|
||||
"........@..........@@.........@........."
|
||||
".......@@..........@@.........@@........"
|
||||
"........@.....................@........."
|
||||
"........@.....................@........."
|
||||
"........@......@@@@@@@@@......@........."
|
||||
"........@......@@@@@@@@@......@........."
|
||||
".........@...................@.........."
|
||||
".........@...................@.........."
|
||||
".........@...................@.........."
|
||||
"..........@.................@..........."
|
||||
"...........@...............@............"
|
||||
"............@@@.........@@@............."
|
||||
"...............@@@@@@@@@................"
|
||||
"...................@...................." }
|
||||
};
|
||||
|
||||
const int kInstanceCount = 10;
|
||||
|
||||
// ---------- Box ---------
|
||||
|
||||
class Box {
|
||||
public:
|
||||
public:
|
||||
static const int kAreaCost = 1;
|
||||
static const int kFixedCost = 10;
|
||||
|
||||
@@ -257,11 +249,14 @@ class Box {
|
||||
int y_max() const { return y_max_; }
|
||||
|
||||
// Lexicographic order
|
||||
int Compare(const Box& box) const {
|
||||
int Compare(const Box &box) const {
|
||||
int c;
|
||||
if ((c = (x_min() - box.x_min())) != 0) return c;
|
||||
if ((c = (x_max() - box.x_max())) != 0) return c;
|
||||
if ((c = (y_min() - box.y_min())) != 0) return c;
|
||||
if ((c = (x_min() - box.x_min())) != 0)
|
||||
return c;
|
||||
if ((c = (x_max() - box.x_max())) != 0)
|
||||
return c;
|
||||
if ((c = (y_min() - box.y_min())) != 0)
|
||||
return c;
|
||||
return y_max() - box.y_max();
|
||||
}
|
||||
|
||||
@@ -279,7 +274,7 @@ class Box {
|
||||
y_max(), Cost());
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int x_min_;
|
||||
int x_max_;
|
||||
int y_min_;
|
||||
@@ -287,7 +282,7 @@ class Box {
|
||||
};
|
||||
|
||||
struct BoxLessThan {
|
||||
bool operator()(const Box& b1, const Box& b2) const {
|
||||
bool operator()(const Box &b1, const Box &b2) const {
|
||||
return b1.Compare(b2) < 0;
|
||||
}
|
||||
};
|
||||
@@ -295,16 +290,13 @@ struct BoxLessThan {
|
||||
// ---------- Covering Problem ---------
|
||||
|
||||
class CoveringProblem {
|
||||
public:
|
||||
public:
|
||||
// Grid is a row-major string of length width*height with '@' for an
|
||||
// occupied cell (strawberry) and '.' for an empty cell. Solver is
|
||||
// not owned.
|
||||
CoveringProblem(MPSolver* const solver, const Instance& instance)
|
||||
: solver_(solver),
|
||||
max_boxes_(instance.max_boxes),
|
||||
width_(instance.width),
|
||||
height_(instance.height),
|
||||
grid_(instance.grid) {}
|
||||
CoveringProblem(MPSolver *const solver, const Instance &instance)
|
||||
: solver_(solver), max_boxes_(instance.max_boxes), width_(instance.width),
|
||||
height_(instance.height), grid_(instance.grid) {}
|
||||
|
||||
// Constructs initial variables and constraints. Initial column
|
||||
// (box) covers entire grid, ensuring feasibility.
|
||||
@@ -316,13 +308,14 @@ class CoveringProblem {
|
||||
}
|
||||
for (int i = 0; i < size; ++i) {
|
||||
char c = grid_[i];
|
||||
if ((c != '@') && (c != '.')) return false;
|
||||
if ((c != '@') && (c != '.'))
|
||||
return false;
|
||||
}
|
||||
|
||||
AddCellConstraints(); // sum for every cell is <=1 or =1
|
||||
AddMaxBoxesConstraint(); // sum of box variables is <= max_boxes()
|
||||
if (!FLAGS_colgen_complete) {
|
||||
AddBox(Box(0, width() - 1, 0, height() - 1)); // grid-covering box
|
||||
AddCellConstraints(); // sum for every cell is <=1 or =1
|
||||
AddMaxBoxesConstraint(); // sum of box variables is <= max_boxes()
|
||||
if (!absl::GetFlag(FLAGS_colgen_complete)) {
|
||||
AddBox(Box(0, width() - 1, 0, height() - 1)); // grid-covering box
|
||||
} else {
|
||||
// Naive alternative to column generation - generate all boxes;
|
||||
// works fine for smaller problems, too slow for big.
|
||||
@@ -366,7 +359,7 @@ class CoveringProblem {
|
||||
// each, so pre-calculate sums of cell duals for all rectangles with
|
||||
// upper-left at 0, 0, and use these to calculate the sum in
|
||||
// constant time using the standard inclusion-exclusion trick.
|
||||
double GetOptimalBox(Box* const target) {
|
||||
double GetOptimalBox(Box *const target) {
|
||||
// Cost change threshold for new Box
|
||||
const double kCostChangeThreshold = -.01;
|
||||
|
||||
@@ -383,7 +376,7 @@ class CoveringProblem {
|
||||
for (int x_min = 0; x_min < width(); ++x_min) {
|
||||
for (int x_max = x_min; x_max < width(); ++x_max) {
|
||||
Box box(x_min, x_max, y_min, y_max);
|
||||
const double cell_coverage_dual = // inclusion-exclusion
|
||||
const double cell_coverage_dual = // inclusion-exclusion
|
||||
+zero_access(upper_left_sums, x_max, y_max) -
|
||||
zero_access(upper_left_sums, x_max, y_min - 1) -
|
||||
zero_access(upper_left_sums, x_min - 1, y_max) +
|
||||
@@ -426,9 +419,9 @@ class CoveringProblem {
|
||||
|
||||
// Add continuous [0,1] box variable with box.Cost() as objective
|
||||
// coefficient. Add to cell constraint of all enclosed cells.
|
||||
MPVariable* AddBox(const Box& box) {
|
||||
MPVariable *AddBox(const Box &box) {
|
||||
CHECK(boxes_.find(box) == boxes_.end());
|
||||
MPVariable* const var = solver_->MakeNumVar(0., 1., box.DebugString());
|
||||
MPVariable *const var = solver_->MakeNumVar(0., 1., box.DebugString());
|
||||
solver_->MutableObjective()->SetCoefficient(var, box.Cost());
|
||||
max_boxes_constraint_->SetCoefficient(var, 1.0);
|
||||
for (int y = box.y_min(); y <= box.y_max(); ++y) {
|
||||
@@ -462,7 +455,7 @@ class CoveringProblem {
|
||||
std::unique_ptr<char[]> display(new char[(width_ + 1) * height_ + 1]);
|
||||
for (int y = 0; y < height_; ++y) {
|
||||
memcpy(display.get() + y * (width_ + 1), grid_ + width_ * y,
|
||||
width_); // Copy the original line.
|
||||
width_); // Copy the original line.
|
||||
display[y * (width_ + 1) + width_] = '\n';
|
||||
}
|
||||
display[height_ * (width_ + 1)] = '\0';
|
||||
@@ -475,7 +468,7 @@ class CoveringProblem {
|
||||
active_box_index++;
|
||||
absl::StrAppendFormat(&output, "%c: box %s with value %f\n",
|
||||
box_character, i->first.DebugString(), value);
|
||||
const Box& box = i->first;
|
||||
const Box &box = i->first;
|
||||
for (int x = box.x_min(); x <= box.x_max(); ++x) {
|
||||
for (int y = box.y_min(); y <= box.y_max(); ++y) {
|
||||
display[x + y * (width_ + 1)] = box_character;
|
||||
@@ -487,10 +480,10 @@ class CoveringProblem {
|
||||
return output;
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
int index(int x, int y) const { return width_ * y + x; }
|
||||
MPConstraint* cell(int x, int y) { return cells_[index(x, y)]; }
|
||||
const MPConstraint* cell(int x, int y) const { return cells_[index(x, y)]; }
|
||||
MPConstraint *cell(int x, int y) { return cells_[index(x, y)]; }
|
||||
const MPConstraint *cell(int x, int y) const { return cells_[index(x, y)]; }
|
||||
|
||||
// Adds constraints that every cell is covered at most once, exactly
|
||||
// once if occupied.
|
||||
@@ -510,7 +503,7 @@ class CoveringProblem {
|
||||
}
|
||||
|
||||
// Gets 2d array element, returning 0 if out-of-bounds.
|
||||
double zero_access(const std::vector<double>& array, int x, int y) const {
|
||||
double zero_access(const std::vector<double> &array, int x, int y) const {
|
||||
if (x < 0 || y < 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -519,7 +512,7 @@ class CoveringProblem {
|
||||
|
||||
// Precomputes the sum of reduced costs for every upper-left
|
||||
// rectangle.
|
||||
void ComputeUpperLeftSums(std::vector<double>* upper_left_sums) const {
|
||||
void ComputeUpperLeftSums(std::vector<double> *upper_left_sums) const {
|
||||
for (int y = 0; y < height(); ++y) {
|
||||
for (int x = 0; x < width(); ++x) {
|
||||
upper_left_sums->operator[](index(x, y)) =
|
||||
@@ -530,22 +523,22 @@ class CoveringProblem {
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map<Box, MPVariable*, BoxLessThan> BoxTable;
|
||||
MPSolver* const solver_; // not owned
|
||||
typedef std::map<Box, MPVariable *, BoxLessThan> BoxTable;
|
||||
MPSolver *const solver_; // not owned
|
||||
const int max_boxes_;
|
||||
const int width_;
|
||||
const int height_;
|
||||
const char* const grid_;
|
||||
std::vector<MPConstraint*> cells_;
|
||||
const char *const grid_;
|
||||
std::vector<MPConstraint *> cells_;
|
||||
BoxTable boxes_;
|
||||
MPConstraint* max_boxes_constraint_;
|
||||
MPConstraint *max_boxes_constraint_;
|
||||
};
|
||||
|
||||
// ---------- Main Solve Method ----------
|
||||
|
||||
// Solves iteratively using delayed column generation, up to maximum
|
||||
// number of steps.
|
||||
void SolveInstance(const Instance& instance,
|
||||
void SolveInstance(const Instance &instance,
|
||||
MPSolver::OptimizationProblemType solver_type) {
|
||||
// Prepares the solver.
|
||||
MPSolver solver("ColumnGeneration", solver_type);
|
||||
@@ -558,14 +551,14 @@ void SolveInstance(const Instance& instance,
|
||||
LOG(INFO) << "Initial problem:\n" << problem.PrintGrid();
|
||||
|
||||
int step_number = 0;
|
||||
while (step_number < FLAGS_colgen_max_iterations) {
|
||||
if (FLAGS_colgen_verbose) {
|
||||
while (step_number < absl::GetFlag(FLAGS_colgen_max_iterations)) {
|
||||
if (absl::GetFlag(FLAGS_colgen_verbose)) {
|
||||
LOG(INFO) << "Step number " << step_number;
|
||||
}
|
||||
|
||||
// Solve with existing columns.
|
||||
CHECK_EQ(MPSolver::OPTIMAL, solver.Solve());
|
||||
if (FLAGS_colgen_verbose) {
|
||||
if (absl::GetFlag(FLAGS_colgen_verbose)) {
|
||||
LOG(INFO) << problem.PrintCovering();
|
||||
}
|
||||
|
||||
@@ -577,7 +570,7 @@ void SolveInstance(const Instance& instance,
|
||||
}
|
||||
|
||||
// Add new column to problem.
|
||||
if (FLAGS_colgen_verbose) {
|
||||
if (absl::GetFlag(FLAGS_colgen_verbose)) {
|
||||
LOG(INFO) << "Adding " << box.DebugString()
|
||||
<< ", reduced_cost =" << reduced_cost;
|
||||
}
|
||||
@@ -586,7 +579,7 @@ void SolveInstance(const Instance& instance,
|
||||
++step_number;
|
||||
}
|
||||
|
||||
if (step_number >= FLAGS_colgen_max_iterations) {
|
||||
if (step_number >= absl::GetFlag(FLAGS_colgen_max_iterations)) {
|
||||
// Solve one last time with all generated columns.
|
||||
CHECK_EQ(MPSolver::OPTIMAL, solver.Solve());
|
||||
}
|
||||
@@ -594,9 +587,9 @@ void SolveInstance(const Instance& instance,
|
||||
LOG(INFO) << step_number << " columns added";
|
||||
LOG(INFO) << "Final coverage: " << problem.PrintCovering();
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
std::string usage = "column_generation\n";
|
||||
usage += " --colgen_verbose print verbosely\n";
|
||||
usage += " --colgen_max_iterations <n> max columns to generate\n";
|
||||
@@ -607,48 +600,50 @@ int main(int argc, char** argv) {
|
||||
operations_research::MPSolver::OptimizationProblemType solver_type;
|
||||
bool found = false;
|
||||
#if defined(USE_CLP)
|
||||
if (FLAGS_colgen_solver == "clp") {
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "clp") {
|
||||
solver_type = operations_research::MPSolver::CLP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif // USE_CLP
|
||||
#endif // USE_CLP
|
||||
#if defined(USE_GLOP)
|
||||
if (FLAGS_colgen_solver == "glop") {
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "glop") {
|
||||
solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif // USE_GLOP
|
||||
#endif // USE_GLOP
|
||||
#if defined(USE_XPRESS)
|
||||
if (FLAGS_colgen_solver == "xpress") {
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "xpress") {
|
||||
solver_type = operations_research::MPSolver::XPRESS_LINEAR_PROGRAMMING;
|
||||
// solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_CPLEX)
|
||||
if (FLAGS_colgen_solver == "cplex") {
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "cplex") {
|
||||
solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif
|
||||
if (!found) {
|
||||
LOG(ERROR) << "Unknown solver " << FLAGS_colgen_solver;
|
||||
LOG(ERROR) << "Unknown solver " << absl::GetFlag(FLAGS_colgen_solver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Chosen solver: " << FLAGS_colgen_solver << std::endl;
|
||||
LOG(INFO) << "Chosen solver: " << absl::GetFlag(FLAGS_colgen_solver)
|
||||
<< std::endl;
|
||||
|
||||
if (FLAGS_colgen_instance == -1) {
|
||||
if (absl::GetFlag(FLAGS_colgen_instance) == -1) {
|
||||
for (int i = 0; i < operations_research::kInstanceCount; ++i) {
|
||||
const operations_research::Instance& instance =
|
||||
const operations_research::Instance &instance =
|
||||
operations_research::kInstances[i];
|
||||
operations_research::SolveInstance(instance, solver_type);
|
||||
}
|
||||
} else {
|
||||
CHECK_GE(FLAGS_colgen_instance, 0);
|
||||
CHECK_LT(FLAGS_colgen_instance, operations_research::kInstanceCount);
|
||||
const operations_research::Instance& instance =
|
||||
operations_research::kInstances[FLAGS_colgen_instance];
|
||||
CHECK_GE(absl::GetFlag(FLAGS_colgen_instance), 0);
|
||||
CHECK_LT(absl::GetFlag(FLAGS_colgen_instance),
|
||||
operations_research::kInstanceCount);
|
||||
const operations_research::Instance &instance =
|
||||
operations_research::kInstances[absl::GetFlag(FLAGS_colgen_instance)];
|
||||
operations_research::SolveInstance(instance, solver_type);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -39,17 +39,22 @@ DEFINE_double(fix_cost, 5000, "Cost of opening a facility.");
|
||||
namespace operations_research {
|
||||
|
||||
typedef struct {
|
||||
double x{0};
|
||||
double y{0};
|
||||
double x { 0 }
|
||||
;
|
||||
double y { 0 }
|
||||
;
|
||||
} Location;
|
||||
|
||||
typedef struct {
|
||||
int f{-1};
|
||||
int c{-1};
|
||||
MPVariable* x{nullptr};
|
||||
int f { -1 }
|
||||
;
|
||||
int c { -1 }
|
||||
;
|
||||
MPVariable *x { nullptr }
|
||||
;
|
||||
} Edge;
|
||||
|
||||
static double Distance(const Location& src, const Location& dst) {
|
||||
static double Distance(const Location &src, const Location &dst) {
|
||||
return sqrt((src.x - dst.x) * (src.x - dst.x) +
|
||||
(src.y - dst.y) * (src.y - dst.y));
|
||||
}
|
||||
@@ -91,15 +96,16 @@ static void UncapacitatedFacilityLocation(
|
||||
// Distance(f,c) <= kMaxDistance
|
||||
MPSolver solver("UncapacitatedFacilityLocation", optimization_problem_type);
|
||||
const double infinity = solver.infinity();
|
||||
MPObjective* objective = solver.MutableObjective();
|
||||
MPObjective *objective = solver.MutableObjective();
|
||||
objective->SetMinimization();
|
||||
|
||||
// Add binary facilities variables
|
||||
std::vector<MPVariable*> xf{};
|
||||
std::vector<MPVariable *> xf {}
|
||||
;
|
||||
for (int f = 0; f < facilities; ++f) {
|
||||
snprintf(name_buffer, kStrLen, "x[%d](%g,%g)", f, facility[f].x,
|
||||
facility[f].y);
|
||||
MPVariable* x = solver.MakeBoolVar(name_buffer);
|
||||
MPVariable *x = solver.MakeBoolVar(name_buffer);
|
||||
xf.push_back(x);
|
||||
objective->SetCoefficient(x, fix_cost);
|
||||
}
|
||||
@@ -109,13 +115,14 @@ static void UncapacitatedFacilityLocation(
|
||||
for (int c = 0; c < clients; ++c) {
|
||||
snprintf(name_buffer, kStrLen, "R-Client[%d](%g,%g)", c, client[c].x,
|
||||
client[c].y);
|
||||
MPConstraint* client_constraint =
|
||||
solver.MakeRowConstraint(/* lb */ 1,
|
||||
/* ub */ infinity, name_buffer);
|
||||
MPConstraint *client_constraint =
|
||||
solver.MakeRowConstraint(/* lb */ 1, /* ub */ infinity, name_buffer);
|
||||
for (int f = 0; f < facilities; ++f) {
|
||||
double distance = Distance(facility[f], client[c]);
|
||||
if (distance > kMaxDistance) continue;
|
||||
Edge edge{};
|
||||
if (distance > kMaxDistance)
|
||||
continue;
|
||||
Edge edge {}
|
||||
;
|
||||
snprintf(name_buffer, kStrLen, "x[%d,%d]", f, c);
|
||||
edge.x = solver.MakeNumVar(/* lb */ 0, /*ub */ 1, name_buffer);
|
||||
edge.f = f;
|
||||
@@ -126,18 +133,18 @@ static void UncapacitatedFacilityLocation(
|
||||
client_constraint->SetCoefficient(edge.x, 1);
|
||||
// add constraint (2)
|
||||
snprintf(name_buffer, kStrLen, "R-Edge[%d,%d]", f, c);
|
||||
MPConstraint* edge_constraint =
|
||||
solver.MakeRowConstraint(/* lb */ 0,
|
||||
/* ub */ infinity, name_buffer);
|
||||
MPConstraint *edge_constraint =
|
||||
solver.MakeRowConstraint(/* lb */ 0, /* ub */ infinity, name_buffer);
|
||||
edge_constraint->SetCoefficient(edge.x, -1);
|
||||
edge_constraint->SetCoefficient(xf[f], 1);
|
||||
}
|
||||
} // End adding all edge variables
|
||||
} // End adding all edge variables
|
||||
LOG(INFO) << "Number of variables = " << solver.NumVariables();
|
||||
LOG(INFO) << "Number of constraints = " << solver.NumConstraints();
|
||||
// display on screen LP if small enough
|
||||
if (clients <= 10 && facilities <= 10) {
|
||||
std::string lp_string{};
|
||||
std::string lp_string {}
|
||||
;
|
||||
solver.ExportModelAsLpFormat(/* obfuscate */ false, &lp_string);
|
||||
std::cout << "LP-Model:\n" << lp_string << std::endl;
|
||||
}
|
||||
@@ -151,15 +158,17 @@ static void UncapacitatedFacilityLocation(
|
||||
LOG(FATAL) << "The problem does not have an optimal solution!";
|
||||
} else {
|
||||
LOG(INFO) << "Optimal objective value = " << objective->Value();
|
||||
if (FLAGS_verbose) {
|
||||
std::vector<std::vector<int>> solution(facilities);
|
||||
for (auto& edge : edges) {
|
||||
if (edge.x->solution_value() < 0.5) continue;
|
||||
if (absl::GetFlag(FLAGS_verbose)) {
|
||||
std::vector<std::vector<int> > solution(facilities);
|
||||
for (auto &edge : edges) {
|
||||
if (edge.x->solution_value() < 0.5)
|
||||
continue;
|
||||
solution[edge.f].push_back(edge.c);
|
||||
}
|
||||
std::cout << "\tSolution:\n";
|
||||
for (int f = 0; f < facilities; ++f) {
|
||||
if (solution[f].size() < 1) continue;
|
||||
if (solution[f].size() < 1)
|
||||
continue;
|
||||
assert(xf[f]->solution_value() > 0.5);
|
||||
snprintf(name_buffer, kStrLen, "\t Facility[%d](%g,%g):", f,
|
||||
facility[f].x, facility[f].y);
|
||||
@@ -208,30 +217,34 @@ void RunAllExamples(int32 facilities, int32 clients, double fix_cost) {
|
||||
LOG(INFO) << "---- Integer programming example with Gurobi ----";
|
||||
UncapacitatedFacilityLocation(facilities, clients, fix_cost,
|
||||
MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif // USE_GUROBI
|
||||
#endif // USE_GUROBI
|
||||
#if defined(USE_CPLEX)
|
||||
LOG(INFO) << "---- Integer programming example with CPLEX ----";
|
||||
UncapacitatedFacilityLocation(facilities, clients, fix_cost,
|
||||
MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif // USE_CPLEX
|
||||
#endif // USE_CPLEX
|
||||
LOG(INFO) << "---- Integer programming example with CP-SAT ----";
|
||||
UncapacitatedFacilityLocation(facilities, clients, fix_cost,
|
||||
MPSolver::SAT_INTEGER_PROGRAMMING);
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
gflags::SetUsageMessage(
|
||||
std::string("This program solve a (randomly generated)\n") +
|
||||
std::string("Uncapacitated Facility Location Problem. Sample Usage:\n"));
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_facilities) << "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, FLAGS_clients) << "Specify a non-null client size.";
|
||||
CHECK_LT(0, FLAGS_fix_cost) << "Specify a non-null client size.";
|
||||
FLAGS_logtostderr = 1;
|
||||
operations_research::RunAllExamples(FLAGS_facilities, FLAGS_clients,
|
||||
FLAGS_fix_cost);
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_facilities))
|
||||
<< "Specify an instance size greater than 0.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_clients))
|
||||
<< "Specify a non-null client size.";
|
||||
CHECK_LT(0, absl::GetFlag(FLAGS_fix_cost))
|
||||
<< "Specify a non-null client size.";
|
||||
absl::GetFlag(FLAGS_logtostderr) = 1;
|
||||
operations_research::RunAllExamples(absl::GetFlag(FLAGS_facilities),
|
||||
absl::GetFlag(FLAGS_clients),
|
||||
absl::GetFlag(FLAGS_fix_cost));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,15 @@ void Solve() {
|
||||
const IntVar end_p2 = cp_model.NewIntVar(Domain(500, 1000));
|
||||
const IntervalVar p2 = cp_model.NewIntervalVar(start_p2, duration_p2, end_p2);
|
||||
|
||||
cp_model.AddEquality(LinearExpr::Sum({duration_p1, duration_p2}), 360);
|
||||
cp_model.AddEquality(LinearExpr::Sum({
|
||||
duration_p1, duration_p2
|
||||
}),
|
||||
360);
|
||||
cp_model.AddLessOrEqual(end_p1, start_p2);
|
||||
|
||||
cp_model.AddNoOverlap({ins, p1, p2});
|
||||
cp_model.AddNoOverlap({
|
||||
ins, p1, p2
|
||||
});
|
||||
|
||||
Model model;
|
||||
|
||||
@@ -42,7 +47,7 @@ void Solve() {
|
||||
|
||||
const int kSolutionLimit = 100;
|
||||
int num_solutions = 0;
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse & r) {
|
||||
LOG(INFO) << "Solution " << num_solutions;
|
||||
LOG(INFO) << " start_p1 = " << SolutionIntegerValue(r, start_p1);
|
||||
LOG(INFO) << " duration_p1 = " << SolutionIntegerValue(r, duration_p1);
|
||||
@@ -60,8 +65,8 @@ void Solve() {
|
||||
LOG(INFO) << "Number of solutions found: " << num_solutions;
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::Solve();
|
||||
|
||||
@@ -39,9 +39,9 @@ namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
// Solve a single machine problem with weighted tardiness cost.
|
||||
void Solve(const std::vector<int64>& durations,
|
||||
const std::vector<int64>& due_dates,
|
||||
const std::vector<int64>& weights) {
|
||||
void Solve(const std::vector<int64> &durations,
|
||||
const std::vector<int64> &due_dates,
|
||||
const std::vector<int64> &weights) {
|
||||
const int num_tasks = durations.size();
|
||||
CHECK_EQ(due_dates.size(), num_tasks);
|
||||
CHECK_EQ(weights.size(), num_tasks);
|
||||
@@ -63,7 +63,8 @@ void Solve(const std::vector<int64>& durations,
|
||||
int next_task = -1;
|
||||
int64 next_cost;
|
||||
for (int j = 0; j < num_tasks; ++j) {
|
||||
if (is_taken[j]) continue;
|
||||
if (is_taken[j])
|
||||
continue;
|
||||
const int64 cost = weights[j] * std::max<int64>(0, end - due_dates[j]);
|
||||
if (next_task == -1 || cost < next_cost) {
|
||||
next_task = j;
|
||||
@@ -117,7 +118,8 @@ void Solve(const std::vector<int64>& durations,
|
||||
cp_model.AddNoOverlap(task_intervals);
|
||||
|
||||
// TODO(user): We can't set an objective upper bound with the current cp_model
|
||||
// interface, so we can't use heuristic or FLAGS_upper_bound here. The best is
|
||||
// interface, so we can't use heuristic or absl::GetFlag(FLAGS_upper_bound)
|
||||
// here. The best is
|
||||
// probably to provide a "solution hint" instead.
|
||||
//
|
||||
// Set a known upper bound (or use the flag). This has a bigger impact than
|
||||
@@ -141,7 +143,8 @@ void Solve(const std::vector<int64>& durations,
|
||||
int num_added_precedences = 0;
|
||||
for (int i = 0; i < num_tasks; ++i) {
|
||||
for (int j = 0; j < num_tasks; ++j) {
|
||||
if (i == j) continue;
|
||||
if (i == j)
|
||||
continue;
|
||||
if (due_dates[i] <= due_dates[j] && durations[i] <= durations[j] &&
|
||||
weights[i] >= weights[j]) {
|
||||
// If two jobs have exactly the same specs, we don't add both
|
||||
@@ -164,8 +167,8 @@ void Solve(const std::vector<int64>& durations,
|
||||
// Note that we only fully instantiate the start/end and only look at the
|
||||
// lower bound for the objective and the tardiness variables.
|
||||
Model model;
|
||||
model.Add(NewSatParameters(FLAGS_params));
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
|
||||
model.Add(NewSatParameters(absl::GetFlag(FLAGS_params)));
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse & r) {
|
||||
// Note that we compute the "real" cost here and do not use the tardiness
|
||||
// variables. This is because in the core based approach, the tardiness
|
||||
// variable might be fixed before the end date, and we just have a >=
|
||||
@@ -215,41 +218,45 @@ void Solve(const std::vector<int64>& durations,
|
||||
void ParseAndSolve() {
|
||||
std::vector<int> numbers;
|
||||
std::vector<std::string> entries;
|
||||
for (const std::string& line : FileLines(FLAGS_input)) {
|
||||
for (const std::string &line : FileLines(absl::GetFlag(FLAGS_input))) {
|
||||
entries = absl::StrSplit(line, ' ', absl::SkipEmpty());
|
||||
for (const std::string& entry : entries) {
|
||||
for (const std::string &entry : entries) {
|
||||
numbers.push_back(0);
|
||||
CHECK(absl::SimpleAtoi(entry, &numbers.back()));
|
||||
}
|
||||
}
|
||||
|
||||
const int instance_size = FLAGS_size * 3;
|
||||
LOG(INFO) << numbers.size() << " numbers in '" << FLAGS_input << "'.";
|
||||
const int instance_size = absl::GetFlag(FLAGS_size) * 3;
|
||||
LOG(INFO) << numbers.size() << " numbers in '" << absl::GetFlag(FLAGS_input)
|
||||
<< "'.";
|
||||
LOG(INFO) << "This correspond to " << numbers.size() / instance_size
|
||||
<< " instances of size " << FLAGS_size;
|
||||
LOG(INFO) << "Loading instance #" << FLAGS_n;
|
||||
CHECK_GE(FLAGS_n, 0);
|
||||
CHECK_LE(FLAGS_n * instance_size, numbers.size());
|
||||
<< " instances of size " << absl::GetFlag(FLAGS_size);
|
||||
LOG(INFO) << "Loading instance #" << absl::GetFlag(FLAGS_n);
|
||||
CHECK_GE(absl::GetFlag(FLAGS_n), 0);
|
||||
CHECK_LE(absl::GetFlag(FLAGS_n) * instance_size, numbers.size());
|
||||
|
||||
// The order in a wt file is: duration, tardiness weights and then due_dates.
|
||||
int index = (FLAGS_n - 1) * instance_size;
|
||||
int index = (absl::GetFlag(FLAGS_n) - 1) * instance_size;
|
||||
std::vector<int64> durations;
|
||||
for (int j = 0; j < FLAGS_size; ++j) durations.push_back(numbers[index++]);
|
||||
for (int j = 0; j < absl::GetFlag(FLAGS_size); ++j)
|
||||
durations.push_back(numbers[index++]);
|
||||
std::vector<int64> weights;
|
||||
for (int j = 0; j < FLAGS_size; ++j) weights.push_back(numbers[index++]);
|
||||
for (int j = 0; j < absl::GetFlag(FLAGS_size); ++j)
|
||||
weights.push_back(numbers[index++]);
|
||||
std::vector<int64> due_dates;
|
||||
for (int j = 0; j < FLAGS_size; ++j) due_dates.push_back(numbers[index++]);
|
||||
for (int j = 0; j < absl::GetFlag(FLAGS_size); ++j)
|
||||
due_dates.push_back(numbers[index++]);
|
||||
|
||||
Solve(durations, due_dates, weights);
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
int main(int argc, char **argv) {
|
||||
absl::SetFlag(&absl::GetFlag(FLAGS_logtostderr), true);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_input.empty()) {
|
||||
if (absl::GetFlag(FLAGS_input).empty()) {
|
||||
LOG(FATAL) << "Please supply a data file with --input=";
|
||||
}
|
||||
operations_research::sat::ParseAndSolve();
|
||||
|
||||
@@ -184,7 +184,7 @@ void RunAllExamples() {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
FLAGS_logtostderr = 1;
|
||||
absl::GetFlag(FLAGS_logtostderr) = 1;
|
||||
operations_research::RunAllExamples();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ DECLARE_bool(routing_no_lns);
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
if (FLAGS_tsp_use_deterministic_random_seed) {
|
||||
if (absl::GetFlag(FLAGS_tsp_use_deterministic_random_seed)) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
@@ -100,23 +100,23 @@ class RandomMatrix {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_tsp_size > 0) {
|
||||
// TSP of size FLAGS_tsp_size.
|
||||
if (absl::GetFlag(FLAGS_tsp_size) > 0) {
|
||||
// TSP of size absl::GetFlag(FLAGS_tsp_size).
|
||||
// Second argument = 1 to build a single tour (it's a TSP).
|
||||
// Nodes are indexed from 0 to FLAGS_tsp_size - 1, by default the start of
|
||||
// Nodes are indexed from 0 to absl::GetFlag(FLAGS_tsp_size) - 1, by default the start of
|
||||
// the route is node 0.
|
||||
RoutingModel routing(FLAGS_tsp_size, 1);
|
||||
RoutingModel routing(absl::GetFlag(FLAGS_tsp_size), 1);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
FLAGS_routing_first_solution = "PathCheapestArc";
|
||||
absl::GetFlag(FLAGS_routing_first_solution) = "PathCheapestArc";
|
||||
// Disabling Large Neighborhood Search, comment out to activate it.
|
||||
FLAGS_routing_no_lns = true;
|
||||
absl::GetFlag(FLAGS_routing_no_lns) = true;
|
||||
|
||||
// Setting the cost function.
|
||||
// Put a permanent callback to the distance accessor here. The callback
|
||||
// has the following signature: ResultCallback2<int64, int64, int64>.
|
||||
// The two arguments are the from and to node inidices.
|
||||
RandomMatrix matrix(FLAGS_tsp_size);
|
||||
if (FLAGS_tsp_use_random_matrix) {
|
||||
RandomMatrix matrix(absl::GetFlag(FLAGS_tsp_size));
|
||||
if (absl::GetFlag(FLAGS_tsp_use_random_matrix)) {
|
||||
matrix.Initialize();
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(
|
||||
NewPermanentCallback(&matrix, &RandomMatrix::Distance));
|
||||
@@ -127,9 +127,9 @@ int main(int argc, char** argv) {
|
||||
// Forbid node connections (randomly).
|
||||
ACMRandom randomizer(GetSeed());
|
||||
int64 forbidden_connections = 0;
|
||||
while (forbidden_connections < FLAGS_tsp_random_forbidden_connections) {
|
||||
const int64 from = randomizer.Uniform(FLAGS_tsp_size - 1);
|
||||
const int64 to = randomizer.Uniform(FLAGS_tsp_size - 1) + 1;
|
||||
while (forbidden_connections < absl::GetFlag(FLAGS_tsp_random_forbidden_connections)) {
|
||||
const int64 from = randomizer.Uniform(absl::GetFlag(FLAGS_tsp_size) - 1);
|
||||
const int64 to = randomizer.Uniform(absl::GetFlag(FLAGS_tsp_size) - 1) + 1;
|
||||
if (routing.NextVar(from)->Contains(to)) {
|
||||
LOG(INFO) << "Forbidding connection " << from << " -> " << to;
|
||||
routing.NextVar(from)->RemoveValue(to);
|
||||
|
||||
@@ -27,12 +27,11 @@ namespace operations_research {
|
||||
//
|
||||
// It is very fast and compact: it uses exactly 8*n bytes of memory.
|
||||
class DenseDoublyLinkedList {
|
||||
public:
|
||||
public:
|
||||
// You can construct a DenseDoublyLinkedList with any range-iterable class
|
||||
// that also has a size() method. The order of the elements is given by the
|
||||
// user and will never change (modulo the removal of elements).
|
||||
template <class T>
|
||||
explicit DenseDoublyLinkedList(const T& sorted_elements);
|
||||
template <class T> explicit DenseDoublyLinkedList(const T &sorted_elements);
|
||||
|
||||
int Size() const { return next_.size(); }
|
||||
|
||||
@@ -44,7 +43,7 @@ class DenseDoublyLinkedList {
|
||||
// You must not call Remove() twice with the same element.
|
||||
void Remove(int i);
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<int> next_;
|
||||
std::vector<int> prev_;
|
||||
};
|
||||
@@ -68,13 +67,16 @@ inline int DenseDoublyLinkedList::Prev(int i) const {
|
||||
inline void DenseDoublyLinkedList::Remove(int i) {
|
||||
const int prev = Prev(i);
|
||||
const int next = Next(i);
|
||||
if (prev >= 0) next_[prev] = next;
|
||||
if (next >= 0) prev_[next] = prev;
|
||||
if (DEBUG_MODE) next_[i] = prev_[i] = -2; // To catch bugs.
|
||||
if (prev >= 0)
|
||||
next_[prev] = next;
|
||||
if (next >= 0)
|
||||
prev_[next] = prev;
|
||||
if (DEBUG_MODE)
|
||||
next_[i] = prev_[i] = -2; // To catch bugs.
|
||||
}
|
||||
|
||||
template <class T>
|
||||
DenseDoublyLinkedList::DenseDoublyLinkedList(const T& elements)
|
||||
DenseDoublyLinkedList::DenseDoublyLinkedList(const T &elements)
|
||||
: next_(elements.size(), -2), prev_(elements.size(), -2) {
|
||||
int last = -1;
|
||||
for (const int e : elements) {
|
||||
@@ -82,16 +84,20 @@ DenseDoublyLinkedList::DenseDoublyLinkedList(const T& elements)
|
||||
DCHECK_LE(e, Size());
|
||||
DCHECK_EQ(-2, prev_[e]) << "Duplicate element: " << e;
|
||||
prev_[e] = last;
|
||||
if (last >= 0) next_[last] = e;
|
||||
if (last >= 0)
|
||||
next_[last] = e;
|
||||
last = e;
|
||||
}
|
||||
if (!elements.empty()) next_[elements.back()] = -1;
|
||||
if (!elements.empty())
|
||||
next_[elements.back()] = -1;
|
||||
if (DEBUG_MODE) {
|
||||
for (int p : prev_) DCHECK_NE(-2, p);
|
||||
for (int n : next_) DCHECK_NE(-2, n);
|
||||
for (int p : prev_)
|
||||
DCHECK_NE(-2, p);
|
||||
for (int n : next_)
|
||||
DCHECK_NE(-2, n);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_DENSE_DOUBLY_LINKED_LIST_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_DENSE_DOUBLY_LINKED_LIST_H_
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace operations_research {
|
||||
|
||||
namespace {
|
||||
uint64 FprintOfInt32(int i) {
|
||||
return util_hash::MurmurHash64(reinterpret_cast<const char*>(&i),
|
||||
return util_hash::MurmurHash64(reinterpret_cast<const char *>(&i),
|
||||
sizeof(int));
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
DynamicPartition::DynamicPartition(int num_elements) {
|
||||
DCHECK_GE(num_elements, 0);
|
||||
@@ -38,15 +38,16 @@ DynamicPartition::DynamicPartition(int num_elements) {
|
||||
}
|
||||
part_of_.assign(num_elements, 0);
|
||||
uint64 fprint = 0;
|
||||
for (int i = 0; i < num_elements; ++i) fprint ^= FprintOfInt32(i);
|
||||
part_.push_back(Part(/*start_index=*/0, /*end_index=*/num_elements,
|
||||
/*parent_part=*/0,
|
||||
/*fprint=*/fprint));
|
||||
for (int i = 0; i < num_elements; ++i)
|
||||
fprint ^= FprintOfInt32(i);
|
||||
part_.push_back(Part(/*start_index=*/ 0, /*end_index=*/ num_elements,
|
||||
/*parent_part=*/ 0, /*fprint=*/ fprint));
|
||||
}
|
||||
|
||||
DynamicPartition::DynamicPartition(
|
||||
const std::vector<int>& initial_part_of_element) {
|
||||
if (initial_part_of_element.empty()) return;
|
||||
const std::vector<int> &initial_part_of_element) {
|
||||
if (initial_part_of_element.empty())
|
||||
return;
|
||||
part_of_ = initial_part_of_element;
|
||||
const int n = part_of_.size();
|
||||
const int num_parts = 1 + *std::max_element(part_of_.begin(), part_of_.end());
|
||||
@@ -54,30 +55,33 @@ DynamicPartition::DynamicPartition(
|
||||
part_.resize(num_parts);
|
||||
|
||||
// Compute the part fingerprints.
|
||||
for (int i = 0; i < n; ++i) part_[part_of_[i]].fprint ^= FprintOfInt32(i);
|
||||
for (int i = 0; i < n; ++i)
|
||||
part_[part_of_[i]].fprint ^= FprintOfInt32(i);
|
||||
|
||||
// Compute the actual start indices of each part, knowing that we'll sort
|
||||
// them as they were given implicitly in "initial_part_of_element".
|
||||
// The code looks a bit weird to do it in-place, with no additional memory.
|
||||
for (int p = 0; p < num_parts; ++p) {
|
||||
part_[p].end_index = 0; // Temporarily utilized as size_of_part.
|
||||
part_[p].end_index = 0; // Temporarily utilized as size_of_part.
|
||||
part_[p].parent_part = p;
|
||||
}
|
||||
for (const int p : part_of_) ++part_[p].end_index; // size_of_part
|
||||
for (const int p : part_of_)
|
||||
++part_[p].end_index; // size_of_part
|
||||
int sum_part_sizes = 0;
|
||||
for (int p = 0; p < num_parts; ++p) {
|
||||
part_[p].start_index = sum_part_sizes;
|
||||
sum_part_sizes += part_[p].end_index; // size of part.
|
||||
sum_part_sizes += part_[p].end_index; // size of part.
|
||||
}
|
||||
|
||||
// Now that we have the correct start indices, we set the end indices to the
|
||||
// start indices, and incrementally add all elements to their part, adjusting
|
||||
// the end indices as we go.
|
||||
for (Part& part : part_) part.end_index = part.start_index;
|
||||
for (Part &part : part_)
|
||||
part.end_index = part.start_index;
|
||||
element_.assign(n, -1);
|
||||
index_of_.assign(n, -1);
|
||||
for (int element = 0; element < n; ++element) {
|
||||
Part* const part = &part_[part_of_[element]];
|
||||
Part *const part = &part_[part_of_[element]];
|
||||
element_[part->end_index] = element;
|
||||
index_of_[element] = part->end_index;
|
||||
++part->end_index;
|
||||
@@ -93,7 +97,7 @@ DynamicPartition::DynamicPartition(
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPartition::Refine(const std::vector<int>& distinguished_subset) {
|
||||
void DynamicPartition::Refine(const std::vector<int> &distinguished_subset) {
|
||||
// tmp_counter_of_part_[i] will contain the number of
|
||||
// elements in distinguished_subset that were part of part #i.
|
||||
tmp_counter_of_part_.resize(NumParts(), 0);
|
||||
@@ -132,12 +136,13 @@ void DynamicPartition::Refine(const std::vector<int>& distinguished_subset) {
|
||||
const int start_index = part_[part].start_index;
|
||||
const int end_index = part_[part].end_index;
|
||||
const int split_index = end_index - tmp_counter_of_part_[part];
|
||||
tmp_counter_of_part_[part] = 0; // Clean up after us.
|
||||
tmp_counter_of_part_[part] = 0; // Clean up after us.
|
||||
DCHECK_GE(split_index, start_index);
|
||||
DCHECK_LT(split_index, end_index);
|
||||
|
||||
// Do nothing if all elements were distinguished.
|
||||
if (split_index == start_index) continue;
|
||||
if (split_index == start_index)
|
||||
continue;
|
||||
|
||||
// Compute the fingerprint of the new part.
|
||||
uint64 new_fprint = 0;
|
||||
@@ -163,7 +168,7 @@ void DynamicPartition::UndoRefineUntilNumPartsEqual(int original_num_parts) {
|
||||
DCHECK_GE(original_num_parts, 1);
|
||||
while (NumParts() > original_num_parts) {
|
||||
const int part_index = NumParts() - 1;
|
||||
const Part& part = part_[part_index];
|
||||
const Part &part = part_[part_index];
|
||||
const int parent_part_index = part.parent_part;
|
||||
DCHECK_LT(parent_part_index, part_index) << "UndoRefineUntilNumPartsEqual()"
|
||||
" called with "
|
||||
@@ -173,7 +178,7 @@ void DynamicPartition::UndoRefineUntilNumPartsEqual(int original_num_parts) {
|
||||
for (const int element : ElementsInPart(part_index)) {
|
||||
part_of_[element] = parent_part_index;
|
||||
}
|
||||
Part* const parent_part = &part_[parent_part_index];
|
||||
Part *const parent_part = &part_[parent_part_index];
|
||||
DCHECK_EQ(part.start_index, parent_part->end_index);
|
||||
parent_part->end_index = part.end_index;
|
||||
parent_part->fprint ^= part.fprint;
|
||||
@@ -185,7 +190,7 @@ std::string DynamicPartition::DebugString(DebugStringSorting sorting) const {
|
||||
if (sorting != SORT_LEXICOGRAPHICALLY && sorting != SORT_BY_PART) {
|
||||
return absl::StrFormat("Unsupported sorting: %d", sorting);
|
||||
}
|
||||
std::vector<std::vector<int>> parts;
|
||||
std::vector<std::vector<int> > parts;
|
||||
for (int i = 0; i < NumParts(); ++i) {
|
||||
IterablePart iterable_part = ElementsInPart(i);
|
||||
parts.emplace_back(iterable_part.begin(), iterable_part.end());
|
||||
@@ -195,8 +200,9 @@ std::string DynamicPartition::DebugString(DebugStringSorting sorting) const {
|
||||
std::sort(parts.begin(), parts.end());
|
||||
}
|
||||
std::string out;
|
||||
for (const std::vector<int>& part : parts) {
|
||||
if (!out.empty()) out += " | ";
|
||||
for (const std::vector<int> &part : parts) {
|
||||
if (!out.empty())
|
||||
out += " | ";
|
||||
out += absl::StrJoin(part, " ");
|
||||
}
|
||||
return out;
|
||||
@@ -206,7 +212,8 @@ void MergingPartition::Reset(int num_nodes) {
|
||||
DCHECK_GE(num_nodes, 0);
|
||||
part_size_.assign(num_nodes, 1);
|
||||
parent_.assign(num_nodes, -1);
|
||||
for (int i = 0; i < num_nodes; ++i) parent_[i] = i;
|
||||
for (int i = 0; i < num_nodes; ++i)
|
||||
parent_[i] = i;
|
||||
tmp_part_bit_.assign(num_nodes, false);
|
||||
}
|
||||
|
||||
@@ -217,7 +224,8 @@ int MergingPartition::MergePartsOf(int node1, int node2) {
|
||||
DCHECK_LT(node2, NumNodes());
|
||||
int root1 = GetRoot(node1);
|
||||
int root2 = GetRoot(node2);
|
||||
if (root1 == root2) return -1;
|
||||
if (root1 == root2)
|
||||
return -1;
|
||||
int s1 = part_size_[root1];
|
||||
int s2 = part_size_[root2];
|
||||
// Attach the smaller part to the larger one. Break ties by root index.
|
||||
@@ -242,7 +250,7 @@ int MergingPartition::GetRootAndCompressPath(int node) {
|
||||
return root;
|
||||
}
|
||||
|
||||
void MergingPartition::KeepOnlyOneNodePerPart(std::vector<int>* nodes) {
|
||||
void MergingPartition::KeepOnlyOneNodePerPart(std::vector<int> *nodes) {
|
||||
int num_nodes_kept = 0;
|
||||
for (const int node : *nodes) {
|
||||
const int representative = GetRootAndCompressPath(node);
|
||||
@@ -255,11 +263,12 @@ void MergingPartition::KeepOnlyOneNodePerPart(std::vector<int>* nodes) {
|
||||
|
||||
// Clean up the tmp_part_bit_ vector. Since we've already compressed the
|
||||
// paths (if backtracking was enabled), no need to do it again.
|
||||
for (const int node : *nodes) tmp_part_bit_[GetRoot(node)] = false;
|
||||
for (const int node : *nodes)
|
||||
tmp_part_bit_[GetRoot(node)] = false;
|
||||
}
|
||||
|
||||
int MergingPartition::FillEquivalenceClasses(
|
||||
std::vector<int>* node_equivalence_classes) {
|
||||
std::vector<int> *node_equivalence_classes) {
|
||||
node_equivalence_classes->assign(NumNodes(), -1);
|
||||
int num_roots = 0;
|
||||
for (int node = 0; node < NumNodes(); ++node) {
|
||||
@@ -274,21 +283,22 @@ int MergingPartition::FillEquivalenceClasses(
|
||||
}
|
||||
|
||||
std::string MergingPartition::DebugString() {
|
||||
std::vector<std::vector<int>> sorted_parts(NumNodes());
|
||||
std::vector<std::vector<int> > sorted_parts(NumNodes());
|
||||
for (int i = 0; i < NumNodes(); ++i) {
|
||||
sorted_parts[GetRootAndCompressPath(i)].push_back(i);
|
||||
}
|
||||
for (std::vector<int>& part : sorted_parts)
|
||||
for (std::vector<int> &part : sorted_parts)
|
||||
std::sort(part.begin(), part.end());
|
||||
std::sort(sorted_parts.begin(), sorted_parts.end());
|
||||
// Note: typically, a lot of elements of "sorted_parts" will be empty,
|
||||
// but these won't be visible in the string that we construct below.
|
||||
std::string out;
|
||||
for (const std::vector<int>& part : sorted_parts) {
|
||||
if (!out.empty()) out += " | ";
|
||||
for (const std::vector<int> &part : sorted_parts) {
|
||||
if (!out.empty())
|
||||
out += " | ";
|
||||
out += absl::StrJoin(part, " ");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -47,14 +47,14 @@ namespace operations_research {
|
||||
//
|
||||
// TODO(user): rename this to BacktrackableSplittingPartition.
|
||||
class DynamicPartition {
|
||||
public:
|
||||
public:
|
||||
// Creates a DynamicPartition on n elements, numbered 0..n-1. Start with
|
||||
// the trivial partition (only one subset containing all elements).
|
||||
explicit DynamicPartition(int num_elements);
|
||||
|
||||
// Ditto, but specify the initial part of each elements. Part indices must
|
||||
// form a dense integer set starting at 0; eg. [2, 1, 0, 1, 1, 3, 0] is valid.
|
||||
explicit DynamicPartition(const std::vector<int>& initial_part_of_element);
|
||||
explicit DynamicPartition(const std::vector<int> &initial_part_of_element);
|
||||
|
||||
// Accessors.
|
||||
int NumElements() const { return element_.size(); }
|
||||
@@ -105,7 +105,7 @@ class DynamicPartition {
|
||||
// keeping track of one additional bit of information for each part that
|
||||
// remains unchanged by a Refine() operation: was that part entirely *in*
|
||||
// the distinguished subset or entirely *out*?
|
||||
void Refine(const std::vector<int>& distinguished_subset);
|
||||
void Refine(const std::vector<int> &distinguished_subset);
|
||||
|
||||
// Undo one or several Refine() operations, until the number of parts
|
||||
// becomes equal to "original_num_parts".
|
||||
@@ -133,11 +133,11 @@ class DynamicPartition {
|
||||
// Note that the order does get changed by Refine() operations.
|
||||
// This is a reference, so it'll only remain valid and constant until the
|
||||
// class is destroyed or until Refine() get called.
|
||||
const std::vector<int>& ElementsInHierarchicalOrder() const {
|
||||
const std::vector<int> &ElementsInHierarchicalOrder() const {
|
||||
return element_;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// A DynamicPartition instance maintains a list of all of its elements,
|
||||
// 'sorted' by partitions: elements of the same subset are contiguous
|
||||
// in that list.
|
||||
@@ -152,13 +152,13 @@ class DynamicPartition {
|
||||
struct Part {
|
||||
// This part holds elements[start_index .. end_index-1].
|
||||
// INVARIANT: end_index > start_index.
|
||||
int start_index; // Inclusive
|
||||
int end_index; // Exclusive
|
||||
int start_index; // Inclusive
|
||||
int end_index; // Exclusive
|
||||
|
||||
// The Part that this part was split out of. See the comment at Refine().
|
||||
// INVARIANT: part[i].parent_part <= i, and the equality holds iff part[i]
|
||||
// has no parent.
|
||||
int parent_part; // Index into the part[] array.
|
||||
int parent_part; // Index into the part[] array.
|
||||
|
||||
// The part's fingerprint is the XOR of all fingerprints of its elements.
|
||||
// See FprintOfInt32() in the .cc.
|
||||
@@ -166,12 +166,10 @@ class DynamicPartition {
|
||||
|
||||
Part() : start_index(0), end_index(0), parent_part(0), fprint(0) {}
|
||||
Part(int start_index, int end_index, int parent_part, uint64 fprint)
|
||||
: start_index(start_index),
|
||||
end_index(end_index),
|
||||
parent_part(parent_part),
|
||||
fprint(fprint) {}
|
||||
: start_index(start_index), end_index(end_index),
|
||||
parent_part(parent_part), fprint(fprint) {}
|
||||
};
|
||||
std::vector<Part> part_; // The disjoint parts.
|
||||
std::vector<Part> part_; // The disjoint parts.
|
||||
|
||||
// Used temporarily and exclusively by Refine(). This prevents Refine()
|
||||
// from being thread-safe.
|
||||
@@ -189,8 +187,8 @@ struct DynamicPartition::IterablePart {
|
||||
int size() const { return end_ - begin_; }
|
||||
|
||||
IterablePart() {}
|
||||
IterablePart(const std::vector<int>::const_iterator& b,
|
||||
const std::vector<int>::const_iterator& e)
|
||||
IterablePart(const std::vector<int>::const_iterator &b,
|
||||
const std::vector<int>::const_iterator &e)
|
||||
: begin_(b), end_(e) {}
|
||||
|
||||
// These typedefs allow this iterator to be used within testing::ElementsAre.
|
||||
@@ -201,7 +199,7 @@ struct DynamicPartition::IterablePart {
|
||||
// Partition class that supports incremental merging, using the union-find
|
||||
// algorithm (see http://en.wikipedia.org/wiki/Disjoint-set_data_structure).
|
||||
class MergingPartition {
|
||||
public:
|
||||
public:
|
||||
// At first, all nodes are in their own singleton part.
|
||||
MergingPartition() { Reset(0); }
|
||||
explicit MergingPartition(int num_nodes) { Reset(num_nodes); }
|
||||
@@ -218,7 +216,7 @@ class MergingPartition {
|
||||
//
|
||||
// Details: a smaller part will always be merged onto a larger one.
|
||||
// Upons ties, the smaller representative becomes the overall representative.
|
||||
int MergePartsOf(int node1, int node2); // The 'union' of the union-find.
|
||||
int MergePartsOf(int node1, int node2); // The 'union' of the union-find.
|
||||
|
||||
// Get the representative of "node" (a node in the same equivalence class,
|
||||
// which will also be returned for any other "node" in the same class).
|
||||
@@ -227,7 +225,7 @@ class MergingPartition {
|
||||
|
||||
// Specialized reader API: prunes "nodes" to only keep at most one node per
|
||||
// part: any node which is in the same part as an earlier node will be pruned.
|
||||
void KeepOnlyOneNodePerPart(std::vector<int>* nodes);
|
||||
void KeepOnlyOneNodePerPart(std::vector<int> *nodes);
|
||||
|
||||
// Output the whole partition as node equivalence classes: if there are K
|
||||
// parts and N nodes, node_equivalence_classes[i] will contain the part index
|
||||
@@ -235,7 +233,7 @@ class MergingPartition {
|
||||
// (i.e. node 0 will always be in part 0; then the next node that isn't in
|
||||
// part 0 will be in part 1, and so on).
|
||||
// Returns the number K of classes.
|
||||
int FillEquivalenceClasses(std::vector<int>* node_equivalence_classes);
|
||||
int FillEquivalenceClasses(std::vector<int> *node_equivalence_classes);
|
||||
|
||||
// Dump all components, with nodes sorted within each part and parts
|
||||
// sorted lexicographically. Eg. "0 1 3 4 | 2 5 | 6 7 8".
|
||||
@@ -259,7 +257,7 @@ class MergingPartition {
|
||||
// version using path compression.
|
||||
int GetRoot(int node) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
// Along the upwards path from 'node' to its root, set the parent of all
|
||||
// nodes (including the root) to 'parent'.
|
||||
void SetParentAlongPathToRoot(int node, int parent);
|
||||
@@ -273,8 +271,8 @@ class MergingPartition {
|
||||
|
||||
// *** Implementation of inline methods of the above classes. ***
|
||||
|
||||
inline DynamicPartition::IterablePart DynamicPartition::ElementsInPart(
|
||||
int i) const {
|
||||
inline DynamicPartition::IterablePart
|
||||
DynamicPartition::ElementsInPart(int i) const {
|
||||
DCHECK_GE(i, 0);
|
||||
DCHECK_LT(i, NumParts());
|
||||
return IterablePart(element_.begin() + part_[i].start_index,
|
||||
@@ -290,7 +288,7 @@ inline int DynamicPartition::PartOf(int element) const {
|
||||
inline int DynamicPartition::SizeOfPart(int part) const {
|
||||
DCHECK_GE(part, 0);
|
||||
DCHECK_LT(part, part_.size());
|
||||
const Part& p = part_[part];
|
||||
const Part &p = part_[part];
|
||||
return p.end_index - p.start_index;
|
||||
}
|
||||
|
||||
@@ -300,8 +298,8 @@ inline int DynamicPartition::ParentOfPart(int part) const {
|
||||
return part_[part].parent_part;
|
||||
}
|
||||
|
||||
inline DynamicPartition::IterablePart DynamicPartition::ElementsInSamePartAs(
|
||||
int i) const {
|
||||
inline DynamicPartition::IterablePart
|
||||
DynamicPartition::ElementsInSamePartAs(int i) const {
|
||||
return ElementsInPart(PartOf(i));
|
||||
}
|
||||
|
||||
@@ -317,7 +315,8 @@ inline int MergingPartition::GetRoot(int node) const {
|
||||
int child = node;
|
||||
while (true) {
|
||||
const int parent = parent_[child];
|
||||
if (parent == child) return child;
|
||||
if (parent == child)
|
||||
return child;
|
||||
child = parent;
|
||||
}
|
||||
}
|
||||
@@ -331,7 +330,8 @@ inline void MergingPartition::SetParentAlongPathToRoot(int node, int parent) {
|
||||
while (true) {
|
||||
const int old_parent = parent_[child];
|
||||
parent_[child] = parent;
|
||||
if (old_parent == child) return;
|
||||
if (old_parent == child)
|
||||
return;
|
||||
child = old_parent;
|
||||
}
|
||||
}
|
||||
@@ -343,6 +343,6 @@ inline void MergingPartition::ResetNode(int node) {
|
||||
part_size_[node] = 1;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_DYNAMIC_PARTITION_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_DYNAMIC_PARTITION_H_
|
||||
|
||||
@@ -21,36 +21,39 @@ namespace operations_research {
|
||||
|
||||
DynamicPermutation::DynamicPermutation(int n)
|
||||
: image_(n, -1), ancestor_(n, -1), tmp_mask_(n, false) {
|
||||
for (int i = 0; i < Size(); ++i) image_[i] = ancestor_[i] = i;
|
||||
for (int i = 0; i < Size(); ++i)
|
||||
image_[i] = ancestor_[i] = i;
|
||||
}
|
||||
|
||||
void DynamicPermutation::AddMappings(const std::vector<int>& src,
|
||||
const std::vector<int>& dst) {
|
||||
void DynamicPermutation::AddMappings(const std::vector<int> &src,
|
||||
const std::vector<int> &dst) {
|
||||
DCHECK_EQ(src.size(), dst.size());
|
||||
mapping_src_size_stack_.push_back(mapping_src_stack_.size());
|
||||
mapping_src_stack_.reserve(mapping_src_stack_.size() + src.size());
|
||||
for (int i = 0; i < src.size(); ++i) {
|
||||
const int s = src[i];
|
||||
const int d = dst[i];
|
||||
DCHECK_EQ(s, ImageOf(s)); // No prior image of s.
|
||||
DCHECK_EQ(d, ancestor_[d]); // No prior ancestor of d.
|
||||
DCHECK_EQ(s, ImageOf(s)); // No prior image of s.
|
||||
DCHECK_EQ(d, ancestor_[d]); // No prior ancestor of d.
|
||||
|
||||
ancestor_[d] = RootOf(s);
|
||||
image_[s] = d;
|
||||
|
||||
if (image_[d] == d) loose_ends_.insert(d);
|
||||
loose_ends_.erase(s); // Also takes care of the corner case s == d.
|
||||
if (image_[d] == d)
|
||||
loose_ends_.insert(d);
|
||||
loose_ends_.erase(s); // Also takes care of the corner case s == d.
|
||||
|
||||
// Remember the sources for the undo stack.
|
||||
mapping_src_stack_.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPermutation::UndoLastMappings(
|
||||
std::vector<int>* undone_mapping_src) {
|
||||
void
|
||||
DynamicPermutation::UndoLastMappings(std::vector<int> *undone_mapping_src) {
|
||||
DCHECK(undone_mapping_src != nullptr);
|
||||
undone_mapping_src->clear();
|
||||
if (mapping_src_size_stack_.empty()) return; // Nothing to undo.
|
||||
if (mapping_src_size_stack_.empty())
|
||||
return; // Nothing to undo.
|
||||
const int num_mappings_before = mapping_src_size_stack_.back();
|
||||
mapping_src_size_stack_.pop_back();
|
||||
const int num_mappings_now = mapping_src_stack_.size();
|
||||
@@ -66,13 +69,14 @@ void DynamicPermutation::UndoLastMappings(
|
||||
const int s = mapping_src_stack_[i];
|
||||
const int d = ImageOf(s);
|
||||
|
||||
if (ancestor_[s] != s) loose_ends_.insert(s);
|
||||
if (ancestor_[s] != s)
|
||||
loose_ends_.insert(s);
|
||||
loose_ends_.erase(d);
|
||||
|
||||
ancestor_[d] = d;
|
||||
image_[s] = s;
|
||||
}
|
||||
mapping_src_stack_.resize(num_mappings_before); // Shrink.
|
||||
mapping_src_stack_.resize(num_mappings_before); // Shrink.
|
||||
}
|
||||
|
||||
void DynamicPermutation::Reset() {
|
||||
@@ -86,12 +90,13 @@ void DynamicPermutation::Reset() {
|
||||
loose_ends_.clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<SparsePermutation> DynamicPermutation::CreateSparsePermutation()
|
||||
const {
|
||||
std::unique_ptr<SparsePermutation>
|
||||
DynamicPermutation::CreateSparsePermutation() const {
|
||||
std::unique_ptr<SparsePermutation> sparse_perm(new SparsePermutation(Size()));
|
||||
int num_identity_singletons = 0;
|
||||
for (const int x : mapping_src_stack_) {
|
||||
if (tmp_mask_[x]) continue;
|
||||
if (tmp_mask_[x])
|
||||
continue;
|
||||
// Deal with the special case of a trivial x->x cycle, which we do *not*
|
||||
// want to add to the sparse permutation.
|
||||
if (ImageOf(x) == x) {
|
||||
@@ -106,11 +111,13 @@ std::unique_ptr<SparsePermutation> DynamicPermutation::CreateSparsePermutation()
|
||||
tmp_mask_[next] = true;
|
||||
DCHECK_NE(next, ImageOf(next));
|
||||
next = ImageOf(next);
|
||||
if (next == root) break;
|
||||
if (next == root)
|
||||
break;
|
||||
}
|
||||
sparse_perm->CloseCurrentCycle();
|
||||
}
|
||||
for (const int x : mapping_src_stack_) tmp_mask_[x] = false;
|
||||
for (const int x : mapping_src_stack_)
|
||||
tmp_mask_[x] = false;
|
||||
DCHECK_EQ(mapping_src_stack_.size(),
|
||||
sparse_perm->Support().size() + num_identity_singletons);
|
||||
return sparse_perm;
|
||||
@@ -121,4 +128,4 @@ std::string DynamicPermutation::DebugString() const {
|
||||
return CreateSparsePermutation()->DebugString();
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#define OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set> // TODO(user): remove when no longer used.
|
||||
#include <set> // TODO(user): remove when no longer used.
|
||||
#include <vector>
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
@@ -31,11 +31,13 @@ class SparsePermutation;
|
||||
// RAM usage: as of 2014-04, this class needs less than:
|
||||
// 32.125 * (n + 2 * support_size) bytes.
|
||||
class DynamicPermutation {
|
||||
public:
|
||||
public:
|
||||
// Upon construction, every element i in [0..n-1] maps to itself.
|
||||
explicit DynamicPermutation(int n);
|
||||
|
||||
int Size() const { return image_.size(); } // Return the original "n".
|
||||
int Size() const {
|
||||
return image_.size();
|
||||
} // Return the original "n".
|
||||
|
||||
// Declares a set of mappings for this permutation: src[i] will map to dst[i].
|
||||
// Requirements that are DCHECKed:
|
||||
@@ -44,7 +46,7 @@ class DynamicPermutation {
|
||||
// - For all i, dst[i] must not already be the image of something.
|
||||
//
|
||||
// Complexity: amortized O(src.size()).
|
||||
void AddMappings(const std::vector<int>& src, const std::vector<int>& dst);
|
||||
void AddMappings(const std::vector<int> &src, const std::vector<int> &dst);
|
||||
|
||||
// Undoes the last AddMappings() operation, and fills the "undone_mapping_src"
|
||||
// vector with the src of that last operation. This works like an undo stack.
|
||||
@@ -54,17 +56,17 @@ class DynamicPermutation {
|
||||
// simply a no-op.
|
||||
//
|
||||
// Complexity: same as the AddMappings() operation being undone.
|
||||
void UndoLastMappings(std::vector<int>* undone_mapping_src);
|
||||
void UndoLastMappings(std::vector<int> *undone_mapping_src);
|
||||
|
||||
// Makes the permutation back to the identity (i.e. like right after
|
||||
// construction).
|
||||
// Complexity: O(support size).
|
||||
void Reset();
|
||||
|
||||
int ImageOf(int i) const; // Complexity: one vector lookup.
|
||||
int ImageOf(int i) const; // Complexity: one vector lookup.
|
||||
|
||||
// Returns the union of all "src" ever given to AddMappings().
|
||||
const std::vector<int>& AllMappingsSrc() const { return mapping_src_stack_; }
|
||||
const std::vector<int> &AllMappingsSrc() const { return mapping_src_stack_; }
|
||||
|
||||
// While the permutation is partially being built, the orbit of elements will
|
||||
// either form unclosed paths, or closed cycles. In the former case,
|
||||
@@ -80,7 +82,7 @@ class DynamicPermutation {
|
||||
// (e.g., paths) built so far.
|
||||
// TODO(user): use a faster underlying container like SparseBitSet, and
|
||||
// tweak this API accordingly.
|
||||
const std::set<int>& LooseEnds() const { return loose_ends_; }
|
||||
const std::set<int> &LooseEnds() const { return loose_ends_; }
|
||||
|
||||
// Creates a SparsePermutation representing the current permutation.
|
||||
// Requirements: the permutation must only have cycles.
|
||||
@@ -90,7 +92,7 @@ class DynamicPermutation {
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<int> image_;
|
||||
// ancestor_[i] isn't exactly RootOf(i): it might itself have an ancestor, and
|
||||
// so on.
|
||||
@@ -123,11 +125,12 @@ inline int DynamicPermutation::RootOf(int i) const {
|
||||
DCHECK_LT(i, Size());
|
||||
while (true) {
|
||||
const int j = ancestor_[i];
|
||||
if (j == i) return i;
|
||||
if (j == i)
|
||||
return i;
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_
|
||||
|
||||
@@ -43,16 +43,17 @@ using util::GraphIsSymmetric;
|
||||
|
||||
namespace {
|
||||
// Some routines used below.
|
||||
void SwapFrontAndBack(std::vector<int>* v) {
|
||||
void SwapFrontAndBack(std::vector<int> *v) {
|
||||
DCHECK(!v->empty());
|
||||
std::swap((*v)[0], v->back());
|
||||
}
|
||||
|
||||
bool PartitionsAreCompatibleAfterPartIndex(const DynamicPartition& p1,
|
||||
const DynamicPartition& p2,
|
||||
bool PartitionsAreCompatibleAfterPartIndex(const DynamicPartition &p1,
|
||||
const DynamicPartition &p2,
|
||||
int part_index) {
|
||||
const int num_parts = p1.NumParts();
|
||||
if (p2.NumParts() != num_parts) return false;
|
||||
if (p2.NumParts() != num_parts)
|
||||
return false;
|
||||
for (int p = part_index; p < num_parts; ++p) {
|
||||
if (p1.SizeOfPart(p) != p2.SizeOfPart(p) ||
|
||||
p1.ParentOfPart(p) != p2.ParentOfPart(p)) {
|
||||
@@ -71,9 +72,9 @@ bool PartitionsAreCompatibleAfterPartIndex(const DynamicPartition& p1,
|
||||
// be repeated in the list), and see if that's sufficient to make the whole
|
||||
// graph symmetry finder support multi-arcs.
|
||||
template <class List>
|
||||
bool ListMapsToList(const List& l1, const List& l2,
|
||||
const DynamicPermutation& permutation,
|
||||
std::vector<bool>* tmp_node_mask) {
|
||||
bool ListMapsToList(const List &l1, const List &l2,
|
||||
const DynamicPermutation &permutation,
|
||||
std::vector<bool> *tmp_node_mask) {
|
||||
int num_elements_delta = 0;
|
||||
bool match = true;
|
||||
for (const int mapped_x : l2) {
|
||||
@@ -89,20 +90,20 @@ bool ListMapsToList(const List& l1, const List& l2,
|
||||
}
|
||||
(*tmp_node_mask)[mapped_x] = false;
|
||||
}
|
||||
if (num_elements_delta != 0) match = false;
|
||||
if (num_elements_delta != 0)
|
||||
match = false;
|
||||
if (!match) {
|
||||
// We need to clean up tmp_node_mask.
|
||||
for (const int x : l2) (*tmp_node_mask)[x] = false;
|
||||
for (const int x : l2)
|
||||
(*tmp_node_mask)[x] = false;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
GraphSymmetryFinder::GraphSymmetryFinder(const Graph& graph, bool is_undirected)
|
||||
: graph_(graph),
|
||||
tmp_dynamic_permutation_(NumNodes()),
|
||||
tmp_node_mask_(NumNodes(), false),
|
||||
tmp_degree_(NumNodes(), 0),
|
||||
GraphSymmetryFinder::GraphSymmetryFinder(const Graph &graph, bool is_undirected)
|
||||
: graph_(graph), tmp_dynamic_permutation_(NumNodes()),
|
||||
tmp_node_mask_(NumNodes(), false), tmp_degree_(NumNodes(), 0),
|
||||
tmp_nodes_with_degree_(NumNodes() + 1) {
|
||||
// Set up an "unlimited" time limit by default.
|
||||
time_limit_ = TimeLimit::Infinite();
|
||||
@@ -131,25 +132,26 @@ GraphSymmetryFinder::GraphSymmetryFinder(const Graph& graph, bool is_undirected)
|
||||
flattened_reverse_adj_lists_.assign(graph.num_arcs(), -1);
|
||||
for (const int node : graph.AllNodes()) {
|
||||
for (const int arc : graph.OutgoingArcs(node)) {
|
||||
flattened_reverse_adj_lists_[reverse_adj_list_index_[graph.Head(arc) +
|
||||
/*shift*/ 1]++] =
|
||||
node;
|
||||
flattened_reverse_adj_lists_[
|
||||
reverse_adj_list_index_[graph.Head(arc) + /*shift*/ 1]++] = node;
|
||||
}
|
||||
}
|
||||
// The last pass shifted reverse_adj_list_index, so it's now as we want it:
|
||||
// [0, in_degree(node0), in_degree(node0) + in_degree(node1), ...]
|
||||
if (DEBUG_MODE) {
|
||||
DCHECK_EQ(graph.num_arcs(), reverse_adj_list_index_[graph.num_nodes()]);
|
||||
for (const int i : flattened_reverse_adj_lists_) DCHECK_NE(i, -1);
|
||||
for (const int i : flattened_reverse_adj_lists_)
|
||||
DCHECK_NE(i, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphSymmetryFinder::IsGraphAutomorphism(
|
||||
const DynamicPermutation& permutation) const {
|
||||
const DynamicPermutation &permutation) const {
|
||||
for (const int base : permutation.AllMappingsSrc()) {
|
||||
const int image = permutation.ImageOf(base);
|
||||
if (image == base) continue;
|
||||
if (image == base)
|
||||
continue;
|
||||
if (!ListMapsToList(graph_[base], graph_[image], permutation,
|
||||
&tmp_node_mask_)) {
|
||||
return false;
|
||||
@@ -160,7 +162,8 @@ bool GraphSymmetryFinder::IsGraphAutomorphism(
|
||||
// to displaced nodes.
|
||||
for (const int base : permutation.AllMappingsSrc()) {
|
||||
const int image = permutation.ImageOf(base);
|
||||
if (image == base) continue;
|
||||
if (image == base)
|
||||
continue;
|
||||
if (!ListMapsToList(TailsOfIncomingArcsTo(base),
|
||||
TailsOfIncomingArcsTo(image), permutation,
|
||||
&tmp_node_mask_)) {
|
||||
@@ -175,22 +178,24 @@ namespace {
|
||||
// Specialized subroutine, to avoid code duplication: see its call site
|
||||
// and its self-explanatory code.
|
||||
template <class T>
|
||||
inline void IncrementCounterForNonSingletons(const T& nodes,
|
||||
const DynamicPartition& partition,
|
||||
std::vector<int>* node_count,
|
||||
std::vector<int>* nodes_seen) {
|
||||
inline void IncrementCounterForNonSingletons(const T &nodes,
|
||||
const DynamicPartition &partition,
|
||||
std::vector<int> *node_count,
|
||||
std::vector<int> *nodes_seen) {
|
||||
for (const int node : nodes) {
|
||||
if (partition.ElementsInSamePartAs(node).size() == 1) continue;
|
||||
if (partition.ElementsInSamePartAs(node).size() == 1)
|
||||
continue;
|
||||
const int count = ++(*node_count)[node];
|
||||
if (count == 1) nodes_seen->push_back(node);
|
||||
if (count == 1)
|
||||
nodes_seen->push_back(node);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
void GraphSymmetryFinder::RecursivelyRefinePartitionByAdjacency(
|
||||
int first_unrefined_part_index, DynamicPartition* partition) {
|
||||
int first_unrefined_part_index, DynamicPartition *partition) {
|
||||
// Rename, for readability of the code below.
|
||||
std::vector<int>& tmp_nodes_with_nonzero_degree = tmp_stack_;
|
||||
std::vector<int> &tmp_nodes_with_nonzero_degree = tmp_stack_;
|
||||
|
||||
// Assuming that the partition was refined based on the adjacency on
|
||||
// parts [0 .. first_unrefined_part_index) already, we simply need to
|
||||
@@ -205,10 +210,10 @@ void GraphSymmetryFinder::RecursivelyRefinePartitionByAdjacency(
|
||||
// advertised.
|
||||
std::vector<bool> adjacency_directions(1, /*outgoing*/ true);
|
||||
if (!reverse_adj_list_index_.empty()) {
|
||||
adjacency_directions.push_back(false); // Also look at incoming arcs.
|
||||
adjacency_directions.push_back(false); // Also look at incoming arcs.
|
||||
}
|
||||
for (int part_index = first_unrefined_part_index;
|
||||
part_index < partition->NumParts(); // Moving target!
|
||||
part_index < partition->NumParts(); // Moving target!
|
||||
++part_index) {
|
||||
for (const bool outgoing_adjacency : adjacency_directions) {
|
||||
// Count the aggregated degree of all nodes, only looking at arcs that
|
||||
@@ -230,23 +235,23 @@ void GraphSymmetryFinder::RecursivelyRefinePartitionByAdjacency(
|
||||
int max_degree = 0;
|
||||
for (const int node : tmp_nodes_with_nonzero_degree) {
|
||||
const int degree = tmp_degree_[node];
|
||||
tmp_degree_[node] = 0; // To clean up after us.
|
||||
tmp_degree_[node] = 0; // To clean up after us.
|
||||
max_degree = std::max(max_degree, degree);
|
||||
tmp_nodes_with_degree_[degree].push_back(node);
|
||||
}
|
||||
tmp_nodes_with_nonzero_degree.clear(); // To clean up after us.
|
||||
tmp_nodes_with_nonzero_degree.clear(); // To clean up after us.
|
||||
// For each degree, refine the partition by the set of nodes with that
|
||||
// degree.
|
||||
for (int degree = 1; degree <= max_degree; ++degree) {
|
||||
partition->Refine(tmp_nodes_with_degree_[degree]);
|
||||
tmp_nodes_with_degree_[degree].clear(); // To clean up after us.
|
||||
tmp_nodes_with_degree_[degree].clear(); // To clean up after us.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphSymmetryFinder::DistinguishNodeInPartition(
|
||||
int node, DynamicPartition* partition, std::vector<int>* new_singletons) {
|
||||
int node, DynamicPartition *partition, std::vector<int> *new_singletons) {
|
||||
const int original_num_parts = partition->NumParts();
|
||||
partition->Refine(std::vector<int>(1, node));
|
||||
RecursivelyRefinePartitionByAdjacency(partition->PartOf(node), partition);
|
||||
@@ -276,8 +281,8 @@ void GraphSymmetryFinder::DistinguishNodeInPartition(
|
||||
|
||||
namespace {
|
||||
void MergeNodeEquivalenceClassesAccordingToPermutation(
|
||||
const SparsePermutation& perm, MergingPartition* node_equivalence_classes,
|
||||
DenseDoublyLinkedList* sorted_representatives) {
|
||||
const SparsePermutation &perm, MergingPartition *node_equivalence_classes,
|
||||
DenseDoublyLinkedList *sorted_representatives) {
|
||||
for (int c = 0; c < perm.NumCycles(); ++c) {
|
||||
// TODO(user): use the global element->image iterator when it exists.
|
||||
int prev = -1;
|
||||
@@ -307,10 +312,10 @@ void MergeNodeEquivalenceClassesAccordingToPermutation(
|
||||
// representatives of the nodes of the targeted part are contiguous in that
|
||||
// linked list.
|
||||
void GetAllOtherRepresentativesInSamePartAs(
|
||||
int representative_node, const DynamicPartition& partition,
|
||||
const DenseDoublyLinkedList& representatives_sorted_by_index_in_partition,
|
||||
MergingPartition* node_equivalence_classes, // Only for debugging.
|
||||
std::vector<int>* pruned_other_nodes) {
|
||||
int representative_node, const DynamicPartition &partition,
|
||||
const DenseDoublyLinkedList &representatives_sorted_by_index_in_partition,
|
||||
MergingPartition *node_equivalence_classes, // Only for debugging.
|
||||
std::vector<int> *pruned_other_nodes) {
|
||||
pruned_other_nodes->clear();
|
||||
const int part_index = partition.PartOf(representative_node);
|
||||
// Iterate on all contiguous representatives after the initial one...
|
||||
@@ -318,7 +323,8 @@ void GetAllOtherRepresentativesInSamePartAs(
|
||||
while (true) {
|
||||
DCHECK_EQ(repr, node_equivalence_classes->GetRoot(repr));
|
||||
repr = representatives_sorted_by_index_in_partition.Prev(repr);
|
||||
if (repr < 0 || partition.PartOf(repr) != part_index) break;
|
||||
if (repr < 0 || partition.PartOf(repr) != part_index)
|
||||
break;
|
||||
pruned_other_nodes->push_back(repr);
|
||||
}
|
||||
// ... and then on all contiguous representatives *before* it.
|
||||
@@ -326,7 +332,8 @@ void GetAllOtherRepresentativesInSamePartAs(
|
||||
while (true) {
|
||||
DCHECK_EQ(repr, node_equivalence_classes->GetRoot(repr));
|
||||
repr = representatives_sorted_by_index_in_partition.Next(repr);
|
||||
if (repr < 0 || partition.PartOf(repr) != part_index) break;
|
||||
if (repr < 0 || partition.PartOf(repr) != part_index)
|
||||
break;
|
||||
pruned_other_nodes->push_back(repr);
|
||||
}
|
||||
|
||||
@@ -342,7 +349,8 @@ void GetAllOtherRepresentativesInSamePartAs(
|
||||
}
|
||||
}
|
||||
node_equivalence_classes->KeepOnlyOneNodePerPart(&expected_output);
|
||||
for (int& x : expected_output) x = node_equivalence_classes->GetRoot(x);
|
||||
for (int &x : expected_output)
|
||||
x = node_equivalence_classes->GetRoot(x);
|
||||
std::sort(expected_output.begin(), expected_output.end());
|
||||
std::vector<int> sorted_output = *pruned_other_nodes;
|
||||
std::sort(sorted_output.begin(), sorted_output.end());
|
||||
@@ -350,12 +358,12 @@ void GetAllOtherRepresentativesInSamePartAs(
|
||||
absl::StrJoin(sorted_output, " "));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
absl::Status GraphSymmetryFinder::FindSymmetries(
|
||||
double time_limit_seconds, std::vector<int>* node_equivalence_classes_io,
|
||||
std::vector<std::unique_ptr<SparsePermutation>>* generators,
|
||||
std::vector<int>* factorized_automorphism_group_size) {
|
||||
double time_limit_seconds, std::vector<int> *node_equivalence_classes_io,
|
||||
std::vector<std::unique_ptr<SparsePermutation> > *generators,
|
||||
std::vector<int> *factorized_automorphism_group_size) {
|
||||
// Initialization.
|
||||
time_limit_ = absl::make_unique<TimeLimit>(time_limit_seconds);
|
||||
IF_STATS_ENABLED(stats_.initialization_time.StartTimer());
|
||||
@@ -369,18 +377,18 @@ absl::Status GraphSymmetryFinder::FindSymmetries(
|
||||
// Break all inherent asymmetries in the graph.
|
||||
{
|
||||
ScopedTimeDistributionUpdater u(&stats_.initialization_refine_time);
|
||||
RecursivelyRefinePartitionByAdjacency(/*first_unrefined_part_index=*/0,
|
||||
RecursivelyRefinePartitionByAdjacency(/*first_unrefined_part_index=*/ 0,
|
||||
&base_partition);
|
||||
}
|
||||
if (time_limit_->LimitReached()) {
|
||||
return absl::Status(absl::StatusCode::kDeadlineExceeded,
|
||||
"During the initial refinement.");
|
||||
}
|
||||
VLOG(4) << "Base partition: "
|
||||
<< base_partition.DebugString(DynamicPartition::SORT_BY_PART);
|
||||
VLOG(4) << "Base partition: " << base_partition.DebugString(
|
||||
DynamicPartition::SORT_BY_PART);
|
||||
|
||||
MergingPartition node_equivalence_classes(NumNodes());
|
||||
std::vector<std::vector<int>> permutations_displacing_node(NumNodes());
|
||||
std::vector<std::vector<int> > permutations_displacing_node(NumNodes());
|
||||
std::vector<int> potential_root_image_nodes;
|
||||
IF_STATS_ENABLED(stats_.initialization_time.StopTimerAndAddElapsedTime());
|
||||
|
||||
@@ -426,8 +434,8 @@ absl::Status GraphSymmetryFinder::FindSymmetries(
|
||||
InvariantDiveState(invariant_node, base_partition.NumParts()));
|
||||
DistinguishNodeInPartition(invariant_node, &base_partition, nullptr);
|
||||
VLOG(4) << "Invariant dive: invariant node = " << invariant_node
|
||||
<< "; partition after: "
|
||||
<< base_partition.DebugString(DynamicPartition::SORT_BY_PART);
|
||||
<< "; partition after: " << base_partition.DebugString(
|
||||
DynamicPartition::SORT_BY_PART);
|
||||
if (time_limit_->LimitReached()) {
|
||||
return absl::Status(absl::StatusCode::kDeadlineExceeded,
|
||||
"During the invariant dive.");
|
||||
@@ -442,7 +450,8 @@ absl::Status GraphSymmetryFinder::FindSymmetries(
|
||||
|
||||
IF_STATS_ENABLED(stats_.main_search_time.StartTimer());
|
||||
while (!invariant_dive_stack.empty()) {
|
||||
if (time_limit_->LimitReached()) break;
|
||||
if (time_limit_->LimitReached())
|
||||
break;
|
||||
// Backtrack the last step of 1) (the invariant dive).
|
||||
IF_STATS_ENABLED(stats_.invariant_unroll_time.StartTimer());
|
||||
const int root_node = invariant_dive_stack.back().invariant_node;
|
||||
@@ -452,8 +461,8 @@ absl::Status GraphSymmetryFinder::FindSymmetries(
|
||||
base_partition.UndoRefineUntilNumPartsEqual(base_num_parts);
|
||||
image_partition.UndoRefineUntilNumPartsEqual(base_num_parts);
|
||||
VLOG(4) << "Backtracking invariant dive: root node = " << root_node
|
||||
<< "; partition: "
|
||||
<< base_partition.DebugString(DynamicPartition::SORT_BY_PART);
|
||||
<< "; partition: " << base_partition.DebugString(
|
||||
DynamicPartition::SORT_BY_PART);
|
||||
|
||||
// Now we'll try to map "root_node" to all image nodes that seem compatible
|
||||
// and that aren't "root_node" itself.
|
||||
@@ -485,7 +494,8 @@ absl::Status GraphSymmetryFinder::FindSymmetries(
|
||||
// Try to map "root_node" to all of its potential images. For each image,
|
||||
// we only care about finding a single compatible permutation, if it exists.
|
||||
while (!potential_root_image_nodes.empty()) {
|
||||
if (time_limit_->LimitReached()) break;
|
||||
if (time_limit_->LimitReached())
|
||||
break;
|
||||
VLOG(4) << "Potential (pruned) images of root node " << root_node
|
||||
<< " left: [" << absl::StrJoin(potential_root_image_nodes, " ")
|
||||
<< "].";
|
||||
@@ -551,9 +561,9 @@ namespace {
|
||||
// Knowing that we want to map some element of part #part_index of
|
||||
// "base_partition" to part #part_index of "image_partition", pick the "best"
|
||||
// such mapping, for the global search algorithm.
|
||||
inline void GetBestMapping(const DynamicPartition& base_partition,
|
||||
const DynamicPartition& image_partition,
|
||||
int part_index, int* base_node, int* image_node) {
|
||||
inline void GetBestMapping(const DynamicPartition &base_partition,
|
||||
const DynamicPartition &image_partition,
|
||||
int part_index, int *base_node, int *image_node) {
|
||||
// As of pending CL 66620435, we've loosely tried three variants of
|
||||
// GetBestMapping():
|
||||
// 1) Just take the first element of the base part, map it to the first
|
||||
@@ -566,8 +576,8 @@ inline void GetBestMapping(const DynamicPartition& base_partition,
|
||||
// Variant 2) gives the best results on most benchmarks, in terms of speed,
|
||||
// but 3) yields much smaller supports for the sat_holeXXX benchmarks, as
|
||||
// long as it's combined with the other tweak enabled by
|
||||
// FLAGS_minimize_permutation_support_size.
|
||||
if (FLAGS_minimize_permutation_support_size) {
|
||||
// absl::GetFlag(FLAGS_minimize_permutation_support_size).
|
||||
if (absl::GetFlag(FLAGS_minimize_permutation_support_size)) {
|
||||
// Variant 3).
|
||||
for (const int node : base_partition.ElementsInPart(part_index)) {
|
||||
if (image_partition.PartOf(node) == part_index) {
|
||||
@@ -588,18 +598,18 @@ inline void GetBestMapping(const DynamicPartition& base_partition,
|
||||
*image_node = *image_partition.ElementsInPart(part_index).begin();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
// TODO(user): refactor this method and its submethods into a dedicated class
|
||||
// whose members will be ominously accessed by all the class methods; most
|
||||
// notably the search state stack. This may improve readability.
|
||||
std::unique_ptr<SparsePermutation>
|
||||
GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
int root_node, int root_image_node, DynamicPartition* base_partition,
|
||||
DynamicPartition* image_partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation>>&
|
||||
int root_node, int root_image_node, DynamicPartition *base_partition,
|
||||
DynamicPartition *image_partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation> > &
|
||||
generators_found_so_far,
|
||||
const std::vector<std::vector<int>>& permutations_displacing_node) {
|
||||
const std::vector<std::vector<int> > &permutations_displacing_node) {
|
||||
// DCHECKs() and statistics.
|
||||
ScopedTimeDistributionUpdater search_time_updater(&stats_.search_time);
|
||||
DCHECK_EQ("", tmp_dynamic_permutation_.DebugString());
|
||||
@@ -618,9 +628,9 @@ GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
// Initialize the search: we can already distinguish "root_node" in the base
|
||||
// partition. See the comment below.
|
||||
search_states_.emplace_back(
|
||||
/*base_node=*/root_node, /*first_image_node=*/-1,
|
||||
/*num_parts_before_trying_to_map_base_node=*/base_partition->NumParts(),
|
||||
/*min_potential_mismatching_part_index=*/base_partition->NumParts());
|
||||
/*base_node=*/ root_node, /*first_image_node=*/ -1,
|
||||
/*num_parts_before_trying_to_map_base_node=*/ base_partition->NumParts(),
|
||||
/*min_potential_mismatching_part_index=*/ base_partition->NumParts());
|
||||
// We inject the image node directly as the "remaining_pruned_image_nodes".
|
||||
search_states_.back().remaining_pruned_image_nodes.assign(1, root_image_node);
|
||||
{
|
||||
@@ -628,7 +638,8 @@ GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
DistinguishNodeInPartition(root_node, base_partition, &base_singletons);
|
||||
}
|
||||
while (!search_states_.empty()) {
|
||||
if (time_limit_->LimitReached()) return nullptr;
|
||||
if (time_limit_->LimitReached())
|
||||
return nullptr;
|
||||
// When exploring a SearchState "ss", we're supposed to have:
|
||||
// - A base_partition that has already been refined on ss->base_node.
|
||||
// (base_singleton is the list of singletons created on the base
|
||||
@@ -639,10 +650,10 @@ GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
//
|
||||
// Also, one should note that the base partition (before its refinement on
|
||||
// base_node) was deemed compatible with the image partition as it is now.
|
||||
const SearchState& ss = search_states_.back();
|
||||
const int image_node = ss.first_image_node >= 0
|
||||
? ss.first_image_node
|
||||
: ss.remaining_pruned_image_nodes.back();
|
||||
const SearchState &ss = search_states_.back();
|
||||
const int image_node =
|
||||
ss.first_image_node >= 0 ? ss.first_image_node
|
||||
: ss.remaining_pruned_image_nodes.back();
|
||||
|
||||
// Statistics, DCHECKs.
|
||||
IF_STATS_ENABLED(stats_.search_depth.Add(search_states_.size()));
|
||||
@@ -773,7 +784,7 @@ GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
// 'current' image node of the upper SearchState (which might lead to us
|
||||
// backtracking it, and so on).
|
||||
while (!search_states_.empty()) {
|
||||
SearchState* const last_ss = &search_states_.back();
|
||||
SearchState *const last_ss = &search_states_.back();
|
||||
image_partition->UndoRefineUntilNumPartsEqual(
|
||||
last_ss->num_parts_before_trying_to_map_base_node);
|
||||
if (last_ss->first_image_node >= 0) {
|
||||
@@ -790,10 +801,10 @@ GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
// like incrementally maintaining the list of permutations compatible
|
||||
// with the partition so far.
|
||||
const int part = image_partition->PartOf(last_ss->first_image_node);
|
||||
last_ss->remaining_pruned_image_nodes.reserve(
|
||||
image_partition->SizeOfPart(part));
|
||||
last_ss->remaining_pruned_image_nodes.push_back(
|
||||
last_ss->first_image_node);
|
||||
last_ss->remaining_pruned_image_nodes
|
||||
.reserve(image_partition->SizeOfPart(part));
|
||||
last_ss->remaining_pruned_image_nodes
|
||||
.push_back(last_ss->first_image_node);
|
||||
for (const int e : image_partition->ElementsInPart(part)) {
|
||||
if (e != last_ss->first_image_node) {
|
||||
last_ss->remaining_pruned_image_nodes.push_back(e);
|
||||
@@ -812,7 +823,8 @@ GraphSymmetryFinder::FindOneSuitablePermutation(
|
||||
last_ss->first_image_node = -1;
|
||||
}
|
||||
last_ss->remaining_pruned_image_nodes.pop_back();
|
||||
if (!last_ss->remaining_pruned_image_nodes.empty()) break;
|
||||
if (!last_ss->remaining_pruned_image_nodes.empty())
|
||||
break;
|
||||
|
||||
VLOG(4) << "Backtracking one level up.";
|
||||
base_partition->UndoRefineUntilNumPartsEqual(
|
||||
@@ -854,9 +866,9 @@ GraphSymmetryFinder::TailsOfIncomingArcsTo(int node) const {
|
||||
}
|
||||
|
||||
void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition(
|
||||
const DynamicPartition& partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation>>& permutations,
|
||||
const std::vector<int>& permutation_indices, std::vector<int>* nodes) {
|
||||
const DynamicPartition &partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation> > &permutations,
|
||||
const std::vector<int> &permutation_indices, std::vector<int> *nodes) {
|
||||
VLOG(4) << " Pruning [" << absl::StrJoin(*nodes, ", ") << "]";
|
||||
// TODO(user): apply a smarter test to decide whether to do the pruning
|
||||
// or not: we can accurately estimate the cost of pruning (iterate through
|
||||
@@ -864,19 +876,20 @@ void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition(
|
||||
// the search below the state that we're currently in, times the expected
|
||||
// number of pruned nodes). Sometimes it may be better to skip the
|
||||
// pruning.
|
||||
if (nodes->size() <= 1) return;
|
||||
if (nodes->size() <= 1)
|
||||
return;
|
||||
|
||||
// Iterate on all targeted permutations. If they are compatible, apply
|
||||
// them to tmp_partition_ which will contain the incrementally merged
|
||||
// equivalence classes.
|
||||
std::vector<int>& tmp_nodes_on_support =
|
||||
tmp_stack_; // Rename, for readability.
|
||||
std::vector<int> &tmp_nodes_on_support =
|
||||
tmp_stack_; // Rename, for readability.
|
||||
DCHECK(tmp_nodes_on_support.empty());
|
||||
// TODO(user): investigate further optimizations: maybe it's possible
|
||||
// to incrementally maintain the set of permutations that is compatible
|
||||
// with the current partition, instead of recomputing it here?
|
||||
for (const int p : permutation_indices) {
|
||||
const SparsePermutation& permutation = *permutations[p];
|
||||
const SparsePermutation &permutation = *permutations[p];
|
||||
// First, a quick compatibility check: the permutation's cycles must be
|
||||
// smaller or equal to the size of the part that they are included in.
|
||||
bool compatible = true;
|
||||
@@ -888,7 +901,8 @@ void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!compatible) continue;
|
||||
if (!compatible)
|
||||
continue;
|
||||
// Now the full compatibility check: each cycle of the permutation must
|
||||
// be fully included in an image part.
|
||||
for (int c = 0; c < permutation.NumCycles(); ++c) {
|
||||
@@ -899,11 +913,12 @@ void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition(
|
||||
compatible = false;
|
||||
break;
|
||||
}
|
||||
part = partition.PartOf(node); // Initilization of 'part'.
|
||||
part = partition.PartOf(node); // Initilization of 'part'.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!compatible) continue;
|
||||
if (!compatible)
|
||||
continue;
|
||||
// The permutation is fully compatible!
|
||||
// TODO(user): ignore cycles that are outside of image_part.
|
||||
MergeNodeEquivalenceClassesAccordingToPermutation(permutation,
|
||||
@@ -929,17 +944,17 @@ void GraphSymmetryFinder::PruneOrbitsUnderPermutationsCompatibleWithPartition(
|
||||
}
|
||||
|
||||
bool GraphSymmetryFinder::ConfirmFullMatchOrFindNextMappingDecision(
|
||||
const DynamicPartition& base_partition,
|
||||
const DynamicPartition& image_partition,
|
||||
const DynamicPermutation& current_permutation_candidate,
|
||||
int* min_potential_mismatching_part_index_io, int* next_base_node,
|
||||
int* next_image_node) const {
|
||||
const DynamicPartition &base_partition,
|
||||
const DynamicPartition &image_partition,
|
||||
const DynamicPermutation ¤t_permutation_candidate,
|
||||
int *min_potential_mismatching_part_index_io, int *next_base_node,
|
||||
int *next_image_node) const {
|
||||
*next_base_node = -1;
|
||||
*next_image_node = -1;
|
||||
|
||||
// The following clause should be true most of the times, except in some
|
||||
// specific use cases.
|
||||
if (!FLAGS_minimize_permutation_support_size) {
|
||||
if (!absl::GetFlag(FLAGS_minimize_permutation_support_size)) {
|
||||
// First, we try to map the loose ends of the current permutations: these
|
||||
// loose ends can't be mapped to themselves, so we'll have to map them to
|
||||
// something anyway.
|
||||
@@ -958,10 +973,8 @@ bool GraphSymmetryFinder::ConfirmFullMatchOrFindNextMappingDecision(
|
||||
if (*next_base_node != -1) {
|
||||
// We found loose ends, but none that mapped to its own root. Just pick
|
||||
// any valid image.
|
||||
*next_image_node =
|
||||
*image_partition
|
||||
.ElementsInPart(base_partition.PartOf(*next_base_node))
|
||||
.begin();
|
||||
*next_image_node = *image_partition
|
||||
.ElementsInPart(base_partition.PartOf(*next_base_node)).begin();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1013,13 +1026,12 @@ bool GraphSymmetryFinder::ConfirmFullMatchOrFindNextMappingDecision(
|
||||
}
|
||||
|
||||
std::string GraphSymmetryFinder::SearchState::DebugString() const {
|
||||
return absl::StrFormat(
|
||||
"SearchState{ base_node=%d, first_image_node=%d,"
|
||||
" remaining_pruned_image_nodes=[%s],"
|
||||
" num_parts_before_trying_to_map_base_node=%d }",
|
||||
base_node, first_image_node,
|
||||
absl::StrJoin(remaining_pruned_image_nodes, " "),
|
||||
num_parts_before_trying_to_map_base_node);
|
||||
return absl::StrFormat("SearchState{ base_node=%d, first_image_node=%d,"
|
||||
" remaining_pruned_image_nodes=[%s],"
|
||||
" num_parts_before_trying_to_map_base_node=%d }",
|
||||
base_node, first_image_node,
|
||||
absl::StrJoin(remaining_pruned_image_nodes, " "),
|
||||
num_parts_before_trying_to_map_base_node);
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace operations_research {
|
||||
class SparsePermutation;
|
||||
|
||||
class GraphSymmetryFinder {
|
||||
public:
|
||||
public:
|
||||
typedef ::util::StaticGraph<> Graph;
|
||||
|
||||
// If the Graph passed to the GraphSymmetryFinder is undirected, i.e.
|
||||
@@ -55,12 +55,12 @@ class GraphSymmetryFinder {
|
||||
//
|
||||
// "graph" must not have multi-arcs.
|
||||
// TODO(user): support multi-arcs.
|
||||
GraphSymmetryFinder(const Graph& graph, bool is_undirected);
|
||||
GraphSymmetryFinder(const Graph &graph, bool is_undirected);
|
||||
|
||||
// Whether the given permutation is an automorphism of the graph given at
|
||||
// construction. This costs O(sum(degree(x))) (the sum is over all nodes x
|
||||
// that are displaced by the permutation).
|
||||
bool IsGraphAutomorphism(const DynamicPermutation& permutation) const;
|
||||
bool IsGraphAutomorphism(const DynamicPermutation &permutation) const;
|
||||
|
||||
// Find a set of generators of the automorphism subgroup of the graph that
|
||||
// respects the given node equivalence classes. The generators are themselves
|
||||
@@ -101,9 +101,9 @@ class GraphSymmetryFinder {
|
||||
// partially valid: its last element may be undervalued. But all prior
|
||||
// elements are valid factors of the automorphism group size.
|
||||
absl::Status FindSymmetries(
|
||||
double time_limit_seconds, std::vector<int>* node_equivalence_classes_io,
|
||||
std::vector<std::unique_ptr<SparsePermutation> >* generators,
|
||||
std::vector<int>* factorized_automorphism_group_size);
|
||||
double time_limit_seconds, std::vector<int> *node_equivalence_classes_io,
|
||||
std::vector<std::unique_ptr<SparsePermutation> > *generators,
|
||||
std::vector<int> *factorized_automorphism_group_size);
|
||||
|
||||
// Fully refine the partition of nodes, using the graph as symmetry breaker.
|
||||
// This means applying the following steps on each part P of the partition:
|
||||
@@ -117,7 +117,7 @@ class GraphSymmetryFinder {
|
||||
// already partially refined on all parts #0...#K, then you should set
|
||||
// "first_unrefined_part_index" to K+1.
|
||||
void RecursivelyRefinePartitionByAdjacency(int first_unrefined_part_index,
|
||||
DynamicPartition* partition);
|
||||
DynamicPartition *partition);
|
||||
|
||||
// **** Methods below are public FOR TESTING ONLY. ****
|
||||
|
||||
@@ -125,11 +125,11 @@ class GraphSymmetryFinder {
|
||||
// fully refined, further refine it by {node}, and propagate by adjacency.
|
||||
// Also, optionally collect all the new singletons of the partition in
|
||||
// "new_singletons", sorted by their part number in the partition.
|
||||
void DistinguishNodeInPartition(int node, DynamicPartition* partition,
|
||||
std::vector<int>* new_singletons_or_null);
|
||||
void DistinguishNodeInPartition(int node, DynamicPartition *partition,
|
||||
std::vector<int> *new_singletons_or_null);
|
||||
|
||||
private:
|
||||
const Graph& graph_;
|
||||
private:
|
||||
const Graph &graph_;
|
||||
|
||||
inline int NumNodes() const { return graph_.num_nodes(); }
|
||||
|
||||
@@ -145,8 +145,8 @@ class GraphSymmetryFinder {
|
||||
// vectors are empty, and TailsOfIncomingArcsTo() crashes.
|
||||
std::vector<int> flattened_reverse_adj_lists_;
|
||||
std::vector<int> reverse_adj_list_index_;
|
||||
util::BeginEndWrapper<std::vector<int>::const_iterator> TailsOfIncomingArcsTo(
|
||||
int node) const;
|
||||
util::BeginEndWrapper<std::vector<int>::const_iterator>
|
||||
TailsOfIncomingArcsTo(int node) const;
|
||||
|
||||
// Deadline management. Populated upon FindSymmetries().
|
||||
mutable std::unique_ptr<TimeLimit> time_limit_;
|
||||
@@ -162,11 +162,11 @@ class GraphSymmetryFinder {
|
||||
// is an inverted index from each node to all permutations (that we found)
|
||||
// that displace it.
|
||||
std::unique_ptr<SparsePermutation> FindOneSuitablePermutation(
|
||||
int root_node, int root_image_node, DynamicPartition* base_partition,
|
||||
DynamicPartition* image_partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation> >&
|
||||
int root_node, int root_image_node, DynamicPartition *base_partition,
|
||||
DynamicPartition *image_partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation> > &
|
||||
generators_found_so_far,
|
||||
const std::vector<std::vector<int> >& permutations_displacing_node);
|
||||
const std::vector<std::vector<int> > &permutations_displacing_node);
|
||||
|
||||
// Data structure used by FindOneSuitablePermutation(). See the .cc
|
||||
struct SearchState {
|
||||
@@ -187,8 +187,7 @@ class GraphSymmetryFinder {
|
||||
int min_potential_mismatching_part_index;
|
||||
|
||||
SearchState(int bn, int in, int np, int mi)
|
||||
: base_node(bn),
|
||||
first_image_node(in),
|
||||
: base_node(bn), first_image_node(in),
|
||||
num_parts_before_trying_to_map_base_node(np),
|
||||
min_potential_mismatching_part_index(mi) {}
|
||||
|
||||
@@ -211,11 +210,11 @@ class GraphSymmetryFinder {
|
||||
// even if the partitions aren't actually a full match, because it uses
|
||||
// fingerprints to compare part. This should almost never happen.
|
||||
bool ConfirmFullMatchOrFindNextMappingDecision(
|
||||
const DynamicPartition& base_partition,
|
||||
const DynamicPartition& image_partition,
|
||||
const DynamicPermutation& current_permutation_candidate,
|
||||
int* min_potential_mismatching_part_index_io, int* next_base_node,
|
||||
int* next_image_node) const;
|
||||
const DynamicPartition &base_partition,
|
||||
const DynamicPartition &image_partition,
|
||||
const DynamicPermutation ¤t_permutation_candidate,
|
||||
int *min_potential_mismatching_part_index_io, int *next_base_node,
|
||||
int *next_image_node) const;
|
||||
|
||||
// Subroutine of FindOneSuitablePermutation(), split out for modularity:
|
||||
// Keep only one node of "nodes" per orbit, where the orbits are described
|
||||
@@ -223,20 +222,20 @@ class GraphSymmetryFinder {
|
||||
// "permutation_indices" and that are compatible with "partition".
|
||||
// For each orbit, keep the first node that appears in "nodes".
|
||||
void PruneOrbitsUnderPermutationsCompatibleWithPartition(
|
||||
const DynamicPartition& partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation> >& all_permutations,
|
||||
const std::vector<int>& permutation_indices, std::vector<int>* nodes);
|
||||
const DynamicPartition &partition,
|
||||
const std::vector<std::unique_ptr<SparsePermutation> > &all_permutations,
|
||||
const std::vector<int> &permutation_indices, std::vector<int> *nodes);
|
||||
|
||||
// Temporary objects used by some of the class methods, and owned by the
|
||||
// class to avoid (costly) re-allocation. Their resting states are described
|
||||
// in the side comments; with N = NumNodes().
|
||||
DynamicPermutation tmp_dynamic_permutation_; // Identity(N)
|
||||
mutable std::vector<bool> tmp_node_mask_; // [0..N-1] = false
|
||||
std::vector<int> tmp_degree_; // [0..N-1] = 0.
|
||||
std::vector<int> tmp_stack_; // Empty.
|
||||
std::vector<std::vector<int> > tmp_nodes_with_degree_; // [0..N-1] = [].
|
||||
MergingPartition tmp_partition_; // Reset(N).
|
||||
std::vector<const SparsePermutation*> tmp_compatible_permutations_; // Empty.
|
||||
DynamicPermutation tmp_dynamic_permutation_; // Identity(N)
|
||||
mutable std::vector<bool> tmp_node_mask_; // [0..N-1] = false
|
||||
std::vector<int> tmp_degree_; // [0..N-1] = 0.
|
||||
std::vector<int> tmp_stack_; // Empty.
|
||||
std::vector<std::vector<int> > tmp_nodes_with_degree_; // [0..N-1] = [].
|
||||
MergingPartition tmp_partition_; // Reset(N).
|
||||
std::vector<const SparsePermutation *> tmp_compatible_permutations_; // Empty.
|
||||
|
||||
// Internal statistics, used for performance tuning and debugging.
|
||||
struct Stats : public StatsGroup {
|
||||
@@ -262,16 +261,19 @@ class GraphSymmetryFinder {
|
||||
"p ┣╸Mapping election / full match detection", this),
|
||||
map_election_std_mapping_time("q ┃ ┣╸Mapping elected", this),
|
||||
map_election_std_full_match_time("r ┃ ┗╸Full Match", this),
|
||||
automorphism_test_time("s ┣╸[Upon full match] Automorphism check",
|
||||
this),
|
||||
automorphism_test_time(
|
||||
"s ┣╸[Upon full match] Automorphism check", this),
|
||||
automorphism_test_fail_time("t ┃ ┣╸Fail", this),
|
||||
automorphism_test_success_time("u ┃ ┗╸Success", this),
|
||||
search_finalize_time("v ┣╸[Upon auto success] Finalization", this),
|
||||
search_finalize_time("v ┣╸[Upon auto success] Finalization",
|
||||
this),
|
||||
dynamic_permutation_undo_time(
|
||||
"w ┣╸[Upon auto fail, full] Dynamic permutation undo", this),
|
||||
"w ┣╸[Upon auto fail, full] Dynamic permutation undo",
|
||||
this),
|
||||
map_reelection_time(
|
||||
"x ┣╸[Upon auto fail, partial] Mapping re-election", this),
|
||||
non_singleton_search_time("y ┃ ┗╸Non-singleton search", this),
|
||||
non_singleton_search_time("y ┃ ┗╸Non-singleton search",
|
||||
this),
|
||||
backtracking_time("z ┗╸Backtracking", this),
|
||||
pruning_time("{ ┗╸Pruning", this),
|
||||
search_depth("~ Search Stats: search_depth", this) {}
|
||||
@@ -309,6 +311,6 @@ class GraphSymmetryFinder {
|
||||
mutable Stats stats_;
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_FIND_GRAPH_SYMMETRIES_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_FIND_GRAPH_SYMMETRIES_H_
|
||||
|
||||
@@ -29,7 +29,7 @@ class HungarianOptimizer {
|
||||
static constexpr int kHungarianOptimizerRowNotFound = -1;
|
||||
static constexpr int kHungarianOptimizerColNotFound = -2;
|
||||
|
||||
public:
|
||||
public:
|
||||
// Setup the initial conditions for the algorithm.
|
||||
|
||||
// Parameters: costs is a matrix of the cost of assigning each agent to
|
||||
@@ -38,25 +38,29 @@ class HungarianOptimizer {
|
||||
// be square (i.e. we can have different numbers of agents and tasks), but it
|
||||
// must be regular (i.e. there must be the same number of entries in each row
|
||||
// of the matrix).
|
||||
explicit HungarianOptimizer(const std::vector<std::vector<double>>& costs);
|
||||
explicit HungarianOptimizer(const std::vector<std::vector<double> > &costs);
|
||||
|
||||
// Find an assignment which maximizes the total cost.
|
||||
// Returns the assignment in the two vectors passed as argument.
|
||||
// preimage[i] is assigned to image[i].
|
||||
void Maximize(std::vector<int>* preimage, std::vector<int>* image);
|
||||
void Maximize(std::vector<int> *preimage, std::vector<int> *image);
|
||||
|
||||
// Like Maximize(), but minimizing the cost instead.
|
||||
void Minimize(std::vector<int>* preimage, std::vector<int>* image);
|
||||
void Minimize(std::vector<int> *preimage, std::vector<int> *image);
|
||||
|
||||
private:
|
||||
typedef void (HungarianOptimizer::*Step)();
|
||||
private:
|
||||
typedef void(HungarianOptimizer::*Step)();
|
||||
|
||||
typedef enum { NONE, PRIME, STAR } Mark;
|
||||
typedef enum {
|
||||
NONE,
|
||||
PRIME,
|
||||
STAR
|
||||
} Mark;
|
||||
|
||||
// Convert the final cost matrix into a set of assignments of preimage->image.
|
||||
// Returns the assignment in the two vectors passed as argument, the same as
|
||||
// Minimize and Maximize
|
||||
void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
|
||||
void FindAssignments(std::vector<int> *preimage, std::vector<int> *image);
|
||||
|
||||
// Is the cell (row, col) starred?
|
||||
bool IsStarred(int row, int col) const { return marks_[row][col] == STAR; }
|
||||
@@ -123,7 +127,7 @@ class HungarianOptimizer {
|
||||
|
||||
// Find an uncovered zero and store its coordinates in (zeroRow_, zeroCol_)
|
||||
// and return true, or return false if no such cell exists.
|
||||
bool FindZero(int* zero_row, int* zero_col) const;
|
||||
bool FindZero(int *zero_row, int *zero_col) const;
|
||||
|
||||
// Print the matrix to stdout (for debugging.)
|
||||
void PrintMatrix();
|
||||
@@ -177,7 +181,7 @@ class HungarianOptimizer {
|
||||
int matrix_size_;
|
||||
|
||||
// The expanded cost matrix.
|
||||
std::vector<std::vector<double>> costs_;
|
||||
std::vector<std::vector<double> > costs_;
|
||||
|
||||
// The greatest cost in the initial cost matrix.
|
||||
double max_cost_;
|
||||
@@ -187,14 +191,14 @@ class HungarianOptimizer {
|
||||
std::vector<bool> cols_covered_;
|
||||
|
||||
// The marks_ (star/prime/none) on each element of the cost matrix.
|
||||
std::vector<std::vector<Mark>> marks_;
|
||||
std::vector<std::vector<Mark> > marks_;
|
||||
|
||||
// The number of stars in each column - used to speed up coverStarredZeroes.
|
||||
std::vector<int> stars_in_col_;
|
||||
|
||||
// Representation of a path_ through the matrix - used in step 5.
|
||||
std::vector<int> preimage_; // i.e. the agents
|
||||
std::vector<int> image_; // i.e. the tasks
|
||||
std::vector<int> preimage_; // i.e. the agents
|
||||
std::vector<int> image_; // i.e. the tasks
|
||||
|
||||
// The width_ and height_ of the initial (non-expanded) cost matrix.
|
||||
int width_;
|
||||
@@ -205,18 +209,9 @@ class HungarianOptimizer {
|
||||
};
|
||||
|
||||
HungarianOptimizer::HungarianOptimizer(
|
||||
const std::vector<std::vector<double>>& costs)
|
||||
: matrix_size_(0),
|
||||
costs_(),
|
||||
max_cost_(0),
|
||||
rows_covered_(),
|
||||
cols_covered_(),
|
||||
marks_(),
|
||||
stars_in_col_(),
|
||||
preimage_(),
|
||||
image_(),
|
||||
width_(0),
|
||||
height_(0),
|
||||
const std::vector<std::vector<double> > &costs)
|
||||
: matrix_size_(0), costs_(), max_cost_(0), rows_covered_(), cols_covered_(),
|
||||
marks_(), stars_in_col_(), preimage_(), image_(), width_(0), height_(0),
|
||||
state_(nullptr) {
|
||||
width_ = costs.size();
|
||||
|
||||
@@ -270,8 +265,8 @@ HungarianOptimizer::HungarianOptimizer(
|
||||
// Find an assignment which maximizes the total cost.
|
||||
// Return an array of pairs of integers. Each pair (i, j) corresponds to
|
||||
// assigning agent i to task j.
|
||||
void HungarianOptimizer::Maximize(std::vector<int>* preimage,
|
||||
std::vector<int>* image) {
|
||||
void HungarianOptimizer::Maximize(std::vector<int> *preimage,
|
||||
std::vector<int> *image) {
|
||||
// Find a maximal assignment by subtracting each of the
|
||||
// original costs from max_cost_ and then minimizing.
|
||||
for (int row = 0; row < width_; ++row) {
|
||||
@@ -285,8 +280,8 @@ void HungarianOptimizer::Maximize(std::vector<int>* preimage,
|
||||
// Find an assignment which minimizes the total cost.
|
||||
// Return an array of pairs of integers. Each pair (i, j) corresponds to
|
||||
// assigning agent i to task j.
|
||||
void HungarianOptimizer::Minimize(std::vector<int>* preimage,
|
||||
std::vector<int>* image) {
|
||||
void HungarianOptimizer::Minimize(std::vector<int> *preimage,
|
||||
std::vector<int> *image) {
|
||||
DoMunkres();
|
||||
FindAssignments(preimage, image);
|
||||
}
|
||||
@@ -294,8 +289,8 @@ void HungarianOptimizer::Minimize(std::vector<int>* preimage,
|
||||
// Convert the final cost matrix into a set of assignments of agents -> tasks.
|
||||
// Return an array of pairs of integers, the same as the return values of
|
||||
// Minimize() and Maximize()
|
||||
void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
|
||||
std::vector<int>* image) {
|
||||
void HungarianOptimizer::FindAssignments(std::vector<int> *preimage,
|
||||
std::vector<int> *image) {
|
||||
preimage->clear();
|
||||
image->clear();
|
||||
for (int row = 0; row < width_; ++row) {
|
||||
@@ -396,7 +391,7 @@ double HungarianOptimizer::FindSmallestUncovered() const {
|
||||
|
||||
// Find an uncovered zero and store its co-ordinates in (zeroRow, zeroCol)
|
||||
// and return true, or return false if no such cell exists.
|
||||
bool HungarianOptimizer::FindZero(int* zero_row, int* zero_col) const {
|
||||
bool HungarianOptimizer::FindZero(int *zero_row, int *zero_col) const {
|
||||
for (int row = 0; row < matrix_size_; ++row) {
|
||||
if (RowCovered(row)) {
|
||||
continue;
|
||||
@@ -642,9 +637,9 @@ void HungarianOptimizer::AugmentPath() {
|
||||
state_ = &HungarianOptimizer::PrimeZeroes;
|
||||
}
|
||||
|
||||
bool InputContainsNan(const std::vector<std::vector<double>>& input) {
|
||||
for (const auto& subvector : input) {
|
||||
for (const auto& num : subvector) {
|
||||
bool InputContainsNan(const std::vector<std::vector<double> > &input) {
|
||||
for (const auto &subvector : input) {
|
||||
for (const auto &num : subvector) {
|
||||
if (std::isnan(num)) {
|
||||
LOG(ERROR) << "The provided input contains " << num << ".";
|
||||
return true;
|
||||
@@ -654,10 +649,10 @@ bool InputContainsNan(const std::vector<std::vector<double>>& input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MinimizeLinearAssignment(
|
||||
const std::vector<std::vector<double>>& cost,
|
||||
absl::flat_hash_map<int, int>* direct_assignment,
|
||||
absl::flat_hash_map<int, int>* reverse_assignment) {
|
||||
void
|
||||
MinimizeLinearAssignment(const std::vector<std::vector<double> > &cost,
|
||||
absl::flat_hash_map<int, int> *direct_assignment,
|
||||
absl::flat_hash_map<int, int> *reverse_assignment) {
|
||||
if (InputContainsNan(cost)) {
|
||||
LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
|
||||
return;
|
||||
@@ -672,10 +667,10 @@ void MinimizeLinearAssignment(
|
||||
}
|
||||
}
|
||||
|
||||
void MaximizeLinearAssignment(
|
||||
const std::vector<std::vector<double>>& cost,
|
||||
absl::flat_hash_map<int, int>* direct_assignment,
|
||||
absl::flat_hash_map<int, int>* reverse_assignment) {
|
||||
void
|
||||
MaximizeLinearAssignment(const std::vector<std::vector<double> > &cost,
|
||||
absl::flat_hash_map<int, int> *direct_assignment,
|
||||
absl::flat_hash_map<int, int> *reverse_assignment) {
|
||||
if (InputContainsNan(cost)) {
|
||||
LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
|
||||
return;
|
||||
@@ -690,4 +685,4 @@ void MaximizeLinearAssignment(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -45,17 +45,17 @@
|
||||
namespace operations_research {
|
||||
|
||||
// See IMPORTANT NOTE at the top of the file.
|
||||
void MinimizeLinearAssignment(
|
||||
const std::vector<std::vector<double> >& cost,
|
||||
absl::flat_hash_map<int, int>* direct_assignment,
|
||||
absl::flat_hash_map<int, int>* reverse_assignment);
|
||||
void
|
||||
MinimizeLinearAssignment(const std::vector<std::vector<double> > &cost,
|
||||
absl::flat_hash_map<int, int> *direct_assignment,
|
||||
absl::flat_hash_map<int, int> *reverse_assignment);
|
||||
|
||||
// See IMPORTANT NOTE at the top of the file.
|
||||
void MaximizeLinearAssignment(
|
||||
const std::vector<std::vector<double> >& cost,
|
||||
absl::flat_hash_map<int, int>* direct_assignment,
|
||||
absl::flat_hash_map<int, int>* reverse_assignment);
|
||||
void
|
||||
MaximizeLinearAssignment(const std::vector<std::vector<double> > &cost,
|
||||
absl::flat_hash_map<int, int> *direct_assignment,
|
||||
absl::flat_hash_map<int, int> *reverse_assignment);
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_HUNGARIAN_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_HUNGARIAN_H_
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace operations_research {
|
||||
// result as well as whether the result is the expected one.
|
||||
|
||||
void GenericCheck(const int expected_assignment_size,
|
||||
const absl::flat_hash_map<int, int>& direct_assignment,
|
||||
const absl::flat_hash_map<int, int>& reverse_assignment,
|
||||
const absl::flat_hash_map<int, int> &direct_assignment,
|
||||
const absl::flat_hash_map<int, int> &reverse_assignment,
|
||||
const int expected_agents[], const int expected_tasks[]) {
|
||||
EXPECT_EQ(expected_assignment_size, direct_assignment.size());
|
||||
EXPECT_EQ(expected_assignment_size, reverse_assignment.size());
|
||||
@@ -26,14 +26,14 @@ void GenericCheck(const int expected_assignment_size,
|
||||
EXPECT_EQ(gtl::FindOrDie(reverse_assignment, expected_tasks[i]),
|
||||
expected_agents[i]);
|
||||
}
|
||||
for (const auto& direct_iter : direct_assignment) {
|
||||
for (const auto &direct_iter : direct_assignment) {
|
||||
EXPECT_EQ(gtl::FindOrDie(reverse_assignment, direct_iter.second),
|
||||
direct_iter.first)
|
||||
<< direct_iter.first << " -> " << direct_iter.second;
|
||||
}
|
||||
}
|
||||
|
||||
void TestMinimization(const std::vector<std::vector<double> >& cost,
|
||||
void TestMinimization(const std::vector<std::vector<double> > &cost,
|
||||
const int expected_assignment_size,
|
||||
const int expected_agents[], const int expected_tasks[]) {
|
||||
absl::flat_hash_map<int, int> direct_assignment;
|
||||
@@ -44,7 +44,7 @@ void TestMinimization(const std::vector<std::vector<double> >& cost,
|
||||
expected_agents, expected_tasks);
|
||||
}
|
||||
|
||||
void TestMaximization(const std::vector<std::vector<double> >& cost,
|
||||
void TestMaximization(const std::vector<std::vector<double> > &cost,
|
||||
const int expected_assignment_size,
|
||||
const int expected_agents[], const int expected_tasks[]) {
|
||||
absl::flat_hash_map<int, int> direct_assignment;
|
||||
@@ -59,30 +59,30 @@ void TestMaximization(const std::vector<std::vector<double> >& cost,
|
||||
|
||||
TEST(LinearAssignmentTest, NullMatrix) {
|
||||
std::vector<std::vector<double> > cost;
|
||||
const int* expected_agents = NULL;
|
||||
const int* expected_tasks = NULL;
|
||||
const int *expected_agents = NULL;
|
||||
const int *expected_tasks = NULL;
|
||||
TestMinimization(cost, 0, expected_agents, expected_tasks);
|
||||
TestMaximization(cost, 0, expected_agents, expected_tasks);
|
||||
}
|
||||
|
||||
#define MATRIX_TEST \
|
||||
{ \
|
||||
std::vector<std::vector<double> > cost(kMatrixHeight); \
|
||||
for (int row = 0; row < kMatrixHeight; ++row) { \
|
||||
cost[row].resize(kMatrixWidth); \
|
||||
for (int col = 0; col < kMatrixWidth; ++col) { \
|
||||
cost[row][col] = kCost[row][col]; \
|
||||
} \
|
||||
} \
|
||||
EXPECT_EQ(arraysize(expected_agents_for_min), \
|
||||
arraysize(expected_tasks_for_min)); \
|
||||
EXPECT_EQ(arraysize(expected_agents_for_max), \
|
||||
arraysize(expected_tasks_for_max)); \
|
||||
const int assignment_size = arraysize(expected_agents_for_max); \
|
||||
TestMinimization(cost, assignment_size, expected_agents_for_min, \
|
||||
expected_tasks_for_min); \
|
||||
TestMaximization(cost, assignment_size, expected_agents_for_max, \
|
||||
expected_tasks_for_max); \
|
||||
#define MATRIX_TEST \
|
||||
{ \
|
||||
std::vector<std::vector<double> > cost(kMatrixHeight); \
|
||||
for (int row = 0; row < kMatrixHeight; ++row) { \
|
||||
cost[row].resize(kMatrixWidth); \
|
||||
for (int col = 0; col < kMatrixWidth; ++col) { \
|
||||
cost[row][col] = kCost[row][col]; \
|
||||
} \
|
||||
} \
|
||||
EXPECT_EQ(arraysize(expected_agents_for_min), \
|
||||
arraysize(expected_tasks_for_min)); \
|
||||
EXPECT_EQ(arraysize(expected_agents_for_max), \
|
||||
arraysize(expected_tasks_for_max)); \
|
||||
const int assignment_size = arraysize(expected_agents_for_max); \
|
||||
TestMinimization(cost, assignment_size, expected_agents_for_min, \
|
||||
expected_tasks_for_min); \
|
||||
TestMaximization(cost, assignment_size, expected_agents_for_max, \
|
||||
expected_tasks_for_max); \
|
||||
}
|
||||
|
||||
// Test on a 1x1 matrix
|
||||
@@ -90,11 +90,11 @@ TEST(LinearAssignmentTest, NullMatrix) {
|
||||
TEST(LinearAssignmentTest, SizeOneMatrix) {
|
||||
const int kMatrixHeight = 1;
|
||||
const int kMatrixWidth = 1;
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = {{4}};
|
||||
const int expected_agents_for_min[] = {0};
|
||||
const int expected_tasks_for_min[] = {0};
|
||||
const int expected_agents_for_max[] = {0};
|
||||
const int expected_tasks_for_max[] = {0};
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = { { 4 } };
|
||||
const int expected_agents_for_min[] = { 0 };
|
||||
const int expected_tasks_for_min[] = { 0 };
|
||||
const int expected_agents_for_max[] = { 0 };
|
||||
const int expected_tasks_for_max[] = { 0 };
|
||||
MATRIX_TEST;
|
||||
}
|
||||
|
||||
@@ -103,14 +103,14 @@ TEST(LinearAssignmentTest, SizeOneMatrix) {
|
||||
TEST(LinearAssignmentTest, Small4x4Matrix) {
|
||||
const int kMatrixHeight = 4;
|
||||
const int kMatrixWidth = 4;
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = {{90, 75, 75, 80},
|
||||
{35, 85, 55, 65},
|
||||
{125, 95, 90, 105},
|
||||
{45, 110, 95, 115}};
|
||||
const int expected_agents_for_min[] = {0, 1, 2, 3};
|
||||
const int expected_tasks_for_min[] = {3, 2, 1, 0};
|
||||
const int expected_agents_for_max[] = {0, 1, 2, 3};
|
||||
const int expected_tasks_for_max[] = {2, 1, 0, 3};
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = { { 90, 75, 75, 80 },
|
||||
{ 35, 85, 55, 65 },
|
||||
{ 125, 95, 90, 105 },
|
||||
{ 45, 110, 95, 115 } };
|
||||
const int expected_agents_for_min[] = { 0, 1, 2, 3 };
|
||||
const int expected_tasks_for_min[] = { 3, 2, 1, 0 };
|
||||
const int expected_agents_for_max[] = { 0, 1, 2, 3 };
|
||||
const int expected_tasks_for_max[] = { 2, 1, 0, 3 };
|
||||
MATRIX_TEST;
|
||||
}
|
||||
|
||||
@@ -118,12 +118,13 @@ TEST(LinearAssignmentTest, Small4x4Matrix) {
|
||||
TEST(LinearAssignmentTest, Small3x4Matrix) {
|
||||
const int kMatrixHeight = 3;
|
||||
const int kMatrixWidth = 4;
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = {
|
||||
{90, 75, 75, 80}, {35, 85, 55, 65}, {125, 95, 90, 105}};
|
||||
const int expected_agents_for_min[] = {0, 1, 2};
|
||||
const int expected_tasks_for_min[] = {1, 0, 2};
|
||||
const int expected_agents_for_max[] = {0, 1, 2};
|
||||
const int expected_tasks_for_max[] = {3, 1, 0};
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = { { 90, 75, 75, 80 },
|
||||
{ 35, 85, 55, 65 },
|
||||
{ 125, 95, 90, 105 } };
|
||||
const int expected_agents_for_min[] = { 0, 1, 2 };
|
||||
const int expected_tasks_for_min[] = { 1, 0, 2 };
|
||||
const int expected_agents_for_max[] = { 0, 1, 2 };
|
||||
const int expected_tasks_for_max[] = { 3, 1, 0 };
|
||||
MATRIX_TEST;
|
||||
}
|
||||
|
||||
@@ -132,14 +133,15 @@ TEST(LinearAssignmentTest, Small4x3Matrix) {
|
||||
const int kMatrixHeight = 4;
|
||||
const int kMatrixWidth = 3;
|
||||
const double kCost[kMatrixHeight][kMatrixWidth] = {
|
||||
{90, 75, 75}, {35, 85, 55}, {125, 95, 90}, {45, 110, 95}};
|
||||
const int expected_agents_for_min[] = {0, 1, 3};
|
||||
const int expected_tasks_for_min[] = {1, 2, 0};
|
||||
const int expected_agents_for_max[] = {0, 2, 3};
|
||||
const int expected_tasks_for_max[] = {2, 0, 1};
|
||||
{ 90, 75, 75 }, { 35, 85, 55 }, { 125, 95, 90 }, { 45, 110, 95 }
|
||||
};
|
||||
const int expected_agents_for_min[] = { 0, 1, 3 };
|
||||
const int expected_tasks_for_min[] = { 1, 2, 0 };
|
||||
const int expected_agents_for_max[] = { 0, 2, 3 };
|
||||
const int expected_tasks_for_max[] = { 2, 0, 1 };
|
||||
MATRIX_TEST;
|
||||
}
|
||||
|
||||
#undef MATRIX_TEST
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -37,8 +37,8 @@ const int kMaxNumberOf64Items = 64;
|
||||
struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
|
||||
explicit CompareKnapsackItemsInDecreasingEfficiencyOrder(int64 _profit_max)
|
||||
: profit_max(_profit_max) {}
|
||||
bool operator()(const KnapsackItemPtr& item1,
|
||||
const KnapsackItemPtr& item2) const {
|
||||
bool operator()(const KnapsackItemPtr &item1,
|
||||
const KnapsackItemPtr &item2) const {
|
||||
return item1->GetEfficiency(profit_max) > item2->GetEfficiency(profit_max);
|
||||
}
|
||||
const int64 profit_max;
|
||||
@@ -50,8 +50,8 @@ struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
|
||||
// prefer the one with the highest current profit, ie. usually the one closer
|
||||
// to a leaf. In practice, the main advantage is to have smaller path.
|
||||
struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
|
||||
bool operator()(const KnapsackSearchNode* node_1,
|
||||
const KnapsackSearchNode* node_2) const {
|
||||
bool operator()(const KnapsackSearchNode *node_1,
|
||||
const KnapsackSearchNode *node_2) const {
|
||||
const int64 profit_upper_bound_1 = node_1->profit_upper_bound();
|
||||
const int64 profit_upper_bound_2 = node_2->profit_upper_bound();
|
||||
if (profit_upper_bound_1 == profit_upper_bound_2) {
|
||||
@@ -62,9 +62,8 @@ struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
|
||||
};
|
||||
|
||||
typedef std::priority_queue<
|
||||
KnapsackSearchNode*, std::vector<KnapsackSearchNode*>,
|
||||
CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder>
|
||||
SearchQueue;
|
||||
KnapsackSearchNode *, std::vector<KnapsackSearchNode *>,
|
||||
CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder> SearchQueue;
|
||||
|
||||
// Returns true when value_1 * value_2 may overflow int64.
|
||||
inline bool WillProductOverflow(int64 value_1, int64 value_2) {
|
||||
@@ -79,7 +78,9 @@ inline bool WillProductOverflow(int64 value_1, int64 value_2) {
|
||||
// Returns an upper bound of (numerator_1 * numerator_2) / denominator
|
||||
int64 UpperBoundOfRatio(int64 numerator_1, int64 numerator_2,
|
||||
int64 denominator) {
|
||||
DCHECK_GT(denominator, int64{0});
|
||||
DCHECK_GT(denominator, int64 {
|
||||
0
|
||||
});
|
||||
if (!WillProductOverflow(numerator_1, numerator_2)) {
|
||||
const int64 numerator = numerator_1 * numerator_2;
|
||||
// Round to zero.
|
||||
@@ -95,26 +96,23 @@ int64 UpperBoundOfRatio(int64 numerator_1, int64 numerator_2,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
// ----- KnapsackSearchNode -----
|
||||
KnapsackSearchNode::KnapsackSearchNode(const KnapsackSearchNode* const parent,
|
||||
const KnapsackAssignment& assignment)
|
||||
: depth_((parent == nullptr) ? 0 : parent->depth() + 1),
|
||||
parent_(parent),
|
||||
assignment_(assignment),
|
||||
current_profit_(0),
|
||||
profit_upper_bound_(kint64max),
|
||||
next_item_id_(kNoSelection) {}
|
||||
KnapsackSearchNode::KnapsackSearchNode(const KnapsackSearchNode *const parent,
|
||||
const KnapsackAssignment &assignment)
|
||||
: depth_((parent == nullptr) ? 0 : parent->depth() + 1), parent_(parent),
|
||||
assignment_(assignment), current_profit_(0),
|
||||
profit_upper_bound_(kint64max), next_item_id_(kNoSelection) {}
|
||||
|
||||
// ----- KnapsackSearchPath -----
|
||||
KnapsackSearchPath::KnapsackSearchPath(const KnapsackSearchNode& from,
|
||||
const KnapsackSearchNode& to)
|
||||
KnapsackSearchPath::KnapsackSearchPath(const KnapsackSearchNode &from,
|
||||
const KnapsackSearchNode &to)
|
||||
: from_(from), via_(nullptr), to_(to) {}
|
||||
|
||||
void KnapsackSearchPath::Init() {
|
||||
const KnapsackSearchNode* node_from = MoveUpToDepth(from_, to_.depth());
|
||||
const KnapsackSearchNode* node_to = MoveUpToDepth(to_, from_.depth());
|
||||
const KnapsackSearchNode *node_from = MoveUpToDepth(from_, to_.depth());
|
||||
const KnapsackSearchNode *node_to = MoveUpToDepth(to_, from_.depth());
|
||||
CHECK_EQ(node_from->depth(), node_to->depth());
|
||||
|
||||
// Find common parent.
|
||||
@@ -125,9 +123,10 @@ void KnapsackSearchPath::Init() {
|
||||
via_ = node_from;
|
||||
}
|
||||
|
||||
const KnapsackSearchNode* KnapsackSearchPath::MoveUpToDepth(
|
||||
const KnapsackSearchNode& node, int depth) const {
|
||||
const KnapsackSearchNode* current_node = &node;
|
||||
const KnapsackSearchNode *
|
||||
KnapsackSearchPath::MoveUpToDepth(const KnapsackSearchNode &node,
|
||||
int depth) const {
|
||||
const KnapsackSearchNode *current_node = &node;
|
||||
while (current_node->depth() > depth) {
|
||||
current_node = current_node->parent();
|
||||
}
|
||||
@@ -144,7 +143,7 @@ void KnapsackState::Init(int number_of_items) {
|
||||
|
||||
// Returns false when the state is invalid.
|
||||
bool KnapsackState::UpdateState(bool revert,
|
||||
const KnapsackAssignment& assignment) {
|
||||
const KnapsackAssignment &assignment) {
|
||||
if (revert) {
|
||||
is_bound_[assignment.item_id] = false;
|
||||
} else {
|
||||
@@ -159,17 +158,14 @@ bool KnapsackState::UpdateState(bool revert,
|
||||
}
|
||||
|
||||
// ----- KnapsackPropagator -----
|
||||
KnapsackPropagator::KnapsackPropagator(const KnapsackState& state)
|
||||
: items_(),
|
||||
current_profit_(0),
|
||||
profit_lower_bound_(0),
|
||||
profit_upper_bound_(kint64max),
|
||||
state_(state) {}
|
||||
KnapsackPropagator::KnapsackPropagator(const KnapsackState &state)
|
||||
: items_(), current_profit_(0), profit_lower_bound_(0),
|
||||
profit_upper_bound_(kint64max), state_(state) {}
|
||||
|
||||
KnapsackPropagator::~KnapsackPropagator() { gtl::STLDeleteElements(&items_); }
|
||||
|
||||
void KnapsackPropagator::Init(const std::vector<int64>& profits,
|
||||
const std::vector<int64>& weights) {
|
||||
void KnapsackPropagator::Init(const std::vector<int64> &profits,
|
||||
const std::vector<int64> &weights) {
|
||||
const int number_of_items = profits.size();
|
||||
items_.assign(number_of_items, static_cast<KnapsackItemPtr>(nullptr));
|
||||
for (int i = 0; i < number_of_items; ++i) {
|
||||
@@ -182,7 +178,7 @@ void KnapsackPropagator::Init(const std::vector<int64>& profits,
|
||||
}
|
||||
|
||||
bool KnapsackPropagator::Update(bool revert,
|
||||
const KnapsackAssignment& assignment) {
|
||||
const KnapsackAssignment &assignment) {
|
||||
if (assignment.is_in) {
|
||||
if (revert) {
|
||||
current_profit_ -= items_[assignment.item_id]->profit;
|
||||
@@ -194,9 +190,9 @@ bool KnapsackPropagator::Update(bool revert,
|
||||
}
|
||||
|
||||
void KnapsackPropagator::CopyCurrentStateToSolution(
|
||||
bool has_one_propagator, std::vector<bool>* solution) const {
|
||||
bool has_one_propagator, std::vector<bool> *solution) const {
|
||||
CHECK(solution != nullptr);
|
||||
for (const KnapsackItem* const item : items_) {
|
||||
for (const KnapsackItem *const item : items_) {
|
||||
const int item_id = item->id;
|
||||
(*solution)[item_id] = state_.is_bound(item_id) && state_.is_in(item_id);
|
||||
}
|
||||
@@ -207,13 +203,9 @@ void KnapsackPropagator::CopyCurrentStateToSolution(
|
||||
|
||||
// ----- KnapsackCapacityPropagator -----
|
||||
KnapsackCapacityPropagator::KnapsackCapacityPropagator(
|
||||
const KnapsackState& state, int64 capacity)
|
||||
: KnapsackPropagator(state),
|
||||
capacity_(capacity),
|
||||
consumed_capacity_(0),
|
||||
break_item_id_(kNoSelection),
|
||||
sorted_items_(),
|
||||
profit_max_(0) {}
|
||||
const KnapsackState &state, int64 capacity)
|
||||
: KnapsackPropagator(state), capacity_(capacity), consumed_capacity_(0),
|
||||
break_item_id_(kNoSelection), sorted_items_(), profit_max_(0) {}
|
||||
|
||||
KnapsackCapacityPropagator::~KnapsackCapacityPropagator() {}
|
||||
|
||||
@@ -227,7 +219,7 @@ void KnapsackCapacityPropagator::ComputeProfitBounds() {
|
||||
int break_sorted_item_id = kNoSelection;
|
||||
const int number_of_sorted_items = sorted_items_.size();
|
||||
for (int sorted_id = 0; sorted_id < number_of_sorted_items; ++sorted_id) {
|
||||
const KnapsackItem* const item = sorted_items_[sorted_id];
|
||||
const KnapsackItem *const item = sorted_items_[sorted_id];
|
||||
if (!state().is_bound(item->id)) {
|
||||
break_item_id_ = item->id;
|
||||
|
||||
@@ -255,7 +247,7 @@ void KnapsackCapacityPropagator::InitPropagator() {
|
||||
break_item_id_ = kNoSelection;
|
||||
sorted_items_ = items();
|
||||
profit_max_ = 0;
|
||||
for (const KnapsackItem* const item : sorted_items_) {
|
||||
for (const KnapsackItem *const item : sorted_items_) {
|
||||
profit_max_ = std::max(profit_max_, item->profit);
|
||||
}
|
||||
++profit_max_;
|
||||
@@ -265,7 +257,7 @@ void KnapsackCapacityPropagator::InitPropagator() {
|
||||
|
||||
// Returns false when the propagator fails.
|
||||
bool KnapsackCapacityPropagator::UpdatePropagator(
|
||||
bool revert, const KnapsackAssignment& assignment) {
|
||||
bool revert, const KnapsackAssignment &assignment) {
|
||||
if (assignment.is_in) {
|
||||
if (revert) {
|
||||
consumed_capacity_ -= items()[assignment.item_id]->weight;
|
||||
@@ -280,10 +272,10 @@ bool KnapsackCapacityPropagator::UpdatePropagator(
|
||||
}
|
||||
|
||||
void KnapsackCapacityPropagator::CopyCurrentStateToSolutionPropagator(
|
||||
std::vector<bool>* solution) const {
|
||||
std::vector<bool> *solution) const {
|
||||
CHECK(solution != nullptr);
|
||||
int64 remaining_capacity = capacity_ - consumed_capacity_;
|
||||
for (const KnapsackItem* const item : sorted_items_) {
|
||||
for (const KnapsackItem *const item : sorted_items_) {
|
||||
if (!state().is_bound(item->id)) {
|
||||
if (remaining_capacity >= item->weight) {
|
||||
remaining_capacity -= item->weight;
|
||||
@@ -333,20 +325,17 @@ int64 KnapsackCapacityPropagator::GetAdditionalProfit(int64 remaining_capacity,
|
||||
}
|
||||
|
||||
// ----- KnapsackGenericSolver -----
|
||||
KnapsackGenericSolver::KnapsackGenericSolver(const std::string& solver_name)
|
||||
: BaseKnapsackSolver(solver_name),
|
||||
propagators_(),
|
||||
master_propagator_id_(kMasterPropagatorId),
|
||||
search_nodes_(),
|
||||
state_(),
|
||||
best_solution_profit_(0),
|
||||
best_solution_() {}
|
||||
KnapsackGenericSolver::KnapsackGenericSolver(const std::string &solver_name)
|
||||
: BaseKnapsackSolver(solver_name), propagators_(),
|
||||
master_propagator_id_(kMasterPropagatorId), search_nodes_(), state_(),
|
||||
best_solution_profit_(0), best_solution_() {}
|
||||
|
||||
KnapsackGenericSolver::~KnapsackGenericSolver() { Clear(); }
|
||||
|
||||
void KnapsackGenericSolver::Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
void
|
||||
KnapsackGenericSolver::Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
CHECK_EQ(capacities.size(), weights.size());
|
||||
|
||||
Clear();
|
||||
@@ -357,7 +346,7 @@ void KnapsackGenericSolver::Init(const std::vector<int64>& profits,
|
||||
for (int i = 0; i < number_of_dimensions; ++i) {
|
||||
CHECK_EQ(number_of_items, weights[i].size());
|
||||
|
||||
KnapsackCapacityPropagator* propagator =
|
||||
KnapsackCapacityPropagator *propagator =
|
||||
new KnapsackCapacityPropagator(state_, capacities[i]);
|
||||
propagator->Init(profits, weights[i]);
|
||||
propagators_.push_back(propagator);
|
||||
@@ -367,8 +356,8 @@ void KnapsackGenericSolver::Init(const std::vector<int64>& profits,
|
||||
|
||||
void KnapsackGenericSolver::GetLowerAndUpperBoundWhenItem(int item_id,
|
||||
bool is_item_in,
|
||||
int64* lower_bound,
|
||||
int64* upper_bound) {
|
||||
int64 *lower_bound,
|
||||
int64 *upper_bound) {
|
||||
CHECK(lower_bound != nullptr);
|
||||
CHECK(upper_bound != nullptr);
|
||||
KnapsackAssignment assignment(item_id, is_item_in);
|
||||
@@ -377,10 +366,9 @@ void KnapsackGenericSolver::GetLowerAndUpperBoundWhenItem(int item_id,
|
||||
*lower_bound = 0LL;
|
||||
*upper_bound = 0LL;
|
||||
} else {
|
||||
*lower_bound =
|
||||
(HasOnePropagator())
|
||||
? propagators_[master_propagator_id_]->profit_lower_bound()
|
||||
: 0LL;
|
||||
*lower_bound = (HasOnePropagator()) ? propagators_[master_propagator_id_]
|
||||
->profit_lower_bound()
|
||||
: 0LL;
|
||||
*upper_bound = GetAggregatedProfitUpperBound();
|
||||
}
|
||||
|
||||
@@ -391,8 +379,8 @@ void KnapsackGenericSolver::GetLowerAndUpperBoundWhenItem(int item_id,
|
||||
}
|
||||
}
|
||||
|
||||
int64 KnapsackGenericSolver::Solve(TimeLimit* time_limit,
|
||||
bool* is_solution_optimal) {
|
||||
int64 KnapsackGenericSolver::Solve(TimeLimit *time_limit,
|
||||
bool *is_solution_optimal) {
|
||||
DCHECK(time_limit != nullptr);
|
||||
DCHECK(is_solution_optimal != nullptr);
|
||||
best_solution_profit_ = 0LL;
|
||||
@@ -400,7 +388,7 @@ int64 KnapsackGenericSolver::Solve(TimeLimit* time_limit,
|
||||
|
||||
SearchQueue search_queue;
|
||||
const KnapsackAssignment assignment(kNoSelection, true);
|
||||
KnapsackSearchNode* root_node = new KnapsackSearchNode(nullptr, assignment);
|
||||
KnapsackSearchNode *root_node = new KnapsackSearchNode(nullptr, assignment);
|
||||
root_node->set_current_profit(GetCurrentProfit());
|
||||
root_node->set_profit_upper_bound(GetAggregatedProfitUpperBound());
|
||||
root_node->set_next_item_id(GetNextItemId());
|
||||
@@ -413,14 +401,14 @@ int64 KnapsackGenericSolver::Solve(TimeLimit* time_limit,
|
||||
search_queue.push(search_nodes_.back());
|
||||
}
|
||||
|
||||
KnapsackSearchNode* current_node = root_node;
|
||||
KnapsackSearchNode *current_node = root_node;
|
||||
while (!search_queue.empty() &&
|
||||
search_queue.top()->profit_upper_bound() > best_solution_profit_) {
|
||||
if (time_limit->LimitReached()) {
|
||||
*is_solution_optimal = false;
|
||||
break;
|
||||
}
|
||||
KnapsackSearchNode* const node = search_queue.top();
|
||||
KnapsackSearchNode *const node = search_queue.top();
|
||||
search_queue.pop();
|
||||
|
||||
if (node != current_node) {
|
||||
@@ -447,11 +435,11 @@ void KnapsackGenericSolver::Clear() {
|
||||
}
|
||||
|
||||
// Returns false when at least one propagator fails.
|
||||
bool KnapsackGenericSolver::UpdatePropagators(const KnapsackSearchPath& path) {
|
||||
bool KnapsackGenericSolver::UpdatePropagators(const KnapsackSearchPath &path) {
|
||||
bool no_fail = true;
|
||||
// Revert previous changes.
|
||||
const KnapsackSearchNode* node = &path.from();
|
||||
const KnapsackSearchNode* via = &path.via();
|
||||
const KnapsackSearchNode *node = &path.from();
|
||||
const KnapsackSearchNode *via = &path.via();
|
||||
while (node != via) {
|
||||
no_fail = IncrementalUpdate(true, node->assignment()) && no_fail;
|
||||
node = node->parent();
|
||||
@@ -467,7 +455,7 @@ bool KnapsackGenericSolver::UpdatePropagators(const KnapsackSearchPath& path) {
|
||||
|
||||
int64 KnapsackGenericSolver::GetAggregatedProfitUpperBound() const {
|
||||
int64 upper_bound = kint64max;
|
||||
for (KnapsackPropagator* const prop : propagators_) {
|
||||
for (KnapsackPropagator *const prop : propagators_) {
|
||||
prop->ComputeProfitBounds();
|
||||
const int64 propagator_upper_bound = prop->profit_upper_bound();
|
||||
upper_bound = std::min(upper_bound, propagator_upper_bound);
|
||||
@@ -475,7 +463,7 @@ int64 KnapsackGenericSolver::GetAggregatedProfitUpperBound() const {
|
||||
return upper_bound;
|
||||
}
|
||||
|
||||
bool KnapsackGenericSolver::MakeNewNode(const KnapsackSearchNode& node,
|
||||
bool KnapsackGenericSolver::MakeNewNode(const KnapsackSearchNode &node,
|
||||
bool is_in) {
|
||||
if (node.next_item_id() == kNoSelection) {
|
||||
return false;
|
||||
@@ -503,7 +491,7 @@ bool KnapsackGenericSolver::MakeNewNode(const KnapsackSearchNode& node,
|
||||
}
|
||||
|
||||
// The node is relevant.
|
||||
KnapsackSearchNode* relevant_node = new KnapsackSearchNode(&node, assignment);
|
||||
KnapsackSearchNode *relevant_node = new KnapsackSearchNode(&node, assignment);
|
||||
relevant_node->set_current_profit(new_node.current_profit());
|
||||
relevant_node->set_profit_upper_bound(new_node.profit_upper_bound());
|
||||
relevant_node->set_next_item_id(new_node.next_item_id());
|
||||
@@ -512,12 +500,13 @@ bool KnapsackGenericSolver::MakeNewNode(const KnapsackSearchNode& node,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KnapsackGenericSolver::IncrementalUpdate(
|
||||
bool revert, const KnapsackAssignment& assignment) {
|
||||
bool
|
||||
KnapsackGenericSolver::IncrementalUpdate(bool revert,
|
||||
const KnapsackAssignment &assignment) {
|
||||
// Do not stop on a failure: To be able to be incremental on the update,
|
||||
// partial solution (state) and propagators must all be in the same state.
|
||||
bool no_fail = state_.UpdateState(revert, assignment);
|
||||
for (KnapsackPropagator* const prop : propagators_) {
|
||||
for (KnapsackPropagator *const prop : propagators_) {
|
||||
no_fail = prop->Update(revert, assignment) && no_fail;
|
||||
}
|
||||
return no_fail;
|
||||
@@ -531,8 +520,8 @@ void KnapsackGenericSolver::UpdateBestSolution() {
|
||||
|
||||
if (best_solution_profit_ < profit_lower_bound) {
|
||||
best_solution_profit_ = profit_lower_bound;
|
||||
propagators_[master_propagator_id_]->CopyCurrentStateToSolution(
|
||||
HasOnePropagator(), &best_solution_);
|
||||
propagators_[master_propagator_id_]
|
||||
->CopyCurrentStateToSolution(HasOnePropagator(), &best_solution_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,23 +531,23 @@ void KnapsackGenericSolver::UpdateBestSolution() {
|
||||
// Experiments show better results than KnapsackGenericSolver when the
|
||||
// number of items is less than 15.
|
||||
class KnapsackBruteForceSolver : public BaseKnapsackSolver {
|
||||
public:
|
||||
explicit KnapsackBruteForceSolver(const std::string& solver_name);
|
||||
public:
|
||||
explicit KnapsackBruteForceSolver(const std::string &solver_name);
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) override;
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) override;
|
||||
|
||||
// Solves the problem and returns the profit of the optimal solution.
|
||||
int64 Solve(TimeLimit* time_limit, bool* is_solution_optimal) override;
|
||||
int64 Solve(TimeLimit *time_limit, bool *is_solution_optimal) override;
|
||||
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
bool best_solution(int item_id) const override {
|
||||
return (best_solution_ & OneBit32(item_id)) != 0U;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int num_items_;
|
||||
int64 profits_weights_[kMaxNumberOfBruteForceItems * 2];
|
||||
int64 capacity_;
|
||||
@@ -569,17 +558,14 @@ class KnapsackBruteForceSolver : public BaseKnapsackSolver {
|
||||
};
|
||||
|
||||
KnapsackBruteForceSolver::KnapsackBruteForceSolver(
|
||||
const std::string& solver_name)
|
||||
: BaseKnapsackSolver(solver_name),
|
||||
num_items_(0),
|
||||
capacity_(0LL),
|
||||
best_solution_profit_(0LL),
|
||||
best_solution_(0U) {}
|
||||
const std::string &solver_name)
|
||||
: BaseKnapsackSolver(solver_name), num_items_(0), capacity_(0LL),
|
||||
best_solution_profit_(0LL), best_solution_(0U) {}
|
||||
|
||||
void KnapsackBruteForceSolver::Init(
|
||||
const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
void
|
||||
KnapsackBruteForceSolver::Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
// TODO(user): Implement multi-dimensional brute force solver.
|
||||
CHECK_EQ(weights.size(), 1)
|
||||
<< "Brute force solver only works with one dimension.";
|
||||
@@ -599,8 +585,8 @@ void KnapsackBruteForceSolver::Init(
|
||||
capacity_ = capacities.at(0);
|
||||
}
|
||||
|
||||
int64 KnapsackBruteForceSolver::Solve(TimeLimit* time_limit,
|
||||
bool* is_solution_optimal) {
|
||||
int64 KnapsackBruteForceSolver::Solve(TimeLimit *time_limit,
|
||||
bool *is_solution_optimal) {
|
||||
DCHECK(is_solution_optimal != nullptr);
|
||||
*is_solution_optimal = true;
|
||||
best_solution_profit_ = 0LL;
|
||||
@@ -620,12 +606,12 @@ int64 KnapsackBruteForceSolver::Solve(TimeLimit* time_limit,
|
||||
local_state = state;
|
||||
item_id = 0;
|
||||
while (diff_state) {
|
||||
if (diff_state & 1U) { // There is a diff.
|
||||
if (local_state & 1U) { // This item is now in the knapsack.
|
||||
if (diff_state & 1U) { // There is a diff.
|
||||
if (local_state & 1U) { // This item is now in the knapsack.
|
||||
sum_profit += profits_weights_[item_id];
|
||||
sum_weight += profits_weights_[item_id + 1];
|
||||
CHECK_LT(item_id + 1, 2 * num_items_);
|
||||
} else { // This item has been removed of the knapsack.
|
||||
} else { // This item has been removed of the knapsack.
|
||||
sum_profit -= profits_weights_[item_id];
|
||||
sum_weight -= profits_weights_[item_id + 1];
|
||||
CHECK_LT(item_id + 1, 2 * num_items_);
|
||||
@@ -654,9 +640,7 @@ int64 KnapsackBruteForceSolver::Solve(TimeLimit* time_limit,
|
||||
struct KnapsackItemWithEfficiency {
|
||||
KnapsackItemWithEfficiency(int _id, int64 _profit, int64 _weight,
|
||||
int64 _profit_max)
|
||||
: id(_id),
|
||||
profit(_profit),
|
||||
weight(_weight),
|
||||
: id(_id), profit(_profit), weight(_weight),
|
||||
efficiency((weight > 0) ? static_cast<double>(_profit) /
|
||||
static_cast<double>(_weight)
|
||||
: static_cast<double>(_profit_max)) {}
|
||||
@@ -672,25 +656,25 @@ struct KnapsackItemWithEfficiency {
|
||||
// items is less or equal to 64. This implementation is about 4 times faster
|
||||
// than KnapsackGenericSolver.
|
||||
class Knapsack64ItemsSolver : public BaseKnapsackSolver {
|
||||
public:
|
||||
explicit Knapsack64ItemsSolver(const std::string& solver_name);
|
||||
public:
|
||||
explicit Knapsack64ItemsSolver(const std::string &solver_name);
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) override;
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) override;
|
||||
|
||||
// Solves the problem and returns the profit of the optimal solution.
|
||||
int64 Solve(TimeLimit* time_limit, bool* is_solution_optimal) override;
|
||||
int64 Solve(TimeLimit *time_limit, bool *is_solution_optimal) override;
|
||||
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
bool best_solution(int item_id) const override {
|
||||
return (best_solution_ & OneBit64(item_id)) != 0ULL;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int GetBreakItemId(int64 capacity) const;
|
||||
void GetLowerAndUpperBound(int64* lower_bound, int64* upper_bound) const;
|
||||
void GetLowerAndUpperBound(int64 *lower_bound, int64 *upper_bound) const;
|
||||
void GoToNextState(bool has_failed);
|
||||
void BuildBestSolution();
|
||||
|
||||
@@ -715,30 +699,23 @@ class Knapsack64ItemsSolver : public BaseKnapsackSolver {
|
||||
|
||||
// Comparator used to sort item in decreasing efficiency order
|
||||
bool CompareKnapsackItemWithEfficiencyInDecreasingEfficiencyOrder(
|
||||
const KnapsackItemWithEfficiency& item1,
|
||||
const KnapsackItemWithEfficiency& item2) {
|
||||
const KnapsackItemWithEfficiency &item1,
|
||||
const KnapsackItemWithEfficiency &item2) {
|
||||
return item1.efficiency > item2.efficiency;
|
||||
}
|
||||
|
||||
// ----- Knapsack64ItemsSolver -----
|
||||
Knapsack64ItemsSolver::Knapsack64ItemsSolver(const std::string& solver_name)
|
||||
: BaseKnapsackSolver(solver_name),
|
||||
sorted_items_(),
|
||||
sum_profits_(),
|
||||
sum_weights_(),
|
||||
capacity_(0LL),
|
||||
state_(0ULL),
|
||||
state_depth_(0),
|
||||
best_solution_profit_(0LL),
|
||||
best_solution_(0ULL),
|
||||
best_solution_depth_(0),
|
||||
state_weight_(0LL),
|
||||
rejected_items_profit_(0LL),
|
||||
Knapsack64ItemsSolver::Knapsack64ItemsSolver(const std::string &solver_name)
|
||||
: BaseKnapsackSolver(solver_name), sorted_items_(), sum_profits_(),
|
||||
sum_weights_(), capacity_(0LL), state_(0ULL), state_depth_(0),
|
||||
best_solution_profit_(0LL), best_solution_(0ULL), best_solution_depth_(0),
|
||||
state_weight_(0LL), rejected_items_profit_(0LL),
|
||||
rejected_items_weight_(0LL) {}
|
||||
|
||||
void Knapsack64ItemsSolver::Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
void
|
||||
Knapsack64ItemsSolver::Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
CHECK_EQ(weights.size(), 1)
|
||||
<< "Brute force solver only works with one dimension.";
|
||||
CHECK_EQ(capacities.size(), weights.size());
|
||||
@@ -776,8 +753,8 @@ void Knapsack64ItemsSolver::Init(const std::vector<int64>& profits,
|
||||
}
|
||||
}
|
||||
|
||||
int64 Knapsack64ItemsSolver::Solve(TimeLimit* time_limit,
|
||||
bool* is_solution_optimal) {
|
||||
int64 Knapsack64ItemsSolver::Solve(TimeLimit *time_limit,
|
||||
bool *is_solution_optimal) {
|
||||
DCHECK(is_solution_optimal != nullptr);
|
||||
*is_solution_optimal = true;
|
||||
const int num_items = sorted_items_.size();
|
||||
@@ -826,8 +803,8 @@ int Knapsack64ItemsSolver::GetBreakItemId(int64 capacity) const {
|
||||
// Unfortunately, experiments show equivalent results with or without this
|
||||
// code optimization (only 1/7 of calls can be reused).
|
||||
// In order to simplify the code, this optimization is not implemented.
|
||||
void Knapsack64ItemsSolver::GetLowerAndUpperBound(int64* lower_bound,
|
||||
int64* upper_bound) const {
|
||||
void Knapsack64ItemsSolver::GetLowerAndUpperBound(int64 *lower_bound,
|
||||
int64 *upper_bound) const {
|
||||
const int64 available_capacity = capacity_ + rejected_items_weight_;
|
||||
const int break_item_id = GetBreakItemId(available_capacity);
|
||||
const int num_items = sorted_items_.size();
|
||||
@@ -854,23 +831,23 @@ void Knapsack64ItemsSolver::GetLowerAndUpperBound(int64* lower_bound,
|
||||
// (10% speed-up). That's the reason why the loop version is implemented.
|
||||
void Knapsack64ItemsSolver::GoToNextState(bool has_failed) {
|
||||
uint64 mask = OneBit64(state_depth_);
|
||||
if (!has_failed) { // Go to next item.
|
||||
if (!has_failed) { // Go to next item.
|
||||
++state_depth_;
|
||||
state_ = state_ | (mask << 1);
|
||||
state_weight_ += sorted_items_[state_depth_].weight;
|
||||
} else {
|
||||
// Backtrack to last item in.
|
||||
while ((state_ & mask) == 0ULL && state_depth_ >= 0) {
|
||||
const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
|
||||
const KnapsackItemWithEfficiency &item = sorted_items_[state_depth_];
|
||||
rejected_items_profit_ -= item.profit;
|
||||
rejected_items_weight_ -= item.weight;
|
||||
--state_depth_;
|
||||
mask = mask >> 1ULL;
|
||||
}
|
||||
|
||||
if (state_ & mask) { // Item was in, remove it.
|
||||
if (state_ & mask) { // Item was in, remove it.
|
||||
state_ = state_ & ~mask;
|
||||
const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
|
||||
const KnapsackItemWithEfficiency &item = sorted_items_[state_depth_];
|
||||
rejected_items_profit_ += item.profit;
|
||||
rejected_items_weight_ += item.weight;
|
||||
state_weight_ -= item.weight;
|
||||
@@ -928,23 +905,23 @@ void Knapsack64ItemsSolver::BuildBestSolution() {
|
||||
// The implemented algorithm is 'DP-3' in "Knapsack problems", Hans Kellerer,
|
||||
// Ulrich Pferschy and David Pisinger, Springer book (ISBN 978-3540402862).
|
||||
class KnapsackDynamicProgrammingSolver : public BaseKnapsackSolver {
|
||||
public:
|
||||
explicit KnapsackDynamicProgrammingSolver(const std::string& solver_name);
|
||||
public:
|
||||
explicit KnapsackDynamicProgrammingSolver(const std::string &solver_name);
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) override;
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) override;
|
||||
|
||||
// Solves the problem and returns the profit of the optimal solution.
|
||||
int64 Solve(TimeLimit* time_limit, bool* is_solution_optimal) override;
|
||||
int64 Solve(TimeLimit *time_limit, bool *is_solution_optimal) override;
|
||||
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
bool best_solution(int item_id) const override {
|
||||
return best_solution_.at(item_id);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int64 SolveSubProblem(int64 capacity, int num_items);
|
||||
|
||||
std::vector<int64> profits_;
|
||||
@@ -957,19 +934,14 @@ class KnapsackDynamicProgrammingSolver : public BaseKnapsackSolver {
|
||||
|
||||
// ----- KnapsackDynamicProgrammingSolver -----
|
||||
KnapsackDynamicProgrammingSolver::KnapsackDynamicProgrammingSolver(
|
||||
const std::string& solver_name)
|
||||
: BaseKnapsackSolver(solver_name),
|
||||
profits_(),
|
||||
weights_(),
|
||||
capacity_(0),
|
||||
computed_profits_(),
|
||||
selected_item_ids_(),
|
||||
best_solution_() {}
|
||||
const std::string &solver_name)
|
||||
: BaseKnapsackSolver(solver_name), profits_(), weights_(), capacity_(0),
|
||||
computed_profits_(), selected_item_ids_(), best_solution_() {}
|
||||
|
||||
void KnapsackDynamicProgrammingSolver::Init(
|
||||
const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
CHECK_EQ(weights.size(), 1)
|
||||
<< "Current implementation of the dynamic programming solver only deals"
|
||||
<< " with one dimension.";
|
||||
@@ -984,7 +956,9 @@ int64 KnapsackDynamicProgrammingSolver::SolveSubProblem(int64 capacity,
|
||||
int num_items) {
|
||||
const int64 capacity_plus_1 = capacity + 1;
|
||||
std::fill_n(selected_item_ids_.begin(), capacity_plus_1, 0);
|
||||
std::fill_n(computed_profits_.begin(), capacity_plus_1, int64{0});
|
||||
std::fill_n(computed_profits_.begin(), capacity_plus_1, int64 {
|
||||
0
|
||||
});
|
||||
for (int item_id = 0; item_id < num_items; ++item_id) {
|
||||
const int64 item_weight = weights_[item_id];
|
||||
const int64 item_profit = profits_[item_id];
|
||||
@@ -1001,8 +975,8 @@ int64 KnapsackDynamicProgrammingSolver::SolveSubProblem(int64 capacity,
|
||||
return selected_item_ids_.at(capacity);
|
||||
}
|
||||
|
||||
int64 KnapsackDynamicProgrammingSolver::Solve(TimeLimit* time_limit,
|
||||
bool* is_solution_optimal) {
|
||||
int64 KnapsackDynamicProgrammingSolver::Solve(TimeLimit *time_limit,
|
||||
bool *is_solution_optimal) {
|
||||
DCHECK(is_solution_optimal != nullptr);
|
||||
*is_solution_optimal = true;
|
||||
const int64 capacity_plus_1 = capacity_ + 1;
|
||||
@@ -1027,57 +1001,53 @@ int64 KnapsackDynamicProgrammingSolver::Solve(TimeLimit* time_limit,
|
||||
|
||||
// ----- KnapsackMIPSolver -----
|
||||
class KnapsackMIPSolver : public BaseKnapsackSolver {
|
||||
public:
|
||||
public:
|
||||
KnapsackMIPSolver(MPSolver::OptimizationProblemType problem_type,
|
||||
const std::string& solver_name);
|
||||
const std::string &solver_name);
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) override;
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) override;
|
||||
|
||||
// Solves the problem and returns the profit of the optimal solution.
|
||||
int64 Solve(TimeLimit* time_limit, bool* is_solution_optimal) override;
|
||||
int64 Solve(TimeLimit *time_limit, bool *is_solution_optimal) override;
|
||||
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
bool best_solution(int item_id) const override {
|
||||
return best_solution_.at(item_id);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
MPSolver::OptimizationProblemType problem_type_;
|
||||
std::vector<int64> profits_;
|
||||
std::vector<std::vector<int64>> weights_;
|
||||
std::vector<std::vector<int64> > weights_;
|
||||
std::vector<int64> capacities_;
|
||||
std::vector<bool> best_solution_;
|
||||
};
|
||||
|
||||
KnapsackMIPSolver::KnapsackMIPSolver(
|
||||
MPSolver::OptimizationProblemType problem_type,
|
||||
const std::string& solver_name)
|
||||
: BaseKnapsackSolver(solver_name),
|
||||
problem_type_(problem_type),
|
||||
profits_(),
|
||||
weights_(),
|
||||
capacities_(),
|
||||
best_solution_() {}
|
||||
const std::string &solver_name)
|
||||
: BaseKnapsackSolver(solver_name), problem_type_(problem_type), profits_(),
|
||||
weights_(), capacities_(), best_solution_() {}
|
||||
|
||||
void KnapsackMIPSolver::Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
void KnapsackMIPSolver::Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
profits_ = profits;
|
||||
weights_ = weights;
|
||||
capacities_ = capacities;
|
||||
}
|
||||
|
||||
int64 KnapsackMIPSolver::Solve(TimeLimit* time_limit,
|
||||
bool* is_solution_optimal) {
|
||||
int64 KnapsackMIPSolver::Solve(TimeLimit *time_limit,
|
||||
bool *is_solution_optimal) {
|
||||
DCHECK(is_solution_optimal != nullptr);
|
||||
*is_solution_optimal = true;
|
||||
MPSolver solver(GetName(), problem_type_);
|
||||
|
||||
const int num_items = profits_.size();
|
||||
std::vector<MPVariable*> variables;
|
||||
std::vector<MPVariable *> variables;
|
||||
solver.MakeBoolVarArray(num_items, "x", &variables);
|
||||
|
||||
// Add constraints.
|
||||
@@ -1086,7 +1056,7 @@ int64 KnapsackMIPSolver::Solve(TimeLimit* time_limit,
|
||||
<< "Weights should be vector of num_dimensions (" << num_dimensions
|
||||
<< ") vectors of size num_items (" << num_items << ").";
|
||||
for (int i = 0; i < num_dimensions; ++i) {
|
||||
MPConstraint* const ct = solver.MakeRowConstraint(0LL, capacities_.at(i));
|
||||
MPConstraint *const ct = solver.MakeRowConstraint(0LL, capacities_.at(i));
|
||||
for (int j = 0; j < num_items; ++j) {
|
||||
ct->SetCoefficient(variables.at(j), weights_.at(i).at(j));
|
||||
}
|
||||
@@ -1095,7 +1065,7 @@ int64 KnapsackMIPSolver::Solve(TimeLimit* time_limit,
|
||||
// Define objective to minimize. Minimization is used instead of maximization
|
||||
// because of an issue with CBC solver which does not always find the optimal
|
||||
// solution on maximization problems.
|
||||
MPObjective* const objective = solver.MutableObjective();
|
||||
MPObjective *const objective = solver.MutableObjective();
|
||||
for (int j = 0; j < num_items; ++j) {
|
||||
objective->SetCoefficient(variables.at(j), -profits_.at(j));
|
||||
}
|
||||
@@ -1116,75 +1086,69 @@ int64 KnapsackMIPSolver::Solve(TimeLimit* time_limit,
|
||||
}
|
||||
|
||||
// ----- KnapsackSolver -----
|
||||
KnapsackSolver::KnapsackSolver(const std::string& solver_name)
|
||||
KnapsackSolver::KnapsackSolver(const std::string &solver_name)
|
||||
: KnapsackSolver(KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER,
|
||||
solver_name) {}
|
||||
|
||||
KnapsackSolver::KnapsackSolver(SolverType solver_type,
|
||||
const std::string& solver_name)
|
||||
: solver_(),
|
||||
known_value_(),
|
||||
best_solution_(),
|
||||
mapping_reduced_item_id_(),
|
||||
is_problem_solved_(false),
|
||||
additional_profit_(0LL),
|
||||
use_reduction_(true),
|
||||
const std::string &solver_name)
|
||||
: solver_(), known_value_(), best_solution_(), mapping_reduced_item_id_(),
|
||||
is_problem_solved_(false), additional_profit_(0LL), use_reduction_(true),
|
||||
time_limit_seconds_(std::numeric_limits<double>::infinity()) {
|
||||
switch (solver_type) {
|
||||
case KNAPSACK_BRUTE_FORCE_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackBruteForceSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_64ITEMS_SOLVER:
|
||||
solver_ = absl::make_unique<Knapsack64ItemsSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER:
|
||||
solver_ =
|
||||
absl::make_unique<KnapsackDynamicProgrammingSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackGenericSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_BRUTE_FORCE_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackBruteForceSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_64ITEMS_SOLVER:
|
||||
solver_ = absl::make_unique<Knapsack64ItemsSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackDynamicProgrammingSolver>(solver_name);
|
||||
break;
|
||||
case KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackGenericSolver>(solver_name);
|
||||
break;
|
||||
#if defined(USE_CBC)
|
||||
case KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::CBC_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
#endif // USE_CBC
|
||||
case KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::CBC_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
#endif // USE_CBC
|
||||
#if defined(USE_SCIP)
|
||||
case KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
#endif // USE_SCIP
|
||||
case KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
#endif // USE_SCIP
|
||||
#if defined(USE_XPRESS)
|
||||
case KNAPSACK_MULTIDIMENSION_XPRESS_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
case KNAPSACK_MULTIDIMENSION_XPRESS_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_CPLEX)
|
||||
case KNAPSACK_MULTIDIMENSION_CPLEX_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
case KNAPSACK_MULTIDIMENSION_CPLEX_MIP_SOLVER:
|
||||
solver_ = absl::make_unique<KnapsackMIPSolver>(
|
||||
MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING, solver_name);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOG(FATAL) << "Unknown knapsack solver type.";
|
||||
default:
|
||||
LOG(FATAL) << "Unknown knapsack solver type.";
|
||||
}
|
||||
}
|
||||
|
||||
KnapsackSolver::~KnapsackSolver() {}
|
||||
|
||||
void KnapsackSolver::Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
void KnapsackSolver::Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
time_limit_ = absl::make_unique<TimeLimit>(time_limit_seconds_);
|
||||
is_solution_optimal_ = false;
|
||||
additional_profit_ = 0LL;
|
||||
is_problem_solved_ = false;
|
||||
|
||||
const int num_items = profits.size();
|
||||
std::vector<std::vector<int64>> reduced_weights;
|
||||
std::vector<std::vector<int64> > reduced_weights;
|
||||
std::vector<int64> reduced_capacities;
|
||||
if (use_reduction_) {
|
||||
const int num_reduced_items = ReduceCapacities(
|
||||
@@ -1216,10 +1180,10 @@ void KnapsackSolver::Init(const std::vector<int64>& profits,
|
||||
}
|
||||
|
||||
int KnapsackSolver::ReduceCapacities(
|
||||
int num_items, const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities,
|
||||
std::vector<std::vector<int64>>* reduced_weights,
|
||||
std::vector<int64>* reduced_capacities) {
|
||||
int num_items, const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities,
|
||||
std::vector<std::vector<int64> > *reduced_weights,
|
||||
std::vector<int64> *reduced_capacities) {
|
||||
known_value_.assign(num_items, false);
|
||||
best_solution_.assign(num_items, false);
|
||||
mapping_reduced_item_id_.assign(num_items, 0);
|
||||
@@ -1306,8 +1270,8 @@ int KnapsackSolver::ReduceProblem(int num_items) {
|
||||
return num_reduced_items;
|
||||
}
|
||||
|
||||
void KnapsackSolver::ComputeAdditionalProfit(
|
||||
const std::vector<int64>& profits) {
|
||||
void
|
||||
KnapsackSolver::ComputeAdditionalProfit(const std::vector<int64> &profits) {
|
||||
const int num_items = profits.size();
|
||||
additional_profit_ = 0LL;
|
||||
for (int item_id = 0; item_id < num_items; ++item_id) {
|
||||
@@ -1318,9 +1282,9 @@ void KnapsackSolver::ComputeAdditionalProfit(
|
||||
}
|
||||
|
||||
void KnapsackSolver::InitReducedProblem(
|
||||
const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64>>& weights,
|
||||
const std::vector<int64>& capacities) {
|
||||
const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) {
|
||||
const int num_items = profits.size();
|
||||
const int num_dimensions = capacities.size();
|
||||
|
||||
@@ -1332,10 +1296,10 @@ void KnapsackSolver::InitReducedProblem(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<int64>> reduced_weights;
|
||||
std::vector<std::vector<int64> > reduced_weights;
|
||||
std::vector<int64> reduced_capacities = capacities;
|
||||
for (int dim = 0; dim < num_dimensions; ++dim) {
|
||||
const std::vector<int64>& one_dimension_weights = weights[dim];
|
||||
const std::vector<int64> &one_dimension_weights = weights[dim];
|
||||
std::vector<int64> one_dimension_reduced_weights;
|
||||
for (int item_id = 0; item_id < num_items; ++item_id) {
|
||||
if (known_value_[item_id]) {
|
||||
@@ -1353,9 +1317,8 @@ void KnapsackSolver::InitReducedProblem(
|
||||
|
||||
int64 KnapsackSolver::Solve() {
|
||||
return additional_profit_ +
|
||||
((is_problem_solved_)
|
||||
? 0
|
||||
: solver_->Solve(time_limit_.get(), &is_solution_optimal_));
|
||||
((is_problem_solved_) ? 0 : solver_->Solve(time_limit_.get(),
|
||||
&is_solution_optimal_));
|
||||
}
|
||||
|
||||
bool KnapsackSolver::BestSolutionContains(int item_id) const {
|
||||
@@ -1371,12 +1334,12 @@ std::string KnapsackSolver::GetName() const { return solver_->GetName(); }
|
||||
// ----- BaseKnapsackSolver -----
|
||||
void BaseKnapsackSolver::GetLowerAndUpperBoundWhenItem(int item_id,
|
||||
bool is_item_in,
|
||||
int64* lower_bound,
|
||||
int64* upper_bound) {
|
||||
int64 *lower_bound,
|
||||
int64 *upper_bound) {
|
||||
CHECK(lower_bound != nullptr);
|
||||
CHECK(upper_bound != nullptr);
|
||||
*lower_bound = 0LL;
|
||||
*upper_bound = kint64max;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -118,7 +118,7 @@ class BaseKnapsackSolver;
|
||||
|
||||
*/
|
||||
class KnapsackSolver {
|
||||
public:
|
||||
public:
|
||||
/** Enum controlling which underlying algorithm is used.
|
||||
*
|
||||
* This enum is passed to the constructor of the KnapsackSolver object.
|
||||
@@ -156,7 +156,7 @@ class KnapsackSolver {
|
||||
* dimensions. This solver is based on Integer Programming solver CBC.
|
||||
*/
|
||||
KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER = 3,
|
||||
#endif // USE_CBC
|
||||
#endif // USE_CBC
|
||||
|
||||
/** Generic Solver.
|
||||
*
|
||||
@@ -172,7 +172,7 @@ class KnapsackSolver {
|
||||
* dimensions. This solver is based on Integer Programming solver SCIP.
|
||||
*/
|
||||
KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER = 6,
|
||||
#endif // USE_SCIP
|
||||
#endif // USE_SCIP
|
||||
|
||||
#if defined(USE_XPRESS)
|
||||
/** XPRESS based solver
|
||||
@@ -193,16 +193,16 @@ class KnapsackSolver {
|
||||
#endif
|
||||
};
|
||||
|
||||
explicit KnapsackSolver(const std::string& solver_name);
|
||||
KnapsackSolver(SolverType solver_type, const std::string& solver_name);
|
||||
explicit KnapsackSolver(const std::string &solver_name);
|
||||
KnapsackSolver(SolverType solver_type, const std::string &solver_name);
|
||||
virtual ~KnapsackSolver();
|
||||
|
||||
/**
|
||||
* Initializes the solver and enters the problem to be solved.
|
||||
*/
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64> >& weights,
|
||||
const std::vector<int64>& capacities);
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities);
|
||||
|
||||
/**
|
||||
* Solves the problem and returns the profit of the optimal solution.
|
||||
@@ -232,19 +232,19 @@ class KnapsackSolver {
|
||||
time_limit_ = absl::make_unique<TimeLimit>(time_limit_seconds_);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Trivial reduction of capacity constraints when the capacity is higher than
|
||||
// the sum of the weights of the items. Returns the number of reduced items.
|
||||
int ReduceCapacities(int num_items,
|
||||
const std::vector<std::vector<int64> >& weights,
|
||||
const std::vector<int64>& capacities,
|
||||
std::vector<std::vector<int64> >* reduced_weights,
|
||||
std::vector<int64>* reduced_capacities);
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities,
|
||||
std::vector<std::vector<int64> > *reduced_weights,
|
||||
std::vector<int64> *reduced_capacities);
|
||||
int ReduceProblem(int num_items);
|
||||
void ComputeAdditionalProfit(const std::vector<int64>& profits);
|
||||
void InitReducedProblem(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64> >& weights,
|
||||
const std::vector<int64>& capacities);
|
||||
void ComputeAdditionalProfit(const std::vector<int64> &profits);
|
||||
void InitReducedProblem(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities);
|
||||
|
||||
std::unique_ptr<BaseKnapsackSolver> solver_;
|
||||
std::vector<bool> known_value_;
|
||||
@@ -321,7 +321,7 @@ struct KnapsackItem {
|
||||
const int64 weight;
|
||||
const int64 profit;
|
||||
};
|
||||
typedef KnapsackItem* KnapsackItemPtr;
|
||||
typedef KnapsackItem *KnapsackItemPtr;
|
||||
|
||||
// ----- KnapsackSearchNode -----
|
||||
// KnapsackSearchNode is a class used to describe a decision in the decision
|
||||
@@ -332,12 +332,12 @@ typedef KnapsackItem* KnapsackItemPtr;
|
||||
// go through the search tree to incrementally build a partial solution from
|
||||
// a previous search node.
|
||||
class KnapsackSearchNode {
|
||||
public:
|
||||
KnapsackSearchNode(const KnapsackSearchNode* const parent,
|
||||
const KnapsackAssignment& assignment);
|
||||
public:
|
||||
KnapsackSearchNode(const KnapsackSearchNode *const parent,
|
||||
const KnapsackAssignment &assignment);
|
||||
int depth() const { return depth_; }
|
||||
const KnapsackSearchNode* const parent() const { return parent_; }
|
||||
const KnapsackAssignment& assignment() const { return assignment_; }
|
||||
const KnapsackSearchNode *const parent() const { return parent_; }
|
||||
const KnapsackAssignment &assignment() const { return assignment_; }
|
||||
|
||||
int64 current_profit() const { return current_profit_; }
|
||||
void set_current_profit(int64 profit) { current_profit_ = profit; }
|
||||
@@ -348,11 +348,11 @@ class KnapsackSearchNode {
|
||||
int next_item_id() const { return next_item_id_; }
|
||||
void set_next_item_id(int id) { next_item_id_ = id; }
|
||||
|
||||
private:
|
||||
private:
|
||||
// 'depth' field is used to navigate efficiently through the search tree
|
||||
// (see KnapsackSearchPath).
|
||||
int depth_;
|
||||
const KnapsackSearchNode* const parent_;
|
||||
const KnapsackSearchNode *const parent_;
|
||||
KnapsackAssignment assignment_;
|
||||
|
||||
// 'current_profit' and 'profit_upper_bound' fields are used to sort search
|
||||
@@ -386,20 +386,20 @@ class KnapsackSearchNode {
|
||||
// So the state can be built by reverting all decisions from 'from' to 'via'
|
||||
// and then applying all decisions from 'via' to 'to'.
|
||||
class KnapsackSearchPath {
|
||||
public:
|
||||
KnapsackSearchPath(const KnapsackSearchNode& from,
|
||||
const KnapsackSearchNode& to);
|
||||
public:
|
||||
KnapsackSearchPath(const KnapsackSearchNode &from,
|
||||
const KnapsackSearchNode &to);
|
||||
void Init();
|
||||
const KnapsackSearchNode& from() const { return from_; }
|
||||
const KnapsackSearchNode& via() const { return *via_; }
|
||||
const KnapsackSearchNode& to() const { return to_; }
|
||||
const KnapsackSearchNode* MoveUpToDepth(const KnapsackSearchNode& node,
|
||||
const KnapsackSearchNode &from() const { return from_; }
|
||||
const KnapsackSearchNode &via() const { return *via_; }
|
||||
const KnapsackSearchNode &to() const { return to_; }
|
||||
const KnapsackSearchNode *MoveUpToDepth(const KnapsackSearchNode &node,
|
||||
int depth) const;
|
||||
|
||||
private:
|
||||
const KnapsackSearchNode& from_;
|
||||
const KnapsackSearchNode* via_; // Computed in 'Init'.
|
||||
const KnapsackSearchNode& to_;
|
||||
private:
|
||||
const KnapsackSearchNode &from_;
|
||||
const KnapsackSearchNode *via_; // Computed in 'Init'.
|
||||
const KnapsackSearchNode &to_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(KnapsackSearchPath);
|
||||
};
|
||||
@@ -407,7 +407,7 @@ class KnapsackSearchPath {
|
||||
// ----- KnapsackState -----
|
||||
// KnapsackState represents a partial solution to the knapsack problem.
|
||||
class KnapsackState {
|
||||
public:
|
||||
public:
|
||||
KnapsackState();
|
||||
|
||||
// Initializes vectors with number_of_items set to false (i.e. not bound yet).
|
||||
@@ -415,13 +415,13 @@ class KnapsackState {
|
||||
// Updates the state by applying or reverting a decision.
|
||||
// Returns false if fails, i.e. trying to apply an inconsistent decision
|
||||
// to an already assigned item.
|
||||
bool UpdateState(bool revert, const KnapsackAssignment& assignment);
|
||||
bool UpdateState(bool revert, const KnapsackAssignment &assignment);
|
||||
|
||||
int GetNumberOfItems() const { return is_bound_.size(); }
|
||||
bool is_bound(int id) const { return is_bound_.at(id); }
|
||||
bool is_in(int id) const { return is_in_.at(id); }
|
||||
|
||||
private:
|
||||
private:
|
||||
// Vectors 'is_bound_' and 'is_in_' contain a boolean value for each item.
|
||||
// 'is_bound_(item_i)' is false when there is no decision for item_i yet.
|
||||
// When item_i is bound, 'is_in_(item_i)' represents the presence (true) or
|
||||
@@ -441,17 +441,17 @@ class KnapsackState {
|
||||
// For instance, 'Init' creates a vector of items, and then calls
|
||||
// 'InitPropagator' to let the derived class perform its own initialization.
|
||||
class KnapsackPropagator {
|
||||
public:
|
||||
explicit KnapsackPropagator(const KnapsackState& state);
|
||||
public:
|
||||
explicit KnapsackPropagator(const KnapsackState &state);
|
||||
virtual ~KnapsackPropagator();
|
||||
|
||||
// Initializes data structure and then calls InitPropagator.
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<int64>& weights);
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<int64> &weights);
|
||||
|
||||
// Updates data structure and then calls UpdatePropagator.
|
||||
// Returns false when failure.
|
||||
bool Update(bool revert, const KnapsackAssignment& assignment);
|
||||
bool Update(bool revert, const KnapsackAssignment &assignment);
|
||||
// ComputeProfitBounds should set 'profit_lower_bound_' and
|
||||
// 'profit_upper_bound_' which are constraint specific.
|
||||
virtual void ComputeProfitBounds() = 0;
|
||||
@@ -470,9 +470,9 @@ class KnapsackPropagator {
|
||||
// there is no need to check the solution with other propagators, so the
|
||||
// partial solution can be smartly completed.
|
||||
void CopyCurrentStateToSolution(bool has_one_propagator,
|
||||
std::vector<bool>* solution) const;
|
||||
std::vector<bool> *solution) const;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// Initializes data structure. This method is called after initialization
|
||||
// of KnapsackPropagator data structure.
|
||||
virtual void InitPropagator() = 0;
|
||||
@@ -480,7 +480,7 @@ class KnapsackPropagator {
|
||||
// Updates internal data structure incrementally. This method is called
|
||||
// after update of KnapsackPropagator data structure.
|
||||
virtual bool UpdatePropagator(bool revert,
|
||||
const KnapsackAssignment& assignment) = 0;
|
||||
const KnapsackAssignment &assignment) = 0;
|
||||
|
||||
// Copies the current state into 'solution'.
|
||||
// Only unbound items have to be copied as CopyCurrentSolution was already
|
||||
@@ -488,20 +488,20 @@ class KnapsackPropagator {
|
||||
// This method is useful when a propagator is able to find a better solution
|
||||
// than the blind instantiation to false of unbound items.
|
||||
virtual void CopyCurrentStateToSolutionPropagator(
|
||||
std::vector<bool>* solution) const = 0;
|
||||
std::vector<bool> *solution) const = 0;
|
||||
|
||||
const KnapsackState& state() const { return state_; }
|
||||
const std::vector<KnapsackItemPtr>& items() const { return items_; }
|
||||
const KnapsackState &state() const { return state_; }
|
||||
const std::vector<KnapsackItemPtr> &items() const { return items_; }
|
||||
|
||||
void set_profit_lower_bound(int64 profit) { profit_lower_bound_ = profit; }
|
||||
void set_profit_upper_bound(int64 profit) { profit_upper_bound_ = profit; }
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<KnapsackItemPtr> items_;
|
||||
int64 current_profit_;
|
||||
int64 profit_lower_bound_;
|
||||
int64 profit_upper_bound_;
|
||||
const KnapsackState& state_;
|
||||
const KnapsackState &state_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(KnapsackPropagator);
|
||||
};
|
||||
@@ -527,24 +527,24 @@ class KnapsackPropagator {
|
||||
// For incrementality reasons, the ith item should be accessible in O(1). That's
|
||||
// the reason why the item vector has to be duplicated 'sorted_items_'.
|
||||
class KnapsackCapacityPropagator : public KnapsackPropagator {
|
||||
public:
|
||||
KnapsackCapacityPropagator(const KnapsackState& state, int64 capacity);
|
||||
public:
|
||||
KnapsackCapacityPropagator(const KnapsackState &state, int64 capacity);
|
||||
~KnapsackCapacityPropagator() override;
|
||||
void ComputeProfitBounds() override;
|
||||
int GetNextItemId() const override { return break_item_id_; }
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// Initializes KnapsackCapacityPropagator (e.g., sort items in decreasing
|
||||
// order).
|
||||
void InitPropagator() override;
|
||||
// Updates internal data structure incrementally (i.e., 'consumed_capacity_')
|
||||
// to avoid a O(number_of_items) scan.
|
||||
bool UpdatePropagator(bool revert,
|
||||
const KnapsackAssignment& assignment) override;
|
||||
void CopyCurrentStateToSolutionPropagator(
|
||||
std::vector<bool>* solution) const override;
|
||||
bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment)
|
||||
override;
|
||||
void CopyCurrentStateToSolutionPropagator(std::vector<bool> *solution) const
|
||||
override;
|
||||
|
||||
private:
|
||||
private:
|
||||
// An obvious additional profit upper bound corresponds to the linear
|
||||
// relaxation: remaining_capacity * efficiency of the break item.
|
||||
// It is possible to do better in O(1), using Martello-Toth bound U2.
|
||||
@@ -567,32 +567,32 @@ class KnapsackCapacityPropagator : public KnapsackPropagator {
|
||||
// ----- BaseKnapsackSolver -----
|
||||
// This is the base class for knapsack solvers.
|
||||
class BaseKnapsackSolver {
|
||||
public:
|
||||
explicit BaseKnapsackSolver(const std::string& solver_name)
|
||||
public:
|
||||
explicit BaseKnapsackSolver(const std::string &solver_name)
|
||||
: solver_name_(solver_name) {}
|
||||
virtual ~BaseKnapsackSolver() {}
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
virtual void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64> >& weights,
|
||||
const std::vector<int64>& capacities) = 0;
|
||||
virtual void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) = 0;
|
||||
|
||||
// Gets the lower and upper bound when the item is in or out of the knapsack.
|
||||
// To ensure objects are correctly initialized, this method should not be
|
||||
// called before ::Init.
|
||||
virtual void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in,
|
||||
int64* lower_bound,
|
||||
int64* upper_bound);
|
||||
int64 *lower_bound,
|
||||
int64 *upper_bound);
|
||||
|
||||
// Solves the problem and returns the profit of the optimal solution.
|
||||
virtual int64 Solve(TimeLimit* time_limit, bool* is_solution_optimal) = 0;
|
||||
virtual int64 Solve(TimeLimit *time_limit, bool *is_solution_optimal) = 0;
|
||||
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
virtual bool best_solution(int item_id) const = 0;
|
||||
|
||||
virtual std::string GetName() const { return solver_name_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
const std::string solver_name_;
|
||||
};
|
||||
|
||||
@@ -606,18 +606,18 @@ class BaseKnapsackSolver {
|
||||
// an aggregated propagator to combine all dimensions and give a better guide
|
||||
// to select the next item (see, for instance, Dobson's aggregated efficiency).
|
||||
class KnapsackGenericSolver : public BaseKnapsackSolver {
|
||||
public:
|
||||
explicit KnapsackGenericSolver(const std::string& solver_name);
|
||||
public:
|
||||
explicit KnapsackGenericSolver(const std::string &solver_name);
|
||||
~KnapsackGenericSolver() override;
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
void Init(const std::vector<int64>& profits,
|
||||
const std::vector<std::vector<int64> >& weights,
|
||||
const std::vector<int64>& capacities) override;
|
||||
void Init(const std::vector<int64> &profits,
|
||||
const std::vector<std::vector<int64> > &weights,
|
||||
const std::vector<int64> &capacities) override;
|
||||
int GetNumberOfItems() const { return state_.GetNumberOfItems(); }
|
||||
void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in,
|
||||
int64* lower_bound,
|
||||
int64* upper_bound) override;
|
||||
int64 *lower_bound, int64 *upper_bound)
|
||||
override;
|
||||
|
||||
// Sets which propagator should be used to guide the search.
|
||||
// 'master_propagator_id' should be in 0..p-1 with p the number of
|
||||
@@ -627,30 +627,30 @@ class KnapsackGenericSolver : public BaseKnapsackSolver {
|
||||
}
|
||||
|
||||
// Solves the problem and returns the profit of the optimal solution.
|
||||
int64 Solve(TimeLimit* time_limit, bool* is_solution_optimal) override;
|
||||
int64 Solve(TimeLimit *time_limit, bool *is_solution_optimal) override;
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
bool best_solution(int item_id) const override {
|
||||
return best_solution_.at(item_id);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Clears internal data structure.
|
||||
void Clear();
|
||||
|
||||
// Updates all propagators reverting/applying all decision on the path.
|
||||
// Returns true if fails. Note that, even if fails, all propagators should
|
||||
// be updated to be in a stable state in order to stay incremental.
|
||||
bool UpdatePropagators(const KnapsackSearchPath& path);
|
||||
bool UpdatePropagators(const KnapsackSearchPath &path);
|
||||
// Updates all propagators reverting/applying one decision.
|
||||
// Return true if fails. Note that, even if fails, all propagators should
|
||||
// be updated to be in a stable state in order to stay incremental.
|
||||
bool IncrementalUpdate(bool revert, const KnapsackAssignment& assignment);
|
||||
bool IncrementalUpdate(bool revert, const KnapsackAssignment &assignment);
|
||||
// Updates the best solution if the current solution has a better profit.
|
||||
void UpdateBestSolution();
|
||||
|
||||
// Returns true if new relevant search node was added to the nodes array, that
|
||||
// means this node should be added to the search queue too.
|
||||
bool MakeNewNode(const KnapsackSearchNode& node, bool is_in);
|
||||
bool MakeNewNode(const KnapsackSearchNode &node, bool is_in);
|
||||
|
||||
// Gets the aggregated (min) profit upper bound among all propagators.
|
||||
int64 GetAggregatedProfitUpperBound() const;
|
||||
@@ -662,16 +662,16 @@ class KnapsackGenericSolver : public BaseKnapsackSolver {
|
||||
return propagators_.at(master_propagator_id_)->GetNextItemId();
|
||||
}
|
||||
|
||||
std::vector<KnapsackPropagator*> propagators_;
|
||||
std::vector<KnapsackPropagator *> propagators_;
|
||||
int master_propagator_id_;
|
||||
std::vector<KnapsackSearchNode*> search_nodes_;
|
||||
std::vector<KnapsackSearchNode *> search_nodes_;
|
||||
KnapsackState state_;
|
||||
int64 best_solution_profit_;
|
||||
std::vector<bool> best_solution_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(KnapsackGenericSolver);
|
||||
};
|
||||
#endif // SWIG
|
||||
} // namespace operations_research
|
||||
#endif // SWIG
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_KNAPSACK_SOLVER_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_KNAPSACK_SOLVER_H_
|
||||
|
||||
@@ -31,8 +31,8 @@ const double kInfinity = std::numeric_limits<double>::infinity();
|
||||
struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
|
||||
explicit CompareKnapsackItemsInDecreasingEfficiencyOrder(double _profit_max)
|
||||
: profit_max(_profit_max) {}
|
||||
bool operator()(const KnapsackItemForCutsPtr& item1,
|
||||
const KnapsackItemForCutsPtr& item2) const {
|
||||
bool operator()(const KnapsackItemForCutsPtr &item1,
|
||||
const KnapsackItemForCutsPtr &item2) const {
|
||||
return item1->GetEfficiency(profit_max) > item2->GetEfficiency(profit_max);
|
||||
}
|
||||
const double profit_max;
|
||||
@@ -44,8 +44,8 @@ struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
|
||||
// prefer the one with the highest current profit. This is usually the one
|
||||
// closer to a leaf. In practice, the main advantage is to have smaller path.
|
||||
struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
|
||||
bool operator()(const KnapsackSearchNodeForCuts* node_1,
|
||||
const KnapsackSearchNodeForCuts* node_2) const {
|
||||
bool operator()(const KnapsackSearchNodeForCuts *node_1,
|
||||
const KnapsackSearchNodeForCuts *node_2) const {
|
||||
const double profit_upper_bound_1 = node_1->profit_upper_bound();
|
||||
const double profit_upper_bound_2 = node_2->profit_upper_bound();
|
||||
if (profit_upper_bound_1 == profit_upper_bound_2) {
|
||||
@@ -56,31 +56,28 @@ struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
|
||||
};
|
||||
|
||||
using SearchQueue = std::priority_queue<
|
||||
KnapsackSearchNodeForCuts*, std::vector<KnapsackSearchNodeForCuts*>,
|
||||
KnapsackSearchNodeForCuts *, std::vector<KnapsackSearchNodeForCuts *>,
|
||||
CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder>;
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
// ----- KnapsackSearchNodeForCuts -----
|
||||
KnapsackSearchNodeForCuts::KnapsackSearchNodeForCuts(
|
||||
const KnapsackSearchNodeForCuts* const parent,
|
||||
const KnapsackAssignmentForCuts& assignment)
|
||||
: depth_(parent == nullptr ? 0 : parent->depth() + 1),
|
||||
parent_(parent),
|
||||
assignment_(assignment),
|
||||
current_profit_(0),
|
||||
profit_upper_bound_(kInfinity),
|
||||
next_item_id_(kNoSelection) {}
|
||||
const KnapsackSearchNodeForCuts *const parent,
|
||||
const KnapsackAssignmentForCuts &assignment)
|
||||
: depth_(parent == nullptr ? 0 : parent->depth() + 1), parent_(parent),
|
||||
assignment_(assignment), current_profit_(0),
|
||||
profit_upper_bound_(kInfinity), next_item_id_(kNoSelection) {}
|
||||
|
||||
// ----- KnapsackSearchPathForCuts -----
|
||||
KnapsackSearchPathForCuts::KnapsackSearchPathForCuts(
|
||||
const KnapsackSearchNodeForCuts* from, const KnapsackSearchNodeForCuts* to)
|
||||
const KnapsackSearchNodeForCuts *from, const KnapsackSearchNodeForCuts *to)
|
||||
: from_(from), via_(nullptr), to_(to) {}
|
||||
|
||||
void KnapsackSearchPathForCuts::Init() {
|
||||
const KnapsackSearchNodeForCuts* node_from =
|
||||
const KnapsackSearchNodeForCuts *node_from =
|
||||
MoveUpToDepth(from_, to_->depth());
|
||||
const KnapsackSearchNodeForCuts* node_to = MoveUpToDepth(to_, from_->depth());
|
||||
const KnapsackSearchNodeForCuts *node_to = MoveUpToDepth(to_, from_->depth());
|
||||
DCHECK_EQ(node_from->depth(), node_to->depth());
|
||||
|
||||
// Find common parent.
|
||||
@@ -91,8 +88,8 @@ void KnapsackSearchPathForCuts::Init() {
|
||||
via_ = node_from;
|
||||
}
|
||||
|
||||
const KnapsackSearchNodeForCuts* MoveUpToDepth(
|
||||
const KnapsackSearchNodeForCuts* node, int depth) {
|
||||
const KnapsackSearchNodeForCuts *
|
||||
MoveUpToDepth(const KnapsackSearchNodeForCuts *node, int depth) {
|
||||
while (node->depth() > depth) {
|
||||
node = node->parent();
|
||||
}
|
||||
@@ -108,8 +105,9 @@ void KnapsackStateForCuts::Init(int number_of_items) {
|
||||
}
|
||||
|
||||
// Returns false when the state is invalid.
|
||||
bool KnapsackStateForCuts::UpdateState(
|
||||
bool revert, const KnapsackAssignmentForCuts& assignment) {
|
||||
bool
|
||||
KnapsackStateForCuts::UpdateState(bool revert,
|
||||
const KnapsackAssignmentForCuts &assignment) {
|
||||
if (revert) {
|
||||
is_bound_[assignment.item_id] = false;
|
||||
} else {
|
||||
@@ -125,17 +123,14 @@ bool KnapsackStateForCuts::UpdateState(
|
||||
|
||||
// ----- KnapsackPropagatorForCuts -----
|
||||
KnapsackPropagatorForCuts::KnapsackPropagatorForCuts(
|
||||
const KnapsackStateForCuts* state)
|
||||
: items_(),
|
||||
current_profit_(0),
|
||||
profit_lower_bound_(0),
|
||||
profit_upper_bound_(kInfinity),
|
||||
state_(state) {}
|
||||
const KnapsackStateForCuts *state)
|
||||
: items_(), current_profit_(0), profit_lower_bound_(0),
|
||||
profit_upper_bound_(kInfinity), state_(state) {}
|
||||
|
||||
KnapsackPropagatorForCuts::~KnapsackPropagatorForCuts() {}
|
||||
|
||||
void KnapsackPropagatorForCuts::Init(const std::vector<double>& profits,
|
||||
const std::vector<double>& weights,
|
||||
void KnapsackPropagatorForCuts::Init(const std::vector<double> &profits,
|
||||
const std::vector<double> &weights,
|
||||
const double capacity) {
|
||||
const int number_of_items = profits.size();
|
||||
items_.clear();
|
||||
@@ -151,8 +146,9 @@ void KnapsackPropagatorForCuts::Init(const std::vector<double>& profits,
|
||||
InitPropagator();
|
||||
}
|
||||
|
||||
bool KnapsackPropagatorForCuts::Update(
|
||||
bool revert, const KnapsackAssignmentForCuts& assignment) {
|
||||
bool
|
||||
KnapsackPropagatorForCuts::Update(bool revert,
|
||||
const KnapsackAssignmentForCuts &assignment) {
|
||||
if (assignment.is_in) {
|
||||
if (revert) {
|
||||
current_profit_ -= items_[assignment.item_id]->profit;
|
||||
@@ -169,14 +165,14 @@ bool KnapsackPropagatorForCuts::Update(
|
||||
}
|
||||
|
||||
void KnapsackPropagatorForCuts::CopyCurrentStateToSolution(
|
||||
std::vector<bool>* solution) const {
|
||||
std::vector<bool> *solution) const {
|
||||
DCHECK(solution != nullptr);
|
||||
for (int i(0); i < items_.size(); ++i) {
|
||||
const int item_id = items_[i]->id;
|
||||
(*solution)[item_id] = state_->is_bound(item_id) && state_->is_in(item_id);
|
||||
}
|
||||
double remaining_capacity = capacity_ - consumed_capacity_;
|
||||
for (const KnapsackItemForCutsPtr& item : sorted_items_) {
|
||||
for (const KnapsackItemForCutsPtr &item : sorted_items_) {
|
||||
if (!state().is_bound(item->id)) {
|
||||
if (remaining_capacity >= item->weight) {
|
||||
remaining_capacity -= item->weight;
|
||||
@@ -196,7 +192,7 @@ void KnapsackPropagatorForCuts::ComputeProfitBounds() {
|
||||
int break_sorted_item_id = kNoSelection;
|
||||
for (int sorted_id(0); sorted_id < sorted_items_.size(); ++sorted_id) {
|
||||
if (!state().is_bound(sorted_items_[sorted_id]->id)) {
|
||||
const KnapsackItemForCutsPtr& item = sorted_items_[sorted_id];
|
||||
const KnapsackItemForCutsPtr &item = sorted_items_[sorted_id];
|
||||
break_item_id_ = item->id;
|
||||
if (remaining_capacity >= item->weight) {
|
||||
remaining_capacity -= item->weight;
|
||||
@@ -231,7 +227,7 @@ void KnapsackPropagatorForCuts::InitPropagator() {
|
||||
i, items()[i]->weight, items()[i]->profit));
|
||||
}
|
||||
profit_max_ = 0;
|
||||
for (const KnapsackItemForCutsPtr& item : sorted_items_) {
|
||||
for (const KnapsackItemForCutsPtr &item : sorted_items_) {
|
||||
profit_max_ = std::max(profit_max_, item->profit);
|
||||
}
|
||||
profit_max_ += 1.0;
|
||||
@@ -279,12 +275,11 @@ double KnapsackPropagatorForCuts::GetAdditionalProfitUpperBound(
|
||||
|
||||
// ----- KnapsackSolverForCuts -----
|
||||
KnapsackSolverForCuts::KnapsackSolverForCuts(std::string solver_name)
|
||||
: propagator_(&state_),
|
||||
best_solution_profit_(0),
|
||||
: propagator_(&state_), best_solution_profit_(0),
|
||||
solver_name_(std::move(solver_name)) {}
|
||||
|
||||
void KnapsackSolverForCuts::Init(const std::vector<double>& profits,
|
||||
const std::vector<double>& weights,
|
||||
void KnapsackSolverForCuts::Init(const std::vector<double> &profits,
|
||||
const std::vector<double> &weights,
|
||||
const double capacity) {
|
||||
const int number_of_items(profits.size());
|
||||
state_.Init(number_of_items);
|
||||
@@ -296,8 +291,8 @@ void KnapsackSolverForCuts::Init(const std::vector<double>& profits,
|
||||
|
||||
void KnapsackSolverForCuts::GetLowerAndUpperBoundWhenItem(int item_id,
|
||||
bool is_item_in,
|
||||
double* lower_bound,
|
||||
double* upper_bound) {
|
||||
double *lower_bound,
|
||||
double *upper_bound) {
|
||||
DCHECK(lower_bound != nullptr);
|
||||
DCHECK(upper_bound != nullptr);
|
||||
KnapsackAssignmentForCuts assignment(item_id, is_item_in);
|
||||
@@ -317,8 +312,8 @@ void KnapsackSolverForCuts::GetLowerAndUpperBoundWhenItem(int item_id,
|
||||
}
|
||||
}
|
||||
|
||||
double KnapsackSolverForCuts::Solve(TimeLimit* time_limit,
|
||||
bool* is_solution_optimal) {
|
||||
double KnapsackSolverForCuts::Solve(TimeLimit *time_limit,
|
||||
bool *is_solution_optimal) {
|
||||
DCHECK(time_limit != nullptr);
|
||||
DCHECK(is_solution_optimal != nullptr);
|
||||
best_solution_profit_ = 0;
|
||||
@@ -332,8 +327,8 @@ double KnapsackSolverForCuts::Solve(TimeLimit* time_limit,
|
||||
root_node->set_profit_upper_bound(GetAggregatedProfitUpperBound());
|
||||
root_node->set_next_item_id(GetNextItemId());
|
||||
search_nodes_.push_back(std::move(root_node));
|
||||
const KnapsackSearchNodeForCuts* current_node =
|
||||
search_nodes_.back().get(); // Start with the root node.
|
||||
const KnapsackSearchNodeForCuts *current_node =
|
||||
search_nodes_.back().get(); // Start with the root node.
|
||||
|
||||
if (MakeNewNode(*current_node, false)) {
|
||||
search_queue.push(search_nodes_.back().get());
|
||||
@@ -362,7 +357,7 @@ double KnapsackSolverForCuts::Solve(TimeLimit* time_limit,
|
||||
*is_solution_optimal = false;
|
||||
break;
|
||||
}
|
||||
KnapsackSearchNodeForCuts* const node = search_queue.top();
|
||||
KnapsackSearchNodeForCuts *const node = search_queue.top();
|
||||
search_queue.pop();
|
||||
|
||||
if (node != current_node) {
|
||||
@@ -385,11 +380,11 @@ double KnapsackSolverForCuts::Solve(TimeLimit* time_limit,
|
||||
|
||||
// Returns false when at least one propagator fails.
|
||||
bool KnapsackSolverForCuts::UpdatePropagators(
|
||||
const KnapsackSearchPathForCuts& path) {
|
||||
const KnapsackSearchPathForCuts &path) {
|
||||
bool no_fail = true;
|
||||
// Revert previous changes.
|
||||
const KnapsackSearchNodeForCuts* node = &path.from();
|
||||
const KnapsackSearchNodeForCuts* const via = &path.via();
|
||||
const KnapsackSearchNodeForCuts *node = &path.from();
|
||||
const KnapsackSearchNodeForCuts *const via = &path.via();
|
||||
while (node != via) {
|
||||
no_fail = IncrementalUpdate(true, node->assignment()) && no_fail;
|
||||
node = node->parent();
|
||||
@@ -409,7 +404,7 @@ double KnapsackSolverForCuts::GetAggregatedProfitUpperBound() {
|
||||
return std::min(kInfinity, propagator_upper_bound);
|
||||
}
|
||||
|
||||
bool KnapsackSolverForCuts::MakeNewNode(const KnapsackSearchNodeForCuts& node,
|
||||
bool KnapsackSolverForCuts::MakeNewNode(const KnapsackSearchNodeForCuts &node,
|
||||
bool is_in) {
|
||||
if (node.next_item_id() == kNoSelection) {
|
||||
return false;
|
||||
@@ -448,7 +443,7 @@ bool KnapsackSolverForCuts::MakeNewNode(const KnapsackSearchNodeForCuts& node,
|
||||
}
|
||||
|
||||
bool KnapsackSolverForCuts::IncrementalUpdate(
|
||||
bool revert, const KnapsackAssignmentForCuts& assignment) {
|
||||
bool revert, const KnapsackAssignmentForCuts &assignment) {
|
||||
// Do not stop on a failure: To be able to be incremental on the update,
|
||||
// partial solution (state) and propagators must all be in the same state.
|
||||
bool no_fail = state_.UpdateState(revert, assignment);
|
||||
@@ -465,4 +460,4 @@ void KnapsackSolverForCuts::UpdateBestSolution() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -100,17 +100,17 @@ using KnapsackItemForCutsPtr = std::unique_ptr<KnapsackItemForCuts>;
|
||||
// go through the search tree to incrementally build a partial solution from
|
||||
// a previous search node.
|
||||
class KnapsackSearchNodeForCuts {
|
||||
public:
|
||||
KnapsackSearchNodeForCuts(const KnapsackSearchNodeForCuts* parent,
|
||||
const KnapsackAssignmentForCuts& assignment);
|
||||
public:
|
||||
KnapsackSearchNodeForCuts(const KnapsackSearchNodeForCuts *parent,
|
||||
const KnapsackAssignmentForCuts &assignment);
|
||||
|
||||
KnapsackSearchNodeForCuts(const KnapsackSearchNodeForCuts&) = delete;
|
||||
KnapsackSearchNodeForCuts& operator=(const KnapsackSearchNodeForCuts&) =
|
||||
KnapsackSearchNodeForCuts(const KnapsackSearchNodeForCuts &) = delete;
|
||||
KnapsackSearchNodeForCuts &operator=(const KnapsackSearchNodeForCuts &) =
|
||||
delete;
|
||||
|
||||
int depth() const { return depth_; }
|
||||
const KnapsackSearchNodeForCuts* const parent() const { return parent_; }
|
||||
const KnapsackAssignmentForCuts& assignment() const { return assignment_; }
|
||||
const KnapsackSearchNodeForCuts *const parent() const { return parent_; }
|
||||
const KnapsackAssignmentForCuts &assignment() const { return assignment_; }
|
||||
|
||||
double current_profit() const { return current_profit_; }
|
||||
void set_current_profit(double profit) { current_profit_ = profit; }
|
||||
@@ -121,10 +121,10 @@ class KnapsackSearchNodeForCuts {
|
||||
int next_item_id() const { return next_item_id_; }
|
||||
void set_next_item_id(int id) { next_item_id_ = id; }
|
||||
|
||||
private:
|
||||
private:
|
||||
// 'depth_' is used to navigate efficiently through the search tree.
|
||||
int depth_;
|
||||
const KnapsackSearchNodeForCuts* const parent_;
|
||||
const KnapsackSearchNodeForCuts *const parent_;
|
||||
KnapsackAssignmentForCuts assignment_;
|
||||
|
||||
// 'current_profit_' and 'profit_upper_bound_' fields are used to sort search
|
||||
@@ -156,38 +156,38 @@ class KnapsackSearchNodeForCuts {
|
||||
// So the state can be built by reverting all decisions from 'from' to 'via'
|
||||
// and then applying all decisions from 'via' to 'to'.
|
||||
class KnapsackSearchPathForCuts {
|
||||
public:
|
||||
KnapsackSearchPathForCuts(const KnapsackSearchNodeForCuts* from,
|
||||
const KnapsackSearchNodeForCuts* to);
|
||||
public:
|
||||
KnapsackSearchPathForCuts(const KnapsackSearchNodeForCuts *from,
|
||||
const KnapsackSearchNodeForCuts *to);
|
||||
|
||||
KnapsackSearchPathForCuts(const KnapsackSearchPathForCuts&) = delete;
|
||||
KnapsackSearchPathForCuts& operator=(const KnapsackSearchPathForCuts&) =
|
||||
KnapsackSearchPathForCuts(const KnapsackSearchPathForCuts &) = delete;
|
||||
KnapsackSearchPathForCuts &operator=(const KnapsackSearchPathForCuts &) =
|
||||
delete;
|
||||
|
||||
void Init();
|
||||
const KnapsackSearchNodeForCuts& from() const { return *from_; }
|
||||
const KnapsackSearchNodeForCuts& via() const { return *via_; }
|
||||
const KnapsackSearchNodeForCuts& to() const { return *to_; }
|
||||
const KnapsackSearchNodeForCuts &from() const { return *from_; }
|
||||
const KnapsackSearchNodeForCuts &via() const { return *via_; }
|
||||
const KnapsackSearchNodeForCuts &to() const { return *to_; }
|
||||
|
||||
private:
|
||||
const KnapsackSearchNodeForCuts* from_;
|
||||
const KnapsackSearchNodeForCuts* via_; // Computed in 'Init'.
|
||||
const KnapsackSearchNodeForCuts* to_;
|
||||
private:
|
||||
const KnapsackSearchNodeForCuts *from_;
|
||||
const KnapsackSearchNodeForCuts *via_; // Computed in 'Init'.
|
||||
const KnapsackSearchNodeForCuts *to_;
|
||||
};
|
||||
|
||||
// From the given node, this method moves up the tree and returns the node at
|
||||
// given depth.
|
||||
const KnapsackSearchNodeForCuts* MoveUpToDepth(
|
||||
const KnapsackSearchNodeForCuts* node, int depth);
|
||||
const KnapsackSearchNodeForCuts *
|
||||
MoveUpToDepth(const KnapsackSearchNodeForCuts *node, int depth);
|
||||
|
||||
// ----- KnapsackStateForCuts -----
|
||||
// KnapsackStateForCuts represents a partial solution to the knapsack problem.
|
||||
class KnapsackStateForCuts {
|
||||
public:
|
||||
public:
|
||||
KnapsackStateForCuts();
|
||||
|
||||
KnapsackStateForCuts(const KnapsackStateForCuts&) = delete;
|
||||
KnapsackStateForCuts& operator=(const KnapsackStateForCuts&) = delete;
|
||||
KnapsackStateForCuts(const KnapsackStateForCuts &) = delete;
|
||||
KnapsackStateForCuts &operator=(const KnapsackStateForCuts &) = delete;
|
||||
|
||||
// Initializes vectors with number_of_items set to false (i.e. not bound yet).
|
||||
void Init(int number_of_items);
|
||||
@@ -195,13 +195,13 @@ class KnapsackStateForCuts {
|
||||
// Updates the state by applying or reverting a decision.
|
||||
// Returns false if fails, i.e. trying to apply an inconsistent decision
|
||||
// to an already assigned item.
|
||||
bool UpdateState(bool revert, const KnapsackAssignmentForCuts& assignment);
|
||||
bool UpdateState(bool revert, const KnapsackAssignmentForCuts &assignment);
|
||||
|
||||
int GetNumberOfItems() const { return is_bound_.size(); }
|
||||
bool is_bound(int id) const { return is_bound_.at(id); }
|
||||
bool is_in(int id) const { return is_in_.at(id); }
|
||||
|
||||
private:
|
||||
private:
|
||||
// Vectors 'is_bound_' and 'is_in_' contain a boolean value for each item.
|
||||
// 'is_bound_(item_i)' is false when there is no decision for item_i yet.
|
||||
// When item_i is bound, 'is_in_(item_i)' represents the presence (true) or
|
||||
@@ -229,20 +229,20 @@ class KnapsackStateForCuts {
|
||||
// the ith item should be accessible in O(1). That's the reason why the item
|
||||
// vector has to be duplicated 'sorted_items_'.
|
||||
class KnapsackPropagatorForCuts {
|
||||
public:
|
||||
explicit KnapsackPropagatorForCuts(const KnapsackStateForCuts* state);
|
||||
public:
|
||||
explicit KnapsackPropagatorForCuts(const KnapsackStateForCuts *state);
|
||||
~KnapsackPropagatorForCuts();
|
||||
|
||||
KnapsackPropagatorForCuts(const KnapsackPropagatorForCuts&) = delete;
|
||||
KnapsackPropagatorForCuts& operator=(const KnapsackPropagatorForCuts&) =
|
||||
KnapsackPropagatorForCuts(const KnapsackPropagatorForCuts &) = delete;
|
||||
KnapsackPropagatorForCuts &operator=(const KnapsackPropagatorForCuts &) =
|
||||
delete;
|
||||
|
||||
// Initializes the data structure and then calls InitPropagator.
|
||||
void Init(const std::vector<double>& profits,
|
||||
const std::vector<double>& weights, double capacity);
|
||||
void Init(const std::vector<double> &profits,
|
||||
const std::vector<double> &weights, double capacity);
|
||||
|
||||
// Updates data structure. Returns false on failure.
|
||||
bool Update(bool revert, const KnapsackAssignmentForCuts& assignment);
|
||||
bool Update(bool revert, const KnapsackAssignmentForCuts &assignment);
|
||||
// ComputeProfitBounds should set 'profit_lower_bound_' and
|
||||
// 'profit_upper_bound_' which are constraint specific.
|
||||
void ComputeProfitBounds();
|
||||
@@ -256,19 +256,19 @@ class KnapsackPropagatorForCuts {
|
||||
|
||||
// Copies the current state into 'solution'.
|
||||
// All unbound items are set to false (i.e. not in the knapsack).
|
||||
void CopyCurrentStateToSolution(std::vector<bool>* solution) const;
|
||||
void CopyCurrentStateToSolution(std::vector<bool> *solution) const;
|
||||
|
||||
// Initializes the propagator. This method is called by Init() after filling
|
||||
// the fields defined in this class.
|
||||
void InitPropagator();
|
||||
|
||||
const KnapsackStateForCuts& state() const { return *state_; }
|
||||
const std::vector<KnapsackItemForCutsPtr>& items() const { return items_; }
|
||||
const KnapsackStateForCuts &state() const { return *state_; }
|
||||
const std::vector<KnapsackItemForCutsPtr> &items() const { return items_; }
|
||||
|
||||
void set_profit_lower_bound(double profit) { profit_lower_bound_ = profit; }
|
||||
void set_profit_upper_bound(double profit) { profit_upper_bound_ = profit; }
|
||||
|
||||
private:
|
||||
private:
|
||||
// An obvious additional profit upper bound corresponds to the linear
|
||||
// relaxation: remaining_capacity * efficiency of the break item.
|
||||
// It is possible to do better in O(1), using Martello-Toth bound U2.
|
||||
@@ -289,7 +289,7 @@ class KnapsackPropagatorForCuts {
|
||||
double current_profit_;
|
||||
double profit_lower_bound_;
|
||||
double profit_upper_bound_;
|
||||
const KnapsackStateForCuts* const state_;
|
||||
const KnapsackStateForCuts *const state_;
|
||||
};
|
||||
|
||||
// ----- KnapsackSolverForCuts -----
|
||||
@@ -298,22 +298,22 @@ class KnapsackPropagatorForCuts {
|
||||
// master propagator. Using SetMasterPropagator allows changing the default
|
||||
// (propagator of the first dimension).
|
||||
class KnapsackSolverForCuts {
|
||||
public:
|
||||
public:
|
||||
explicit KnapsackSolverForCuts(std::string solver_name);
|
||||
|
||||
KnapsackSolverForCuts(const KnapsackSolverForCuts&) = delete;
|
||||
KnapsackSolverForCuts& operator=(const KnapsackSolverForCuts&) = delete;
|
||||
KnapsackSolverForCuts(const KnapsackSolverForCuts &) = delete;
|
||||
KnapsackSolverForCuts &operator=(const KnapsackSolverForCuts &) = delete;
|
||||
|
||||
// Initializes the solver and enters the problem to be solved.
|
||||
void Init(const std::vector<double>& profits,
|
||||
const std::vector<double>& weights, const double capacity);
|
||||
void Init(const std::vector<double> &profits,
|
||||
const std::vector<double> &weights, const double capacity);
|
||||
int GetNumberOfItems() const { return state_.GetNumberOfItems(); }
|
||||
|
||||
// Gets the lower and the upper bound when the item is in or out of the
|
||||
// knapsack. To ensure objects are correctly initialized, this method should
|
||||
// not be called before Init().
|
||||
void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in,
|
||||
double* lower_bound, double* upper_bound);
|
||||
double *lower_bound, double *upper_bound);
|
||||
|
||||
// Get the best upper bound found so far.
|
||||
double GetUpperBound() { return GetAggregatedProfitUpperBound(); }
|
||||
@@ -336,31 +336,31 @@ class KnapsackSolverForCuts {
|
||||
void set_node_limit(const int64 node_limit) { node_limit_ = node_limit; }
|
||||
|
||||
// Solves the problem and returns the profit of the best solution found.
|
||||
double Solve(TimeLimit* time_limit, bool* is_solution_optimal);
|
||||
double Solve(TimeLimit *time_limit, bool *is_solution_optimal);
|
||||
// Returns true if the item 'item_id' is packed in the optimal knapsack.
|
||||
bool best_solution(int item_id) const {
|
||||
DCHECK(item_id < best_solution_.size());
|
||||
return best_solution_[item_id];
|
||||
}
|
||||
|
||||
const std::string& GetName() const { return solver_name_; }
|
||||
const std::string &GetName() const { return solver_name_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
// Updates propagator reverting/applying all decision on the path. Returns
|
||||
// true if the propagation fails. Note that even if it fails, propagator
|
||||
// should be updated to be in a stable state in order to stay incremental.
|
||||
bool UpdatePropagators(const KnapsackSearchPathForCuts& path);
|
||||
bool UpdatePropagators(const KnapsackSearchPathForCuts &path);
|
||||
// Updates propagator reverting/applying one decision. Returns true if
|
||||
// the propagation fails. Note that even if it fails, propagator should
|
||||
// be updated to be in a stable state in order to stay incremental.
|
||||
bool IncrementalUpdate(bool revert,
|
||||
const KnapsackAssignmentForCuts& assignment);
|
||||
const KnapsackAssignmentForCuts &assignment);
|
||||
// Updates the best solution if the current solution has a better profit.
|
||||
void UpdateBestSolution();
|
||||
|
||||
// Returns true if new relevant search node was added to the nodes array. That
|
||||
// means this node should be added to the search queue too.
|
||||
bool MakeNewNode(const KnapsackSearchNodeForCuts& node, bool is_in);
|
||||
bool MakeNewNode(const KnapsackSearchNodeForCuts &node, bool is_in);
|
||||
|
||||
// Gets the aggregated (min) profit upper bound among all propagators.
|
||||
double GetAggregatedProfitUpperBound();
|
||||
@@ -368,7 +368,7 @@ class KnapsackSolverForCuts {
|
||||
int GetNextItemId() const { return propagator_.GetNextItemId(); }
|
||||
|
||||
KnapsackPropagatorForCuts propagator_;
|
||||
std::vector<std::unique_ptr<KnapsackSearchNodeForCuts>> search_nodes_;
|
||||
std::vector<std::unique_ptr<KnapsackSearchNodeForCuts> > search_nodes_;
|
||||
KnapsackStateForCuts state_;
|
||||
double best_solution_profit_;
|
||||
std::vector<bool> best_solution_;
|
||||
@@ -381,6 +381,6 @@ class KnapsackSolverForCuts {
|
||||
};
|
||||
// TODO(user) : Add reduction algorithm.
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_KNAPSACK_SOLVER_FOR_CUTS_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_KNAPSACK_SOLVER_FOR_CUTS_H_
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
void SparsePermutation::RemoveCycles(const std::vector<int>& cycle_indices) {
|
||||
void SparsePermutation::RemoveCycles(const std::vector<int> &cycle_indices) {
|
||||
// TODO(user): make this a class member to avoid allocation if the complexity
|
||||
// becomes an issue. In this case, also optimize the loop below by not copying
|
||||
// the first cycles.
|
||||
@@ -32,8 +32,8 @@ void SparsePermutation::RemoveCycles(const std::vector<int>& cycle_indices) {
|
||||
<< "Duplicate index given to RemoveCycles(): " << i;
|
||||
should_be_deleted[i] = true;
|
||||
}
|
||||
int new_cycles_size = 0; // new index in cycles_
|
||||
int new_cycle_ends_size = 0; // new index in cycle_ends_
|
||||
int new_cycles_size = 0; // new index in cycles_
|
||||
int new_cycle_ends_size = 0; // new index in cycle_ends_
|
||||
int start = 0;
|
||||
for (int i = 0; i < NumCycles(); ++i) {
|
||||
const int end = cycle_ends_[i];
|
||||
@@ -51,25 +51,30 @@ void SparsePermutation::RemoveCycles(const std::vector<int>& cycle_indices) {
|
||||
|
||||
std::string SparsePermutation::DebugString() const {
|
||||
DCHECK_EQ(cycles_.empty(), cycle_ends_.empty());
|
||||
if (!cycles_.empty()) DCHECK_EQ(cycles_.size(), cycle_ends_.back());
|
||||
std::vector<std::vector<int>> cycles;
|
||||
if (!cycles_.empty())
|
||||
DCHECK_EQ(cycles_.size(), cycle_ends_.back());
|
||||
std::vector<std::vector<int> > cycles;
|
||||
int start = 0;
|
||||
for (const int end : cycle_ends_) {
|
||||
// Find the minimum.
|
||||
int min_pos = start;
|
||||
for (int i = start + 1; i < end; ++i) {
|
||||
if (cycles_[i] < cycles_[min_pos]) min_pos = i;
|
||||
if (cycles_[i] < cycles_[min_pos])
|
||||
min_pos = i;
|
||||
}
|
||||
std::vector<int> cycle;
|
||||
for (int i = min_pos; i < end; ++i) cycle.push_back(cycles_[i]);
|
||||
for (int i = start; i < min_pos; ++i) cycle.push_back(cycles_[i]);
|
||||
for (int i = min_pos; i < end; ++i)
|
||||
cycle.push_back(cycles_[i]);
|
||||
for (int i = start; i < min_pos; ++i)
|
||||
cycle.push_back(cycles_[i]);
|
||||
cycles.push_back(cycle);
|
||||
start = end;
|
||||
}
|
||||
std::sort(cycles.begin(), cycles.end());
|
||||
std::string out;
|
||||
for (const std::vector<int>& cycle : cycles) {
|
||||
if (!out.empty()) out += " ";
|
||||
for (const std::vector<int> &cycle : cycles) {
|
||||
if (!out.empty())
|
||||
out += " ";
|
||||
out += "(";
|
||||
out += absl::StrJoin(cycle, " ");
|
||||
out += ")";
|
||||
@@ -77,4 +82,4 @@ std::string SparsePermutation::DebugString() const {
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -25,8 +25,9 @@ namespace operations_research {
|
||||
// elements: it needs only O(K) memory for a permutation that displaces
|
||||
// K elements.
|
||||
class SparsePermutation {
|
||||
public:
|
||||
explicit SparsePermutation(int size) : size_(size) {} // Identity.
|
||||
public:
|
||||
explicit SparsePermutation(int size) : size_(size) {
|
||||
} // Identity.
|
||||
|
||||
// TODO(user,user): complete the reader API.
|
||||
int Size() const { return size_; }
|
||||
@@ -34,7 +35,7 @@ class SparsePermutation {
|
||||
|
||||
// Returns the "support" of this permutation; that is, the set of elements
|
||||
// displaced by it.
|
||||
const std::vector<int>& Support() const { return cycles_; }
|
||||
const std::vector<int> &Support() const { return cycles_; }
|
||||
|
||||
// The permutation has NumCycles() cycles numbered 0 .. NumCycles()-1.
|
||||
// To iterate over cycle #i of the permutation, do this:
|
||||
@@ -66,7 +67,7 @@ class SparsePermutation {
|
||||
|
||||
// Removes the cycles with given indices from the permutation. This
|
||||
// works in O(K) for a permutation displacing K elements.
|
||||
void RemoveCycles(const std::vector<int>& cycle_indices);
|
||||
void RemoveCycles(const std::vector<int> &cycle_indices);
|
||||
|
||||
// Output all non-identity cycles of the permutation, sorted
|
||||
// lexicographically (each cycle is described starting by its smallest
|
||||
@@ -75,7 +76,7 @@ class SparsePermutation {
|
||||
// Example: "(1 4 3) (5 9) (6 8 7)".
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
const int size_;
|
||||
std::vector<int> cycles_;
|
||||
std::vector<int> cycle_ends_;
|
||||
@@ -102,8 +103,8 @@ struct SparsePermutation::Iterator {
|
||||
typedef std::vector<int>::const_iterator const_iterator;
|
||||
|
||||
Iterator() {}
|
||||
Iterator(const std::vector<int>::const_iterator& b,
|
||||
const std::vector<int>::const_iterator& e)
|
||||
Iterator(const std::vector<int>::const_iterator &b,
|
||||
const std::vector<int>::const_iterator &e)
|
||||
: begin_(b), end_(e) {}
|
||||
|
||||
std::vector<int>::const_iterator begin() const { return begin_; }
|
||||
@@ -128,6 +129,6 @@ inline int SparsePermutation::LastElementInCycle(int i) const {
|
||||
return cycles_[cycle_ends_[i] - 1];
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_ALGORITHMS_SPARSE_PERMUTATION_H_
|
||||
#endif // OR_TOOLS_ALGORITHMS_SPARSE_PERMUTATION_H_
|
||||
|
||||
@@ -19,14 +19,13 @@ namespace operations_research {
|
||||
// Kahan summation compensation algorithm.
|
||||
//
|
||||
// http://en.wikipedia.org/wiki/Kahan_summation_algorithm
|
||||
template <typename FpNumber>
|
||||
class AccurateSum {
|
||||
public:
|
||||
template <typename FpNumber> class AccurateSum {
|
||||
public:
|
||||
// You may copy-construct an AccurateSum.
|
||||
AccurateSum() : sum_(), error_sum_() {}
|
||||
|
||||
// Adds an FpNumber to the sum.
|
||||
void Add(const FpNumber& value) {
|
||||
void Add(const FpNumber &value) {
|
||||
error_sum_ += value;
|
||||
const FpNumber new_sum = sum_ + error_sum_;
|
||||
error_sum_ += sum_ - new_sum;
|
||||
@@ -36,11 +35,11 @@ class AccurateSum {
|
||||
// Gets the value of the sum.
|
||||
FpNumber Value() const { return sum_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
FpNumber sum_;
|
||||
FpNumber error_sum_;
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_BASE_ACCURATE_SUM_H_
|
||||
#endif // OR_TOOLS_BASE_ACCURATE_SUM_H_
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
#include "ortools/base/adjustable_priority_queue.h"
|
||||
|
||||
#endif // OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_INL_H_
|
||||
#endif // OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_INL_H_
|
||||
|
||||
@@ -24,36 +24,35 @@
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/macros.h"
|
||||
|
||||
template <typename T, typename Comparator>
|
||||
class LowerPriorityThan {
|
||||
public:
|
||||
explicit LowerPriorityThan(Comparator* compare) : compare_(compare) {}
|
||||
bool operator()(T* a, T* b) const { return (*compare_)(*a, *b); }
|
||||
template <typename T, typename Comparator> class LowerPriorityThan {
|
||||
public:
|
||||
explicit LowerPriorityThan(Comparator *compare) : compare_(compare) {}
|
||||
bool operator()(T *a, T *b) const { return (*compare_)(*a, *b); }
|
||||
|
||||
private:
|
||||
Comparator* compare_;
|
||||
private:
|
||||
Comparator *compare_;
|
||||
};
|
||||
|
||||
template <typename T, typename Comp = std::less<T> >
|
||||
class AdjustablePriorityQueue {
|
||||
public:
|
||||
public:
|
||||
// The objects references 'c' and 'm' are not required to be alive for the
|
||||
// lifetime of this object.
|
||||
AdjustablePriorityQueue() {}
|
||||
AdjustablePriorityQueue(const Comp& c) : c_(c) {}
|
||||
AdjustablePriorityQueue(const AdjustablePriorityQueue&) = delete;
|
||||
AdjustablePriorityQueue& operator=(const AdjustablePriorityQueue&) = delete;
|
||||
AdjustablePriorityQueue(AdjustablePriorityQueue&&) = default;
|
||||
AdjustablePriorityQueue& operator=(AdjustablePriorityQueue&&) = default;
|
||||
AdjustablePriorityQueue(const Comp &c) : c_(c) {}
|
||||
AdjustablePriorityQueue(const AdjustablePriorityQueue &) = delete;
|
||||
AdjustablePriorityQueue &operator=(const AdjustablePriorityQueue &) = delete;
|
||||
AdjustablePriorityQueue(AdjustablePriorityQueue &&) = default;
|
||||
AdjustablePriorityQueue &operator=(AdjustablePriorityQueue &&) = default;
|
||||
|
||||
void Add(T* val) {
|
||||
void Add(T *val) {
|
||||
// Extend the size of the vector by one. We could just use
|
||||
// vector<T>::resize(), but maybe T is not default-constructible.
|
||||
elems_.push_back(val);
|
||||
AdjustUpwards(elems_.size() - 1);
|
||||
}
|
||||
|
||||
void Remove(T* val) {
|
||||
void Remove(T *val) {
|
||||
int end = elems_.size() - 1;
|
||||
int i = val->GetHeapIndex();
|
||||
if (i == end) {
|
||||
@@ -66,12 +65,12 @@ class AdjustablePriorityQueue {
|
||||
NoteChangedPriority(elems_[i]);
|
||||
}
|
||||
|
||||
bool Contains(const T* val) const {
|
||||
bool Contains(const T *val) const {
|
||||
int i = val->GetHeapIndex();
|
||||
return (i >= 0 && i < elems_.size() && elems_[i] == val);
|
||||
}
|
||||
|
||||
void NoteChangedPriority(T* val) {
|
||||
void NoteChangedPriority(T *val) {
|
||||
LowerPriorityThan<T, Comp> lower_priority(&c_);
|
||||
int i = val->GetHeapIndex();
|
||||
int parent = (i - 1) / 2;
|
||||
@@ -84,13 +83,14 @@ class AdjustablePriorityQueue {
|
||||
// If val ever changes its priority, you need to call this function
|
||||
// to notify the pq so it can move it in the heap accordingly.
|
||||
|
||||
T* Top() { return elems_[0]; }
|
||||
T *Top() { return elems_[0]; }
|
||||
|
||||
const T* Top() const { return elems_[0]; }
|
||||
const T *Top() const { return elems_[0]; }
|
||||
|
||||
void AllTop(std::vector<T*>* topvec) {
|
||||
void AllTop(std::vector<T *> *topvec) {
|
||||
topvec->clear();
|
||||
if (Size() == 0) return;
|
||||
if (Size() == 0)
|
||||
return;
|
||||
std::list<int> need_to_check_children;
|
||||
need_to_check_children.push_back(0);
|
||||
// Implements breadth-first search down tree, stopping whenever
|
||||
@@ -148,11 +148,11 @@ class AdjustablePriorityQueue {
|
||||
// This is for debugging, e.g. the caller can use it to
|
||||
// examine the heap for rationality w.r.t. other parts of the
|
||||
// program.
|
||||
const std::vector<T*>* Raw() const { return &elems_; }
|
||||
const std::vector<T *> *Raw() const { return &elems_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
void AdjustUpwards(int i) {
|
||||
T* const t = elems_[i];
|
||||
T *const t = elems_[i];
|
||||
while (i > 0) {
|
||||
const int parent = (i - 1) / 2;
|
||||
if (!c_(*elems_[parent], *t)) {
|
||||
@@ -167,7 +167,7 @@ class AdjustablePriorityQueue {
|
||||
}
|
||||
|
||||
void AdjustDownwards(int i) {
|
||||
T* const t = elems_[i];
|
||||
T *const t = elems_[i];
|
||||
while (true) {
|
||||
const int left_child = 1 + 2 * i;
|
||||
if (left_child >= elems_.size()) {
|
||||
@@ -190,7 +190,7 @@ class AdjustablePriorityQueue {
|
||||
}
|
||||
|
||||
Comp c_;
|
||||
std::vector<T*> elems_;
|
||||
std::vector<T *> elems_;
|
||||
};
|
||||
|
||||
#endif // OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_H_
|
||||
#endif // OR_TOOLS_BASE_ADJUSTABLE_PRIORITY_QUEUE_H_
|
||||
|
||||
@@ -22,21 +22,21 @@
|
||||
#else
|
||||
#define BASE_EXPORT __declspec(dllimport)
|
||||
#define BASE_EXPORT_PRIVATE __declspec(dllimport)
|
||||
#endif // defined(BASE_IMPLEMENTATION)
|
||||
#endif // defined(BASE_IMPLEMENTATION)
|
||||
|
||||
#else // defined(WIN32)
|
||||
#else // defined(WIN32)
|
||||
#if defined(BASE_IMPLEMENTATION)
|
||||
#define BASE_EXPORT __attribute__((visibility("default")))
|
||||
#define BASE_EXPORT_PRIVATE __attribute__((visibility("default")))
|
||||
#else
|
||||
#define BASE_EXPORT
|
||||
#define BASE_EXPORT_PRIVATE
|
||||
#endif // defined(BASE_IMPLEMENTATION)
|
||||
#endif // defined(BASE_IMPLEMENTATION)
|
||||
#endif
|
||||
|
||||
#else // defined(COMPONENT_BUILD)
|
||||
#else // defined(COMPONENT_BUILD)
|
||||
#define BASE_EXPORT
|
||||
#define BASE_EXPORT_PRIVATE
|
||||
#endif
|
||||
|
||||
#endif // OR_TOOLS_BASE_BASE_EXPORT_H_
|
||||
#endif // OR_TOOLS_BASE_BASE_EXPORT_H_
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
#endif // OR_TOOLS_BASE_BASICTYPES_H_
|
||||
#endif // OR_TOOLS_BASE_BASICTYPES_H_
|
||||
|
||||
@@ -28,7 +28,7 @@ void Bitmap::Resize(uint32 size, bool fill) {
|
||||
const uint32 old_array_size = array_size_;
|
||||
array_size_ = new_array_size;
|
||||
max_size_ = size;
|
||||
uint64* new_map = new uint64[array_size_];
|
||||
uint64 *new_map = new uint64[array_size_];
|
||||
memcpy(new_map, map_, old_array_size * sizeof(*map_));
|
||||
delete[] map_;
|
||||
map_ = new_map;
|
||||
@@ -38,4 +38,4 @@ void Bitmap::Resize(uint32 size, bool fill) {
|
||||
Set(index, fill);
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -24,24 +24,23 @@ inline uint64 OneBit64(int pos) { return GG_ULONGLONG(1) << pos; }
|
||||
inline uint64 BitPos64(uint64 pos) { return (pos & 63); }
|
||||
inline uint64 BitOffset64(uint64 pos) { return (pos >> 6); }
|
||||
inline uint64 BitLength64(uint64 size) { return ((size + 63) >> 6); }
|
||||
inline bool IsBitSet64(const uint64* const bitset, uint64 pos) {
|
||||
inline bool IsBitSet64(const uint64 *const bitset, uint64 pos) {
|
||||
return (bitset[BitOffset64(pos)] & OneBit64(BitPos64(pos)));
|
||||
}
|
||||
inline void SetBit64(uint64* const bitset, uint64 pos) {
|
||||
inline void SetBit64(uint64 *const bitset, uint64 pos) {
|
||||
bitset[BitOffset64(pos)] |= OneBit64(BitPos64(pos));
|
||||
}
|
||||
inline void ClearBit64(uint64* const bitset, uint64 pos) {
|
||||
inline void ClearBit64(uint64 *const bitset, uint64 pos) {
|
||||
bitset[BitOffset64(pos)] &= ~OneBit64(BitPos64(pos));
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace internal
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
public:
|
||||
// Constructor : This allocates on a uint32 boundary.
|
||||
// fill: true = initialize with 1's, false = initialize with 0's.
|
||||
explicit Bitmap(uint32 size, bool fill = false)
|
||||
: max_size_(size),
|
||||
array_size_(internal::BitLength64(size)),
|
||||
: max_size_(size), array_size_(internal::BitLength64(size)),
|
||||
map_(new uint64[array_size_]) {
|
||||
// initialize all of the bits
|
||||
SetAll(fill);
|
||||
@@ -76,12 +75,12 @@ class Bitmap {
|
||||
// Clears all bits in the bitmap
|
||||
void Clear() { SetAll(false); }
|
||||
|
||||
private:
|
||||
uint32 max_size_; // the upper bound of the bitmap
|
||||
private:
|
||||
uint32 max_size_; // the upper bound of the bitmap
|
||||
uint32 array_size_;
|
||||
uint64* map_; // the bitmap
|
||||
uint64 *map_; // the bitmap
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_BASE_BITMAP_H_
|
||||
#endif // OR_TOOLS_BASE_BITMAP_H_
|
||||
|
||||
@@ -23,31 +23,31 @@ namespace absl {
|
||||
|
||||
namespace cleanup_internal {
|
||||
|
||||
template <typename Callback>
|
||||
class Storage {
|
||||
template <typename Callback> class Storage {
|
||||
using InvokeT = absl::base_internal::invoke_result_t<Callback>;
|
||||
static_assert(std::is_same<InvokeT, void>::value, "");
|
||||
static_assert(!std::is_reference<Callback>::value, "");
|
||||
|
||||
public:
|
||||
public:
|
||||
Storage() : contains_callback_(false), callback_() {}
|
||||
|
||||
Storage(Storage&& other_storage)
|
||||
Storage(Storage &&other_storage)
|
||||
: contains_callback_(other_storage.ContainsCallback()),
|
||||
callback_(other_storage.ReleaseCallback()) {}
|
||||
|
||||
template <typename TheCallback>
|
||||
explicit Storage(TheCallback&& the_callback)
|
||||
explicit Storage(TheCallback &&the_callback)
|
||||
: contains_callback_(true),
|
||||
callback_(std::forward<TheCallback>(the_callback)) {}
|
||||
|
||||
template <typename OtherCallback>
|
||||
Storage(Storage<OtherCallback>&& other_storage) // NOLINT
|
||||
Storage(Storage<OtherCallback> &&other_storage) // NOLINT
|
||||
: contains_callback_(other_storage.ContainsCallback()),
|
||||
callback_(other_storage.ReleaseCallback()) {}
|
||||
|
||||
Storage& operator=(Storage&& other_storage) {
|
||||
if (ContainsCallback()) std::move(callback_)();
|
||||
Storage &operator=(Storage &&other_storage) {
|
||||
if (ContainsCallback())
|
||||
std::move(callback_)();
|
||||
contains_callback_ = other_storage.ContainsCallback();
|
||||
callback_ = other_storage.ReleaseCallback();
|
||||
return *this;
|
||||
@@ -68,60 +68,60 @@ class Storage {
|
||||
std::move(callback_)();
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
bool contains_callback_;
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
struct AccessStorage {
|
||||
template <template <typename> class Cleanup, typename Callback>
|
||||
static Storage<Callback>& From(Cleanup<Callback>& cleanup) {
|
||||
static Storage<Callback> &From(Cleanup<Callback> &cleanup) {
|
||||
return cleanup.storage_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cleanup_internal
|
||||
} // namespace cleanup_internal
|
||||
|
||||
template <typename Callback>
|
||||
class ABSL_MUST_USE_RESULT Cleanup {
|
||||
template <typename Callback> class ABSL_MUST_USE_RESULT Cleanup {
|
||||
using Storage = cleanup_internal::Storage<Callback>;
|
||||
using AccessStorage = cleanup_internal::AccessStorage;
|
||||
|
||||
public:
|
||||
public:
|
||||
Cleanup() = default;
|
||||
|
||||
Cleanup(Cleanup&&) = default;
|
||||
Cleanup(Cleanup &&) = default;
|
||||
|
||||
template <typename TheCallback>
|
||||
explicit Cleanup(TheCallback&& the_callback)
|
||||
explicit Cleanup(TheCallback &&the_callback)
|
||||
: storage_(std::forward<TheCallback>(the_callback)) {}
|
||||
|
||||
template <typename OtherCallback>
|
||||
Cleanup(Cleanup<OtherCallback>&& other_cleanup) // NOLINT
|
||||
Cleanup(Cleanup<OtherCallback> &&other_cleanup) // NOLINT
|
||||
: storage_(std::move(AccessStorage::From(other_cleanup))) {}
|
||||
|
||||
~Cleanup() {
|
||||
if (storage_.ContainsCallback()) storage_.InvokeCallback();
|
||||
if (storage_.ContainsCallback())
|
||||
storage_.InvokeCallback();
|
||||
}
|
||||
|
||||
// Assignment to a cleanup object behaves like destroying it and making a new
|
||||
// one in its place (analogous to `std::unique_ptr<T>` semantics).
|
||||
Cleanup& operator=(Cleanup&&) = default;
|
||||
Cleanup &operator=(Cleanup &&) = default;
|
||||
|
||||
bool is_released() const { return !storage_.ContainsCallback(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
friend AccessStorage;
|
||||
|
||||
Storage storage_;
|
||||
};
|
||||
|
||||
template <int&... PreventExplicitTemplateArguments, typename Callback>
|
||||
absl::Cleanup<absl::decay_t<Callback>> MakeCleanup(Callback&& callback) {
|
||||
return absl::Cleanup<absl::decay_t<Callback>>(
|
||||
template <int &... PreventExplicitTemplateArguments, typename Callback>
|
||||
absl::Cleanup<absl::decay_t<Callback> > MakeCleanup(Callback &&callback) {
|
||||
return absl::Cleanup<absl::decay_t<Callback> >(
|
||||
std::forward<Callback>(callback));
|
||||
}
|
||||
|
||||
} // namespace absl
|
||||
} // namespace absl
|
||||
|
||||
#endif // OR_TOOLS_BASE_CLEANUP_H_
|
||||
#endif // OR_TOOLS_BASE_CLEANUP_H_
|
||||
|
||||
@@ -18,29 +18,21 @@
|
||||
|
||||
namespace absl {
|
||||
|
||||
template <class T>
|
||||
inline void SetFlag(T* flag, const T& value) {
|
||||
template <class T> inline void SetFlag(T *flag, const T &value) {
|
||||
*flag = value;
|
||||
}
|
||||
|
||||
template <class T, class V>
|
||||
inline void SetFlag(T* flag, const V& value) {
|
||||
template <class T, class V> inline void SetFlag(T *flag, const V &value) {
|
||||
*flag = value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline const T& GetFlag(T* flag) {
|
||||
return *flag;
|
||||
}
|
||||
template <class T> inline const T &GetFlag(T *flag) { return *flag; }
|
||||
|
||||
template <class T>
|
||||
inline const T& GetFlag(const T& flag) {
|
||||
return flag;
|
||||
}
|
||||
template <class T> inline const T &GetFlag(const T &flag) { return flag; }
|
||||
|
||||
} // namespace absl
|
||||
} // namespace absl
|
||||
|
||||
#define ABSL_DECLARE_FLAG(t, n) DECLARE_##t(n)
|
||||
#define ABSL_FLAG(t, n, d, h) DEFINE_##t(n, d, h)
|
||||
|
||||
#endif // OR_TOOLS_BASE_COMMANDLINEFLAGS_H_
|
||||
#endif // OR_TOOLS_BASE_COMMANDLINEFLAGS_H_
|
||||
|
||||
@@ -73,36 +73,60 @@ namespace internal {
|
||||
|
||||
struct LogBase {
|
||||
template <typename ElementT>
|
||||
void Log(std::ostream &out, const ElementT &element) const { // NOLINT
|
||||
void Log(std::ostream &out, const ElementT &element) const { // NOLINT
|
||||
out << element;
|
||||
}
|
||||
void LogEllipsis(std::ostream &out) const { // NOLINT
|
||||
void LogEllipsis(std::ostream &out) const { // NOLINT
|
||||
out << "...";
|
||||
}
|
||||
};
|
||||
|
||||
struct LogShortBase : public LogBase {
|
||||
void LogOpening(std::ostream &out) const { out << "["; } // NOLINT
|
||||
void LogClosing(std::ostream &out) const { out << "]"; } // NOLINT
|
||||
void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT
|
||||
void LogSeparator(std::ostream &out) const { out << ", "; } // NOLINT
|
||||
void LogOpening(std::ostream &out) const {
|
||||
out << "[";
|
||||
} // NOLINT
|
||||
void LogClosing(std::ostream &out) const {
|
||||
out << "]";
|
||||
} // NOLINT
|
||||
void LogFirstSeparator(std::ostream &out) const {
|
||||
out << "";
|
||||
} // NOLINT
|
||||
void LogSeparator(std::ostream &out) const {
|
||||
out << ", ";
|
||||
} // NOLINT
|
||||
};
|
||||
|
||||
struct LogMultilineBase : public LogBase {
|
||||
void LogOpening(std::ostream &out) const { out << "["; } // NOLINT
|
||||
void LogClosing(std::ostream &out) const { out << "\n]"; } // NOLINT
|
||||
void LogFirstSeparator(std::ostream &out) const { out << "\n"; } // NOLINT
|
||||
void LogSeparator(std::ostream &out) const { out << "\n"; } // NOLINT
|
||||
void LogOpening(std::ostream &out) const {
|
||||
out << "[";
|
||||
} // NOLINT
|
||||
void LogClosing(std::ostream &out) const {
|
||||
out << "\n]";
|
||||
} // NOLINT
|
||||
void LogFirstSeparator(std::ostream &out) const {
|
||||
out << "\n";
|
||||
} // NOLINT
|
||||
void LogSeparator(std::ostream &out) const {
|
||||
out << "\n";
|
||||
} // NOLINT
|
||||
};
|
||||
|
||||
struct LogLegacyBase : public LogBase {
|
||||
void LogOpening(std::ostream &out) const { out << ""; } // NOLINT
|
||||
void LogClosing(std::ostream &out) const { out << ""; } // NOLINT
|
||||
void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT
|
||||
void LogSeparator(std::ostream &out) const { out << " "; } // NOLINT
|
||||
void LogOpening(std::ostream &out) const {
|
||||
out << "";
|
||||
} // NOLINT
|
||||
void LogClosing(std::ostream &out) const {
|
||||
out << "";
|
||||
} // NOLINT
|
||||
void LogFirstSeparator(std::ostream &out) const {
|
||||
out << "";
|
||||
} // NOLINT
|
||||
void LogSeparator(std::ostream &out) const {
|
||||
out << " ";
|
||||
} // NOLINT
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace internal
|
||||
|
||||
// LogShort uses [] braces and separates items with comma-spaces. For
|
||||
// example "[1, 2, 3]".
|
||||
@@ -113,11 +137,11 @@ struct LogShort : public internal::LogShortBase {
|
||||
// LogShortUpToN(max_elements) formats the same as LogShort but prints no more
|
||||
// than the max_elements elements.
|
||||
class LogShortUpToN : public internal::LogShortBase {
|
||||
public:
|
||||
public:
|
||||
explicit LogShortUpToN(int64 max_elements) : max_elements_(max_elements) {}
|
||||
int64 MaxElements() const { return max_elements_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
int64 max_elements_;
|
||||
};
|
||||
|
||||
@@ -140,12 +164,12 @@ struct LogMultiline : public internal::LogMultilineBase {
|
||||
// LogMultilineUpToN(max_elements) formats the same as LogMultiline but
|
||||
// prints no more than max_elements elements.
|
||||
class LogMultilineUpToN : public internal::LogMultilineBase {
|
||||
public:
|
||||
public:
|
||||
explicit LogMultilineUpToN(int64 max_elements)
|
||||
: max_elements_(max_elements) {}
|
||||
int64 MaxElements() const { return max_elements_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
int64 max_elements_;
|
||||
};
|
||||
|
||||
@@ -170,7 +194,7 @@ typedef LogShortUpTo100 LogDefault;
|
||||
// LogRangeToStream should be used to define operator<< for
|
||||
// STL and STL-like containers. For example, see stl_logging.h.
|
||||
template <typename IteratorT, typename PolicyT>
|
||||
inline void LogRangeToStream(std::ostream &out, // NOLINT
|
||||
inline void LogRangeToStream(std::ostream &out, // NOLINT
|
||||
IteratorT begin, IteratorT end,
|
||||
const PolicyT &policy) {
|
||||
policy.LogOpening(out);
|
||||
@@ -197,9 +221,8 @@ namespace detail {
|
||||
// LogContainer functions, so its lifetime should be confined to a
|
||||
// single logging statement. Objects of this type should not be
|
||||
// assigned to local variables.
|
||||
template <typename IteratorT, typename PolicyT>
|
||||
class RangeLogger {
|
||||
public:
|
||||
template <typename IteratorT, typename PolicyT> class RangeLogger {
|
||||
public:
|
||||
RangeLogger(const IteratorT &begin, const IteratorT &end,
|
||||
const PolicyT &policy)
|
||||
: begin_(begin), end_(end), policy_(policy) {}
|
||||
@@ -218,15 +241,14 @@ class RangeLogger {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
IteratorT begin_;
|
||||
IteratorT end_;
|
||||
PolicyT policy_;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
class EnumLogger {
|
||||
public:
|
||||
template <typename E> class EnumLogger {
|
||||
public:
|
||||
explicit EnumLogger(E e) : e_(e) {}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &out, const EnumLogger &v) {
|
||||
@@ -234,11 +256,11 @@ class EnumLogger {
|
||||
return out << static_cast<I>(v.e_);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
E e_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
// Log a range using "policy". For example:
|
||||
//
|
||||
@@ -273,7 +295,7 @@ detail::RangeLogger<IteratorT, LogDefault> LogRange(const IteratorT &begin,
|
||||
// elements, enclosed in [] braces.
|
||||
template <typename ContainerT, typename PolicyT>
|
||||
auto LogContainer(const ContainerT &container, const PolicyT &policy)
|
||||
-> decltype(gtl::LogRange(container.begin(), container.end(), policy)) {
|
||||
->decltype(gtl::LogRange(container.begin(), container.end(), policy)) {
|
||||
return gtl::LogRange(container.begin(), container.end(), policy);
|
||||
}
|
||||
|
||||
@@ -285,7 +307,7 @@ auto LogContainer(const ContainerT &container, const PolicyT &policy)
|
||||
// separation, no newlines, and with limit of 100 items.
|
||||
template <typename ContainerT>
|
||||
auto LogContainer(const ContainerT &container)
|
||||
-> decltype(gtl::LogContainer(container, LogDefault())) {
|
||||
->decltype(gtl::LogContainer(container, LogDefault())) {
|
||||
return gtl::LogContainer(container, LogDefault());
|
||||
}
|
||||
|
||||
@@ -293,12 +315,11 @@ auto LogContainer(const ContainerT &container)
|
||||
//
|
||||
// enum class Color { kRed, kGreen, kBlue };
|
||||
// LOG(INFO) << gtl::LogEnum(kRed);
|
||||
template <typename E>
|
||||
detail::EnumLogger<E> LogEnum(E e) {
|
||||
template <typename E> detail::EnumLogger<E> LogEnum(E e) {
|
||||
static_assert(std::is_enum<E>::value, "must be an enum");
|
||||
return detail::EnumLogger<E>(e);
|
||||
}
|
||||
|
||||
} // namespace gtl
|
||||
} // namespace gtl
|
||||
|
||||
#endif // OR_TOOLS_BASE_CONTAINER_LOGGING_H_
|
||||
#endif // OR_TOOLS_BASE_CONTAINER_LOGGING_H_
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define WIN32_LEAN_AND_MEAN // disables several conflicting macros
|
||||
#define WIN32_LEAN_AND_MEAN // disables several conflicting macros
|
||||
#include <windows.h>
|
||||
#elif defined(__GNUC__)
|
||||
#include <dlfcn.h>
|
||||
@@ -30,7 +30,7 @@
|
||||
#define NAMEOF(x) #x
|
||||
|
||||
class DynamicLibrary {
|
||||
public:
|
||||
public:
|
||||
DynamicLibrary() : library_handle_(nullptr) {}
|
||||
|
||||
~DynamicLibrary() {
|
||||
@@ -45,10 +45,10 @@ class DynamicLibrary {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TryToLoad(const std::string& library_name) {
|
||||
bool TryToLoad(const std::string &library_name) {
|
||||
library_name_ = std::string(library_name);
|
||||
#if defined(_MSC_VER)
|
||||
library_handle_ = static_cast<void*>(LoadLibrary(library_name.c_str()));
|
||||
library_handle_ = static_cast<void *>(LoadLibrary(library_name.c_str()));
|
||||
#elif defined(__GNUC__)
|
||||
library_handle_ = dlopen(library_name.c_str(), RTLD_NOW);
|
||||
#endif
|
||||
@@ -58,53 +58,52 @@ class DynamicLibrary {
|
||||
bool LibraryIsLoaded() const { return library_handle_ != nullptr; }
|
||||
|
||||
template <typename T>
|
||||
std::function<T> GetFunction(const char* function_name) {
|
||||
const void* function_address =
|
||||
std::function<T> GetFunction(const char *function_name) {
|
||||
const void *function_address =
|
||||
#if defined(_MSC_VER)
|
||||
static_cast<void*>(GetProcAddress(
|
||||
static_cast<void *>(GetProcAddress(
|
||||
static_cast<HINSTANCE>(library_handle_), function_name));
|
||||
#else
|
||||
dlsym(library_handle_, function_name);
|
||||
dlsym(library_handle_, function_name);
|
||||
#endif
|
||||
|
||||
CHECK(function_address != nullptr)
|
||||
<< "Error: could not find function " << std::string(function_name)
|
||||
<< " in " << library_name_;
|
||||
CHECK(function_address != nullptr) << "Error: could not find function "
|
||||
<< std::string(function_name) << " in "
|
||||
<< library_name_;
|
||||
|
||||
return TypeParser<T>::CreateFunction(function_address);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::function<T> GetFunction(const std::string& function_name) {
|
||||
std::function<T> GetFunction(const std::string &function_name) {
|
||||
return GetFunction<T>(function_name.c_str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void GetFunction(std::function<T>* function, const char* function_name) {
|
||||
void GetFunction(std::function<T> *function, const char *function_name) {
|
||||
*function = GetFunction<T>(function_name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void GetFunction(std::function<T>* function,
|
||||
void GetFunction(std::function<T> *function,
|
||||
const std::string function_name) {
|
||||
GetFunction<T>(function, function_name.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
void* library_handle_ = nullptr;
|
||||
private:
|
||||
void *library_handle_ = nullptr;
|
||||
std::string library_name_;
|
||||
|
||||
template <typename T>
|
||||
struct TypeParser {};
|
||||
template <typename T> struct TypeParser {
|
||||
};
|
||||
|
||||
template <typename Ret, typename... Args>
|
||||
struct TypeParser<Ret(Args...)> {
|
||||
static std::function<Ret(Args...)> CreateFunction(
|
||||
const void* function_address) {
|
||||
return std::function<Ret(Args...)>(reinterpret_cast<Ret (*)(Args...)>(
|
||||
const_cast<void*>(function_address)));
|
||||
template <typename Ret, typename... Args> struct TypeParser<Ret(Args...)> {
|
||||
static std::function<Ret(Args...)>
|
||||
CreateFunction(const void *function_address) {
|
||||
return std::function<Ret(Args...)>(reinterpret_cast<Ret(*)(Args...)>(
|
||||
const_cast<void *>(function_address)));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif // OR_TOOLS_BASE_DYNAMIC_LIBRARY_H_
|
||||
#endif // OR_TOOLS_BASE_DYNAMIC_LIBRARY_H_
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
namespace EncodingUtils {
|
||||
|
||||
// Returns the number of characters of a UTF8-encoded string.
|
||||
inline int UTF8StrLen(const std::string& utf8_str) {
|
||||
if (utf8_str.empty()) return 0;
|
||||
const char* c = utf8_str.c_str();
|
||||
inline int UTF8StrLen(const std::string &utf8_str) {
|
||||
if (utf8_str.empty())
|
||||
return 0;
|
||||
const char *c = utf8_str.c_str();
|
||||
int count = 0;
|
||||
while (*c != '\0') {
|
||||
++count;
|
||||
@@ -32,6 +33,6 @@ inline int UTF8StrLen(const std::string& utf8_str) {
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace EncodingUtils
|
||||
} // namespace EncodingUtils
|
||||
|
||||
#endif // OR_TOOLS_BASE_ENCODINGUTILS_H_
|
||||
#endif // OR_TOOLS_BASE_ENCODINGUTILS_H_
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
File::File(FILE* const f_des, const absl::string_view& name)
|
||||
File::File(FILE *const f_des, const absl::string_view &name)
|
||||
: f_(f_des), name_(name) {}
|
||||
|
||||
bool File::Delete(const char* const name) { return remove(name) == 0; }
|
||||
bool File::Delete(const char *const name) { return remove(name) == 0; }
|
||||
|
||||
bool File::Exists(const char* const name) { return access(name, F_OK) == 0; }
|
||||
bool File::Exists(const char *const name) { return access(name, F_OK) == 0; }
|
||||
|
||||
size_t File::Size() {
|
||||
struct stat f_stat;
|
||||
@@ -63,47 +63,49 @@ absl::Status File::Close(int flags) {
|
||||
absl::StrCat("Could not close file '", name_, "'"));
|
||||
}
|
||||
|
||||
void File::ReadOrDie(void* const buf, size_t size) {
|
||||
void File::ReadOrDie(void *const buf, size_t size) {
|
||||
CHECK_EQ(fread(buf, 1, size, f_), size);
|
||||
}
|
||||
|
||||
size_t File::Read(void* const buf, size_t size) {
|
||||
size_t File::Read(void *const buf, size_t size) {
|
||||
return fread(buf, 1, size, f_);
|
||||
}
|
||||
|
||||
void File::WriteOrDie(const void* const buf, size_t size) {
|
||||
void File::WriteOrDie(const void *const buf, size_t size) {
|
||||
CHECK_EQ(fwrite(buf, 1, size, f_), size);
|
||||
}
|
||||
size_t File::Write(const void* const buf, size_t size) {
|
||||
size_t File::Write(const void *const buf, size_t size) {
|
||||
return fwrite(buf, 1, size, f_);
|
||||
}
|
||||
|
||||
File* File::OpenOrDie(const char* const name, const char* const flag) {
|
||||
FILE* const f_des = fopen(name, flag);
|
||||
File *File::OpenOrDie(const char *const name, const char *const flag) {
|
||||
FILE *const f_des = fopen(name, flag);
|
||||
if (f_des == NULL) {
|
||||
std::cerr << "Cannot open " << name;
|
||||
exit(1);
|
||||
}
|
||||
File* const f = new File(f_des, name);
|
||||
File *const f = new File(f_des, name);
|
||||
return f;
|
||||
}
|
||||
|
||||
File* File::Open(const char* const name, const char* const flag) {
|
||||
FILE* const f_des = fopen(name, flag);
|
||||
if (f_des == NULL) return NULL;
|
||||
File* const f = new File(f_des, name);
|
||||
File *File::Open(const char *const name, const char *const flag) {
|
||||
FILE *const f_des = fopen(name, flag);
|
||||
if (f_des == NULL)
|
||||
return NULL;
|
||||
File *const f = new File(f_des, name);
|
||||
return f;
|
||||
}
|
||||
|
||||
char* File::ReadLine(char* const output, uint64 max_length) {
|
||||
char *File::ReadLine(char *const output, uint64 max_length) {
|
||||
return fgets(output, max_length, f_);
|
||||
}
|
||||
|
||||
int64 File::ReadToString(std::string* const output, uint64 max_length) {
|
||||
int64 File::ReadToString(std::string *const output, uint64 max_length) {
|
||||
CHECK(output != nullptr);
|
||||
output->clear();
|
||||
|
||||
if (max_length == 0) return 0;
|
||||
if (max_length == 0)
|
||||
return 0;
|
||||
|
||||
int64 needed = max_length;
|
||||
int bufsize = (needed < (2 << 20) ? needed : (2 << 20));
|
||||
@@ -123,12 +125,13 @@ int64 File::ReadToString(std::string* const output, uint64 max_length) {
|
||||
return (nread >= 0 ? static_cast<int64>(output->size()) : -1);
|
||||
}
|
||||
|
||||
size_t File::WriteString(const std::string& line) {
|
||||
size_t File::WriteString(const std::string &line) {
|
||||
return Write(line.c_str(), line.size());
|
||||
}
|
||||
|
||||
bool File::WriteLine(const std::string& line) {
|
||||
if (Write(line.c_str(), line.size()) != line.size()) return false;
|
||||
bool File::WriteLine(const std::string &line) {
|
||||
if (Write(line.c_str(), line.size()) != line.size())
|
||||
return false;
|
||||
return Write("\n", 1) == 1;
|
||||
}
|
||||
|
||||
@@ -139,8 +142,8 @@ bool File::Open() const { return f_ != NULL; }
|
||||
void File::Init() {}
|
||||
|
||||
namespace file {
|
||||
absl::Status Open(const absl::string_view& filename,
|
||||
const absl::string_view& mode, File** f, int flags) {
|
||||
absl::Status Open(const absl::string_view &filename,
|
||||
const absl::string_view &mode, File **f, int flags) {
|
||||
if (flags == Defaults()) {
|
||||
*f = File::Open(filename, mode.data());
|
||||
if (*f != nullptr) {
|
||||
@@ -151,29 +154,30 @@ absl::Status Open(const absl::string_view& filename,
|
||||
absl::StrCat("Could not open '", filename, "'"));
|
||||
}
|
||||
|
||||
File* OpenOrDie(const absl::string_view& filename,
|
||||
const absl::string_view& mode, int flags) {
|
||||
File* f;
|
||||
File *OpenOrDie(const absl::string_view &filename,
|
||||
const absl::string_view &mode, int flags) {
|
||||
File *f;
|
||||
CHECK_EQ(flags, Defaults());
|
||||
f = File::Open(filename, mode.data());
|
||||
CHECK(f != nullptr) << absl::StrCat("Could not open '", filename, "'");
|
||||
return f;
|
||||
}
|
||||
|
||||
absl::Status GetContents(const absl::string_view& filename, std::string* output,
|
||||
absl::Status GetContents(const absl::string_view &filename, std::string *output,
|
||||
int flags) {
|
||||
if (flags == Defaults()) {
|
||||
File* file = File::Open(filename, "r");
|
||||
File *file = File::Open(filename, "r");
|
||||
if (file != NULL) {
|
||||
const int64 size = file->Size();
|
||||
if (file->ReadToString(output, size) == size) return absl::OkStatus();
|
||||
if (file->ReadToString(output, size) == size)
|
||||
return absl::OkStatus();
|
||||
}
|
||||
}
|
||||
return absl::Status(absl::StatusCode::kInvalidArgument,
|
||||
absl::StrCat("Could not read '", filename, "'"));
|
||||
}
|
||||
|
||||
absl::Status WriteString(File* file, const absl::string_view& contents,
|
||||
absl::Status WriteString(File *file, const absl::string_view &contents,
|
||||
int flags) {
|
||||
if (flags == Defaults() && file != NULL &&
|
||||
file->Write(contents.data(), contents.size()) == contents.size() &&
|
||||
@@ -185,29 +189,29 @@ absl::Status WriteString(File* file, const absl::string_view& contents,
|
||||
absl::StrCat("Could not write ", contents.size(), " bytes"));
|
||||
}
|
||||
|
||||
absl::Status SetContents(const absl::string_view& filename,
|
||||
const absl::string_view& contents, int flags) {
|
||||
absl::Status SetContents(const absl::string_view &filename,
|
||||
const absl::string_view &contents, int flags) {
|
||||
return WriteString(File::Open(filename, "w"), contents, flags);
|
||||
}
|
||||
|
||||
bool ReadFileToString(const absl::string_view& file_name, std::string* output) {
|
||||
bool ReadFileToString(const absl::string_view &file_name, std::string *output) {
|
||||
return GetContents(file_name, output, file::Defaults()).ok();
|
||||
}
|
||||
|
||||
bool WriteStringToFile(const std::string& data,
|
||||
const absl::string_view& file_name) {
|
||||
bool WriteStringToFile(const std::string &data,
|
||||
const absl::string_view &file_name) {
|
||||
return SetContents(file_name, data, file::Defaults()).ok();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class NoOpErrorCollector : public google::protobuf::io::ErrorCollector {
|
||||
public:
|
||||
virtual void AddError(int line, int column, const std::string& message) {}
|
||||
public:
|
||||
virtual void AddError(int line, int column, const std::string &message) {}
|
||||
};
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
bool ReadFileToProto(const absl::string_view& file_name,
|
||||
google::protobuf::Message* proto) {
|
||||
bool ReadFileToProto(const absl::string_view &file_name,
|
||||
google::protobuf::Message *proto) {
|
||||
std::string str;
|
||||
if (!ReadFileToString(file_name, &str)) {
|
||||
LOG(INFO) << "Could not read " << file_name;
|
||||
@@ -235,78 +239,83 @@ bool ReadFileToProto(const absl::string_view& file_name,
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReadFileToProtoOrDie(const absl::string_view& file_name,
|
||||
google::protobuf::Message* proto) {
|
||||
void ReadFileToProtoOrDie(const absl::string_view &file_name,
|
||||
google::protobuf::Message *proto) {
|
||||
CHECK(ReadFileToProto(file_name, proto)) << "file_name: " << file_name;
|
||||
}
|
||||
|
||||
bool WriteProtoToASCIIFile(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name) {
|
||||
bool WriteProtoToASCIIFile(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name) {
|
||||
std::string proto_string;
|
||||
return google::protobuf::TextFormat::PrintToString(proto, &proto_string) &&
|
||||
WriteStringToFile(proto_string, file_name);
|
||||
}
|
||||
|
||||
void WriteProtoToASCIIFileOrDie(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name) {
|
||||
void WriteProtoToASCIIFileOrDie(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name) {
|
||||
CHECK(WriteProtoToASCIIFile(proto, file_name)) << "file_name: " << file_name;
|
||||
}
|
||||
|
||||
bool WriteProtoToFile(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name) {
|
||||
bool WriteProtoToFile(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name) {
|
||||
std::string proto_string;
|
||||
return proto.AppendToString(&proto_string) &&
|
||||
WriteStringToFile(proto_string, file_name);
|
||||
}
|
||||
|
||||
void WriteProtoToFileOrDie(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name) {
|
||||
void WriteProtoToFileOrDie(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name) {
|
||||
CHECK(WriteProtoToFile(proto, file_name)) << "file_name: " << file_name;
|
||||
}
|
||||
|
||||
absl::Status GetTextProto(const absl::string_view& filename,
|
||||
google::protobuf::Message* proto, int flags) {
|
||||
absl::Status GetTextProto(const absl::string_view &filename,
|
||||
google::protobuf::Message *proto, int flags) {
|
||||
if (flags == Defaults()) {
|
||||
if (ReadFileToProto(filename, proto)) return absl::OkStatus();
|
||||
if (ReadFileToProto(filename, proto))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
return absl::Status(
|
||||
absl::StatusCode::kInvalidArgument,
|
||||
absl::StrCat("Could not read proto from '", filename, "'."));
|
||||
}
|
||||
|
||||
absl::Status SetTextProto(const absl::string_view& filename,
|
||||
const google::protobuf::Message& proto, int flags) {
|
||||
absl::Status SetTextProto(const absl::string_view &filename,
|
||||
const google::protobuf::Message &proto, int flags) {
|
||||
if (flags == Defaults()) {
|
||||
if (WriteProtoToASCIIFile(proto, filename)) return absl::OkStatus();
|
||||
if (WriteProtoToASCIIFile(proto, filename))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
return absl::Status(
|
||||
absl::StatusCode::kInvalidArgument,
|
||||
absl::StrCat("Could not write proto to '", filename, "'."));
|
||||
}
|
||||
|
||||
absl::Status SetBinaryProto(const absl::string_view& filename,
|
||||
const google::protobuf::Message& proto, int flags) {
|
||||
absl::Status SetBinaryProto(const absl::string_view &filename,
|
||||
const google::protobuf::Message &proto, int flags) {
|
||||
if (flags == Defaults()) {
|
||||
if (WriteProtoToFile(proto, filename)) return absl::OkStatus();
|
||||
if (WriteProtoToFile(proto, filename))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
return absl::Status(
|
||||
absl::StatusCode::kInvalidArgument,
|
||||
absl::StrCat("Could not write proto to '", filename, "'."));
|
||||
}
|
||||
|
||||
absl::Status Delete(const absl::string_view& path, int flags) {
|
||||
absl::Status Delete(const absl::string_view &path, int flags) {
|
||||
if (flags == Defaults()) {
|
||||
if (remove(path.data()) == 0) return absl::OkStatus();
|
||||
if (remove(path.data()) == 0)
|
||||
return absl::OkStatus();
|
||||
}
|
||||
return absl::Status(absl::StatusCode::kInvalidArgument,
|
||||
absl::StrCat("Could not delete '", path, "'."));
|
||||
}
|
||||
|
||||
absl::Status Exists(const absl::string_view& path, int flags) {
|
||||
absl::Status Exists(const absl::string_view &path, int flags) {
|
||||
if (flags == Defaults()) {
|
||||
if (access(path.data(), F_OK) == 0) return absl::OkStatus();
|
||||
if (access(path.data(), F_OK) == 0)
|
||||
return absl::OkStatus();
|
||||
}
|
||||
return absl::Status(absl::StatusCode::kInvalidArgument,
|
||||
absl::StrCat("File '", path, "' does not exist."));
|
||||
}
|
||||
} // namespace file
|
||||
} // namespace file
|
||||
|
||||
@@ -30,56 +30,56 @@
|
||||
// This file defines some IO interfaces for compatibility with Google
|
||||
// IO specifications.
|
||||
class File {
|
||||
public:
|
||||
public:
|
||||
// Opens file "name" with flags specified by "flag".
|
||||
// Flags are defined by fopen(), that is "r", "r+", "w", "w+". "a", and "a+".
|
||||
static File* Open(const char* const name, const char* const flag);
|
||||
static File *Open(const char *const name, const char *const flag);
|
||||
|
||||
#ifndef SWIG // no overloading
|
||||
inline static File* Open(const absl::string_view& name,
|
||||
const char* const mode) {
|
||||
#ifndef SWIG // no overloading
|
||||
inline static File *Open(const absl::string_view &name,
|
||||
const char *const mode) {
|
||||
return Open(name.data(), mode);
|
||||
}
|
||||
#endif // SWIG
|
||||
#endif // SWIG
|
||||
|
||||
// Opens file "name" with flags specified by "flag".
|
||||
// If open failed, program will exit.
|
||||
static File* OpenOrDie(const char* const name, const char* const flag);
|
||||
static File *OpenOrDie(const char *const name, const char *const flag);
|
||||
|
||||
#ifndef SWIG // no overloading
|
||||
inline static File* OpenOrDie(const absl::string_view& name,
|
||||
const char* const flag) {
|
||||
#ifndef SWIG // no overloading
|
||||
inline static File *OpenOrDie(const absl::string_view &name,
|
||||
const char *const flag) {
|
||||
return OpenOrDie(name.data(), flag);
|
||||
}
|
||||
#endif // SWIG
|
||||
#endif // SWIG
|
||||
|
||||
// Reads "size" bytes to buff from file, buff should be pre-allocated.
|
||||
size_t Read(void* const buff, size_t size);
|
||||
size_t Read(void *const buff, size_t size);
|
||||
|
||||
// Reads "size" bytes to buff from file, buff should be pre-allocated.
|
||||
// If read failed, program will exit.
|
||||
void ReadOrDie(void* const buff, size_t size);
|
||||
void ReadOrDie(void *const buff, size_t size);
|
||||
|
||||
// Reads a line from file to a string.
|
||||
// Each line must be no more than max_length bytes.
|
||||
char* ReadLine(char* const output, uint64 max_length);
|
||||
char *ReadLine(char *const output, uint64 max_length);
|
||||
|
||||
// Reads the whole file to a string, with a maximum length of 'max_length'.
|
||||
// Returns the number of bytes read.
|
||||
int64 ReadToString(std::string* const line, uint64 max_length);
|
||||
int64 ReadToString(std::string *const line, uint64 max_length);
|
||||
|
||||
// Writes "size" bytes of buff to file, buff should be pre-allocated.
|
||||
size_t Write(const void* const buff, size_t size);
|
||||
size_t Write(const void *const buff, size_t size);
|
||||
|
||||
// Writes "size" bytes of buff to file, buff should be pre-allocated.
|
||||
// If write failed, program will exit.
|
||||
void WriteOrDie(const void* const buff, size_t size);
|
||||
void WriteOrDie(const void *const buff, size_t size);
|
||||
|
||||
// Writes a string to file.
|
||||
size_t WriteString(const std::string& line);
|
||||
size_t WriteString(const std::string &line);
|
||||
|
||||
// Writes a string to file and append a "\n".
|
||||
bool WriteLine(const std::string& line);
|
||||
bool WriteLine(const std::string &line);
|
||||
|
||||
// Closes the file.
|
||||
bool Close();
|
||||
@@ -98,20 +98,20 @@ class File {
|
||||
absl::string_view filename() const;
|
||||
|
||||
// Deletes a file.
|
||||
static bool Delete(const char* const name);
|
||||
static bool Delete(const absl::string_view& name) {
|
||||
static bool Delete(const char *const name);
|
||||
static bool Delete(const absl::string_view &name) {
|
||||
return Delete(name.data());
|
||||
}
|
||||
|
||||
// Tests if a file exists.
|
||||
static bool Exists(const char* const name);
|
||||
static bool Exists(const char *const name);
|
||||
|
||||
bool Open() const;
|
||||
|
||||
private:
|
||||
File(FILE* const descriptor, const absl::string_view& name);
|
||||
private:
|
||||
File(FILE *const descriptor, const absl::string_view &name);
|
||||
|
||||
FILE* f_;
|
||||
FILE *f_;
|
||||
const absl::string_view name_;
|
||||
};
|
||||
|
||||
@@ -119,42 +119,42 @@ namespace file {
|
||||
inline int Defaults() { return 0xBABA; }
|
||||
|
||||
// As of 2016-01, these methods can only be used with flags = file::Defaults().
|
||||
absl::Status Open(const absl::string_view& filename,
|
||||
const absl::string_view& mode, File** f, int flags);
|
||||
File* OpenOrDie(const absl::string_view& filename,
|
||||
const absl::string_view& mode, int flags);
|
||||
absl::Status GetTextProto(const absl::string_view& filename,
|
||||
google::protobuf::Message* proto, int flags);
|
||||
absl::Status SetTextProto(const absl::string_view& filename,
|
||||
const google::protobuf::Message& proto, int flags);
|
||||
absl::Status SetBinaryProto(const absl::string_view& filename,
|
||||
const google::protobuf::Message& proto, int flags);
|
||||
absl::Status SetContents(const absl::string_view& filename,
|
||||
const absl::string_view& contents, int flags);
|
||||
absl::Status GetContents(const absl::string_view& filename, std::string* output,
|
||||
absl::Status Open(const absl::string_view &filename,
|
||||
const absl::string_view &mode, File **f, int flags);
|
||||
File *OpenOrDie(const absl::string_view &filename,
|
||||
const absl::string_view &mode, int flags);
|
||||
absl::Status GetTextProto(const absl::string_view &filename,
|
||||
google::protobuf::Message *proto, int flags);
|
||||
absl::Status SetTextProto(const absl::string_view &filename,
|
||||
const google::protobuf::Message &proto, int flags);
|
||||
absl::Status SetBinaryProto(const absl::string_view &filename,
|
||||
const google::protobuf::Message &proto, int flags);
|
||||
absl::Status SetContents(const absl::string_view &filename,
|
||||
const absl::string_view &contents, int flags);
|
||||
absl::Status GetContents(const absl::string_view &filename, std::string *output,
|
||||
int flags);
|
||||
absl::Status WriteString(File* file, const absl::string_view& contents,
|
||||
absl::Status WriteString(File *file, const absl::string_view &contents,
|
||||
int flags);
|
||||
|
||||
bool ReadFileToString(const absl::string_view& file_name, std::string* output);
|
||||
bool WriteStringToFile(const std::string& data,
|
||||
const absl::string_view& file_name);
|
||||
bool ReadFileToProto(const absl::string_view& file_name,
|
||||
google::protobuf::Message* proto);
|
||||
void ReadFileToProtoOrDie(const absl::string_view& file_name,
|
||||
google::protobuf::Message* proto);
|
||||
bool WriteProtoToASCIIFile(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name);
|
||||
void WriteProtoToASCIIFileOrDie(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name);
|
||||
bool WriteProtoToFile(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name);
|
||||
void WriteProtoToFileOrDie(const google::protobuf::Message& proto,
|
||||
const absl::string_view& file_name);
|
||||
bool ReadFileToString(const absl::string_view &file_name, std::string *output);
|
||||
bool WriteStringToFile(const std::string &data,
|
||||
const absl::string_view &file_name);
|
||||
bool ReadFileToProto(const absl::string_view &file_name,
|
||||
google::protobuf::Message *proto);
|
||||
void ReadFileToProtoOrDie(const absl::string_view &file_name,
|
||||
google::protobuf::Message *proto);
|
||||
bool WriteProtoToASCIIFile(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name);
|
||||
void WriteProtoToASCIIFileOrDie(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name);
|
||||
bool WriteProtoToFile(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name);
|
||||
void WriteProtoToFileOrDie(const google::protobuf::Message &proto,
|
||||
const absl::string_view &file_name);
|
||||
|
||||
absl::Status Delete(const absl::string_view& path, int flags);
|
||||
absl::Status Exists(const absl::string_view& path, int flags);
|
||||
absl::Status Delete(const absl::string_view &path, int flags);
|
||||
absl::Status Exists(const absl::string_view &path, int flags);
|
||||
|
||||
} // namespace file
|
||||
} // namespace file
|
||||
|
||||
#endif // OR_TOOLS_BASE_FILE_H_
|
||||
#endif // OR_TOOLS_BASE_FILE_H_
|
||||
|
||||
@@ -31,39 +31,39 @@
|
||||
|
||||
// Implements the minimum interface for a range-based for loop iterator.
|
||||
class FileLineIterator {
|
||||
public:
|
||||
public:
|
||||
enum {
|
||||
DEFAULT = 0x0000,
|
||||
REMOVE_LINEFEED = DEFAULT,
|
||||
KEEP_LINEFEED = 0x0001, // Terminating \n in result.
|
||||
REMOVE_INLINE_CR = 0x0002, // Remove \r characters.
|
||||
REMOVE_BLANK_LINES = 0x0004, // Remove empty or \n-only lines.
|
||||
KEEP_LINEFEED = 0x0001, // Terminating \n in result.
|
||||
REMOVE_INLINE_CR = 0x0002, // Remove \r characters.
|
||||
REMOVE_BLANK_LINES = 0x0004, // Remove empty or \n-only lines.
|
||||
};
|
||||
|
||||
FileLineIterator(File* file, int options)
|
||||
: next_position_after_eol_(0),
|
||||
buffer_size_(0),
|
||||
file_(file),
|
||||
FileLineIterator(File *file, int options)
|
||||
: next_position_after_eol_(0), buffer_size_(0), file_(file),
|
||||
options_(options) {
|
||||
ReadNextLine();
|
||||
}
|
||||
const std::string& operator*() const { return line_; }
|
||||
bool operator!=(const FileLineIterator& other) const {
|
||||
const std::string &operator*() const { return line_; }
|
||||
bool operator!=(const FileLineIterator &other) const {
|
||||
return file_ != other.file_;
|
||||
}
|
||||
void operator++() { ReadNextLine(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
bool HasOption(int option) const { return options_ & option; }
|
||||
|
||||
void ReadNextLine() {
|
||||
line_.clear();
|
||||
if (file_ == nullptr) return;
|
||||
if (file_ == nullptr)
|
||||
return;
|
||||
do {
|
||||
while (true) {
|
||||
int i = next_position_after_eol_;
|
||||
for (; i < buffer_size_; ++i) {
|
||||
if (buffer_[i] == '\n') break;
|
||||
if (buffer_[i] == '\n')
|
||||
break;
|
||||
}
|
||||
if (i == buffer_size_) {
|
||||
line_.append(&buffer_[next_position_after_eol_],
|
||||
@@ -107,31 +107,33 @@ class FileLineIterator {
|
||||
char buffer_[kBufferSize];
|
||||
int next_position_after_eol_;
|
||||
int64 buffer_size_;
|
||||
File* file_;
|
||||
File *file_;
|
||||
std::string line_;
|
||||
const int options_;
|
||||
};
|
||||
|
||||
class FileLines {
|
||||
public:
|
||||
FileLines(const std::string& filename, int options) : options_(options) {
|
||||
if (!file::Open(filename, "r", &file_, file::Defaults()).ok()) return;
|
||||
public:
|
||||
FileLines(const std::string &filename, int options) : options_(options) {
|
||||
if (!file::Open(filename, "r", &file_, file::Defaults()).ok())
|
||||
return;
|
||||
}
|
||||
|
||||
explicit FileLines(const std::string& filename)
|
||||
explicit FileLines(const std::string &filename)
|
||||
: FileLines(filename, FileLineIterator::DEFAULT) {}
|
||||
|
||||
~FileLines() {
|
||||
if (file_ != nullptr) file_->Close(file::Defaults()).IgnoreError();
|
||||
if (file_ != nullptr)
|
||||
file_->Close(file::Defaults()).IgnoreError();
|
||||
}
|
||||
|
||||
FileLineIterator begin() { return FileLineIterator(file_, options_); }
|
||||
|
||||
FileLineIterator end() const { return FileLineIterator(nullptr, options_); }
|
||||
|
||||
private:
|
||||
File* file_;
|
||||
private:
|
||||
File *file_;
|
||||
const int options_;
|
||||
};
|
||||
|
||||
#endif // OR_TOOLS_UTIL_FILELINEITER_H_
|
||||
#endif // OR_TOOLS_UTIL_FILELINEITER_H_
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
namespace operations_research {
|
||||
// 32 bit version.
|
||||
static inline void mix(uint32& a, uint32& b, uint32& c) { // NOLINT
|
||||
static inline void mix(uint32 &a, uint32 &b, uint32 &c) { // NOLINT
|
||||
a -= b;
|
||||
a -= c;
|
||||
a ^= (c >> 13);
|
||||
@@ -56,7 +56,7 @@ static inline void mix(uint32& a, uint32& b, uint32& c) { // NOLINT
|
||||
}
|
||||
|
||||
// 64 bit version.
|
||||
static inline void mix(uint64& a, uint64& b, uint64& c) { // NOLINT
|
||||
static inline void mix(uint64 &a, uint64 &b, uint64 &c) { // NOLINT
|
||||
a -= b;
|
||||
a -= c;
|
||||
a ^= (c >> 43);
|
||||
@@ -95,60 +95,58 @@ static inline void mix(uint64& a, uint64& b, uint64& c) { // NOLINT
|
||||
c ^= (b >> 22);
|
||||
}
|
||||
inline uint32 Hash32NumWithSeed(uint32 num, uint32 c) {
|
||||
uint32 b = 0x9e3779b9UL; // The golden ratio; an arbitrary value.
|
||||
uint32 b = 0x9e3779b9UL; // The golden ratio; an arbitrary value.
|
||||
operations_research::mix(num, b, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
inline uint64 Hash64NumWithSeed(uint64 num, uint64 c) {
|
||||
uint64 b = GG_ULONGLONG(0xe08c1d668b756f82); // More of the golden ratio.
|
||||
uint64 b = GG_ULONGLONG(0xe08c1d668b756f82); // More of the golden ratio.
|
||||
operations_research::mix(num, b, c);
|
||||
return c;
|
||||
}
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
// Support a few hash<> operators, in the hash namespace.
|
||||
namespace std {
|
||||
template <class First, class Second>
|
||||
struct hash<std::pair<First, Second>> {
|
||||
size_t operator()(const std::pair<First, Second>& p) const {
|
||||
template <class First, class Second> struct hash<std::pair<First, Second> > {
|
||||
size_t operator()(const std::pair<First, Second> &p) const {
|
||||
size_t h1 = hash<First>()(p.first);
|
||||
size_t h2 = hash<Second>()(p.second);
|
||||
// The decision below is at compile time
|
||||
return (sizeof(h1) <= sizeof(uint32))
|
||||
? // NOLINT
|
||||
? // NOLINT
|
||||
operations_research::Hash32NumWithSeed(h1, h2)
|
||||
: operations_research::Hash64NumWithSeed(h1, h2);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, std::size_t N>
|
||||
struct hash<std::array<T, N>> {
|
||||
public:
|
||||
size_t operator()(const std::array<T, N>& t) const {
|
||||
template <class T, std::size_t N> struct hash<std::array<T, N> > {
|
||||
public:
|
||||
size_t operator()(const std::array<T, N> &t) const {
|
||||
uint64 current = 71;
|
||||
for (int index = 0; index < N; ++index) {
|
||||
const T& elem = t[index];
|
||||
const T &elem = t[index];
|
||||
const uint64 new_hash = hash<T>()(elem);
|
||||
current = operations_research::Hash64NumWithSeed(current, new_hash);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
// Less than operator for MSVC.
|
||||
bool operator()(const std::array<T, N>& a, const std::array<T, N>& b) const {
|
||||
bool operator()(const std::array<T, N> &a, const std::array<T, N> &b) const {
|
||||
return a < b;
|
||||
}
|
||||
static const size_t bucket_size = 4; // These are required by MSVC.
|
||||
static const size_t min_buckets = 8; // 4 and 8 are defaults.
|
||||
static const size_t bucket_size = 4; // These are required by MSVC.
|
||||
static const size_t min_buckets = 8; // 4 and 8 are defaults.
|
||||
};
|
||||
} // namespace std
|
||||
} // namespace std
|
||||
|
||||
#endif // SWIG
|
||||
#endif // SWIG
|
||||
|
||||
namespace util_hash {
|
||||
|
||||
inline uint64 Hash(uint64 num, uint64 c) {
|
||||
uint64 b = GG_ULONGLONG(0xe08c1d668b756f82); // More of the golden ratio.
|
||||
uint64 b = GG_ULONGLONG(0xe08c1d668b756f82); // More of the golden ratio.
|
||||
operations_research::mix(num, b, c);
|
||||
return c;
|
||||
}
|
||||
@@ -158,6 +156,6 @@ inline uint64 Hash(uint64 a, uint64 b, uint64 c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace util_hash
|
||||
} // namespace util_hash
|
||||
|
||||
#endif // OR_TOOLS_BASE_HASH_H_
|
||||
#endif // OR_TOOLS_BASE_HASH_H_
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <ostream> // NOLINT
|
||||
#include <ostream> // NOLINT
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
@@ -157,16 +157,15 @@
|
||||
|
||||
namespace gtl {
|
||||
|
||||
template <typename IntTypeName, typename _ValueType>
|
||||
class IntType;
|
||||
template <typename IntTypeName, typename _ValueType> class IntType;
|
||||
|
||||
// Defines the IntType using value_type and typedefs it to int_type_name.
|
||||
// The struct int_type_name ## _tag_ trickery is needed to ensure that a new
|
||||
// type is created per int_type_name.
|
||||
#define DEFINE_INT_TYPE(int_type_name, value_type) \
|
||||
struct int_type_name##_tag_ { \
|
||||
static constexpr absl::string_view TypeName() { return #int_type_name; } \
|
||||
}; \
|
||||
#define DEFINE_INT_TYPE(int_type_name, value_type) \
|
||||
struct int_type_name##_tag_ { \
|
||||
static constexpr absl::string_view TypeName() { return #int_type_name; } \
|
||||
}; \
|
||||
typedef ::gtl::IntType<int_type_name##_tag_, value_type> int_type_name;
|
||||
|
||||
// Holds a integral value (of type ValueType) and behaves as a
|
||||
@@ -179,11 +178,10 @@ class IntType;
|
||||
// the integer type value (see supported list above).
|
||||
//
|
||||
// This class is NOT thread-safe.
|
||||
template <typename IntTypeName, typename _ValueType>
|
||||
class IntType {
|
||||
public:
|
||||
typedef _ValueType ValueType; // for non-member operators
|
||||
typedef IntType<IntTypeName, ValueType> ThisType; // Syntactic sugar.
|
||||
template <typename IntTypeName, typename _ValueType> class IntType {
|
||||
public:
|
||||
typedef _ValueType ValueType; // for non-member operators
|
||||
typedef IntType<IntTypeName, ValueType> ThisType; // Syntactic sugar.
|
||||
|
||||
static constexpr absl::string_view TypeName() {
|
||||
return IntTypeName::TypeName();
|
||||
@@ -191,12 +189,12 @@ class IntType {
|
||||
|
||||
// Note that this may change from time to time without notice.
|
||||
struct Hasher {
|
||||
size_t operator()(const IntType& arg) const {
|
||||
size_t operator()(const IntType &arg) const {
|
||||
return static_cast<size_t>(arg.value());
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
// Default c'tor initializing value_ to 0.
|
||||
constexpr IntType() : value_(0) {}
|
||||
// C'tor explicitly initializing from a ValueType.
|
||||
@@ -212,26 +210,25 @@ class IntType {
|
||||
// static_cast<T>(var.value());
|
||||
constexpr ValueType value() const { return value_; }
|
||||
|
||||
template <typename ValType>
|
||||
constexpr ValType value() const {
|
||||
template <typename ValType> constexpr ValType value() const {
|
||||
return static_cast<ValType>(value_);
|
||||
}
|
||||
|
||||
// -- UNARY OPERATORS --------------------------------------------------------
|
||||
ThisType& operator++() { // prefix ++
|
||||
ThisType &operator++() { // prefix ++
|
||||
++value_;
|
||||
return *this;
|
||||
}
|
||||
const ThisType operator++(int v) { // postfix ++
|
||||
const ThisType operator++(int v) { // postfix ++
|
||||
ThisType temp(*this);
|
||||
++value_;
|
||||
return temp;
|
||||
}
|
||||
ThisType& operator--() { // prefix --
|
||||
ThisType &operator--() { // prefix --
|
||||
--value_;
|
||||
return *this;
|
||||
}
|
||||
const ThisType operator--(int v) { // postfix --
|
||||
const ThisType operator--(int v) { // postfix --
|
||||
ThisType temp(*this);
|
||||
--value_;
|
||||
return temp;
|
||||
@@ -242,33 +239,33 @@ class IntType {
|
||||
constexpr const ThisType operator-() const { return ThisType(-value_); }
|
||||
constexpr const ThisType operator~() const { return ThisType(~value_); }
|
||||
|
||||
// -- ASSIGNMENT OPERATORS ---------------------------------------------------
|
||||
// We support the following assignment operators: =, +=, -=, *=, /=, <<=, >>=
|
||||
// and %= for both ThisType and ValueType.
|
||||
#define INT_TYPE_ASSIGNMENT_OP(op) \
|
||||
ThisType& operator op(const ThisType& arg_value) { \
|
||||
value_ op arg_value.value(); \
|
||||
return *this; \
|
||||
} \
|
||||
ThisType& operator op(ValueType arg_value) { \
|
||||
value_ op arg_value; \
|
||||
return *this; \
|
||||
// -- ASSIGNMENT OPERATORS ---------------------------------------------------
|
||||
// We support the following assignment operators: =, +=, -=, *=, /=, <<=, >>=
|
||||
// and %= for both ThisType and ValueType.
|
||||
#define INT_TYPE_ASSIGNMENT_OP(op) \
|
||||
ThisType &operator op(const ThisType & arg_value) { \
|
||||
value_ op arg_value.value(); \
|
||||
return *this; \
|
||||
} \
|
||||
ThisType &operator op(ValueType arg_value) { \
|
||||
value_ op arg_value; \
|
||||
return *this; \
|
||||
}
|
||||
INT_TYPE_ASSIGNMENT_OP(+=);
|
||||
INT_TYPE_ASSIGNMENT_OP(-=);
|
||||
INT_TYPE_ASSIGNMENT_OP(*=);
|
||||
INT_TYPE_ASSIGNMENT_OP(/=);
|
||||
INT_TYPE_ASSIGNMENT_OP(<<=); // NOLINT
|
||||
INT_TYPE_ASSIGNMENT_OP(>>=); // NOLINT
|
||||
INT_TYPE_ASSIGNMENT_OP(%=);
|
||||
INT_TYPE_ASSIGNMENT_OP( += );
|
||||
INT_TYPE_ASSIGNMENT_OP( -= );
|
||||
INT_TYPE_ASSIGNMENT_OP( *= );
|
||||
INT_TYPE_ASSIGNMENT_OP( /= );
|
||||
INT_TYPE_ASSIGNMENT_OP( <<= ); // NOLINT
|
||||
INT_TYPE_ASSIGNMENT_OP( >>= ); // NOLINT
|
||||
INT_TYPE_ASSIGNMENT_OP( %= );
|
||||
#undef INT_TYPE_ASSIGNMENT_OP
|
||||
|
||||
ThisType& operator=(ValueType arg_value) {
|
||||
ThisType &operator=(ValueType arg_value) {
|
||||
value_ = arg_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// The integer value of type ValueType.
|
||||
ValueType value_;
|
||||
|
||||
@@ -280,8 +277,8 @@ class IntType {
|
||||
// We provide the << operator, primarily for logging purposes. Currently, there
|
||||
// seems to be no need for an >> operator.
|
||||
template <typename IntTypeName, typename ValueType>
|
||||
std::ostream& operator<<(std::ostream& os, // NOLINT
|
||||
IntType<IntTypeName, ValueType> arg) {
|
||||
std::ostream &operator<<(std::ostream &os, // NOLINT
|
||||
IntType<IntTypeName, ValueType> arg) {
|
||||
return os << arg.value();
|
||||
}
|
||||
|
||||
@@ -292,32 +289,32 @@ std::ostream& operator<<(std::ostream& os, // NOLINT
|
||||
//
|
||||
// NB: Although it is possible to do IntType * IntType and IntType / IntType,
|
||||
// it is probably non-sensical from a dimensionality analysis perspective.
|
||||
#define INT_TYPE_ARITHMETIC_OP(op) \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
constexpr IntType<IntTypeName, ValueType> operator op( \
|
||||
IntType<IntTypeName, ValueType> id_1, \
|
||||
IntType<IntTypeName, ValueType> id_2) { \
|
||||
return IntType<IntTypeName, ValueType>(id_1.value() op id_2.value()); \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
constexpr IntType<IntTypeName, ValueType> operator op( \
|
||||
IntType<IntTypeName, ValueType> id, \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType arg_val) { \
|
||||
return IntType<IntTypeName, ValueType>(id.value() op arg_val); \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
constexpr IntType<IntTypeName, ValueType> operator op( \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType arg_val, \
|
||||
IntType<IntTypeName, ValueType> id) { \
|
||||
return IntType<IntTypeName, ValueType>(arg_val op id.value()); \
|
||||
#define INT_TYPE_ARITHMETIC_OP(op) \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
constexpr IntType<IntTypeName, ValueType> operator op( \
|
||||
IntType<IntTypeName, ValueType> id_1, \
|
||||
IntType<IntTypeName, ValueType> id_2) { \
|
||||
return IntType<IntTypeName, ValueType>(id_1.value() op id_2.value()); \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
constexpr IntType<IntTypeName, ValueType> operator op( \
|
||||
IntType<IntTypeName, ValueType> id, \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType arg_val) { \
|
||||
return IntType<IntTypeName, ValueType>(id.value() op arg_val); \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
constexpr IntType<IntTypeName, ValueType> operator op( \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType arg_val, \
|
||||
IntType<IntTypeName, ValueType> id) { \
|
||||
return IntType<IntTypeName, ValueType>(arg_val op id.value()); \
|
||||
}
|
||||
INT_TYPE_ARITHMETIC_OP(+);
|
||||
INT_TYPE_ARITHMETIC_OP(-);
|
||||
INT_TYPE_ARITHMETIC_OP(*);
|
||||
INT_TYPE_ARITHMETIC_OP(/);
|
||||
INT_TYPE_ARITHMETIC_OP(<<); // NOLINT
|
||||
INT_TYPE_ARITHMETIC_OP(>>); // NOLINT
|
||||
INT_TYPE_ARITHMETIC_OP(%);
|
||||
INT_TYPE_ARITHMETIC_OP( / );
|
||||
INT_TYPE_ARITHMETIC_OP( << ); // NOLINT
|
||||
INT_TYPE_ARITHMETIC_OP( >> ); // NOLINT
|
||||
INT_TYPE_ARITHMETIC_OP( % );
|
||||
#undef INT_TYPE_ARITHMETIC_OP
|
||||
|
||||
// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
|
||||
@@ -326,40 +323,41 @@ INT_TYPE_ARITHMETIC_OP(%);
|
||||
// IntType<IntTypeName, ValueType> OP IntType<IntTypeName, ValueType>
|
||||
// IntType<IntTypeName, ValueType> OP ValueType
|
||||
// ValueType OP IntType<IntTypeName, ValueType>
|
||||
#define INT_TYPE_COMPARISON_OP(op) \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
static inline constexpr bool operator op( \
|
||||
IntType<IntTypeName, ValueType> id_1, \
|
||||
IntType<IntTypeName, ValueType> id_2) { \
|
||||
return id_1.value() op id_2.value(); \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
static inline constexpr bool operator op( \
|
||||
IntType<IntTypeName, ValueType> id, \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType val) { \
|
||||
return id.value() op val; \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
static inline constexpr bool operator op( \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType val, \
|
||||
IntType<IntTypeName, ValueType> id) { \
|
||||
return val op id.value(); \
|
||||
#define INT_TYPE_COMPARISON_OP(op) \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
static inline constexpr bool operator op( \
|
||||
IntType<IntTypeName, ValueType> id_1, \
|
||||
IntType<IntTypeName, ValueType> id_2) { \
|
||||
return id_1.value() op id_2.value(); \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
static inline constexpr bool operator op( \
|
||||
IntType<IntTypeName, ValueType> id, \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType val) { \
|
||||
return id.value() op val; \
|
||||
} \
|
||||
template <typename IntTypeName, typename ValueType> \
|
||||
static inline constexpr bool operator op( \
|
||||
typename IntType<IntTypeName, ValueType>::ValueType val, \
|
||||
IntType<IntTypeName, ValueType> id) { \
|
||||
return val op id.value(); \
|
||||
}
|
||||
INT_TYPE_COMPARISON_OP(==); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP(!=); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP(<); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP(<=); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP(>); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP(>=); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP( == ); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP( != ); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP( < ); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP( <= ); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP( > ); // NOLINT
|
||||
INT_TYPE_COMPARISON_OP( >= ); // NOLINT
|
||||
#undef INT_TYPE_COMPARISON_OP
|
||||
|
||||
} // namespace gtl
|
||||
} // namespace gtl
|
||||
|
||||
// Allows it to be used as a key to hashable containers.
|
||||
namespace std {
|
||||
template <typename IntTypeName, typename ValueType>
|
||||
struct hash<gtl::IntType<IntTypeName, ValueType> >
|
||||
: gtl::IntType<IntTypeName, ValueType>::Hasher {};
|
||||
} // namespace std
|
||||
struct hash<gtl::IntType<IntTypeName, ValueType> > : gtl::IntType<
|
||||
IntTypeName, ValueType>::Hasher {
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#endif // OR_TOOLS_BASE_INT_TYPE_H_
|
||||
#endif // OR_TOOLS_BASE_INT_TYPE_H_
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace gtl {
|
||||
// STL vector ------------------------------------------------------------------
|
||||
template <typename IntType, typename T, typename Alloc = std::allocator<T> >
|
||||
class ITIVector {
|
||||
public:
|
||||
public:
|
||||
typedef IntType IndexType;
|
||||
typedef std::vector<T, Alloc> ParentType;
|
||||
|
||||
@@ -91,34 +91,34 @@ class ITIVector {
|
||||
typedef typename ParentType::reverse_iterator reverse_iterator;
|
||||
typedef typename ParentType::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
public:
|
||||
public:
|
||||
ITIVector() {}
|
||||
|
||||
explicit ITIVector(const allocator_type& a) : v_(a) {}
|
||||
explicit ITIVector(const allocator_type &a) : v_(a) {}
|
||||
explicit ITIVector(size_type n) : v_(n) {}
|
||||
|
||||
ITIVector(size_type n, const value_type& v,
|
||||
const allocator_type& a = allocator_type())
|
||||
ITIVector(size_type n, const value_type &v,
|
||||
const allocator_type &a = allocator_type())
|
||||
: v_(n, v, a) {}
|
||||
|
||||
ITIVector(std::initializer_list<value_type> il) // NOLINT(runtime/explicit)
|
||||
ITIVector(std::initializer_list<value_type> il) // NOLINT(runtime/explicit)
|
||||
: v_(il) {}
|
||||
|
||||
template <typename InputIteratorType>
|
||||
ITIVector(InputIteratorType first, InputIteratorType last,
|
||||
const allocator_type& a = allocator_type())
|
||||
const allocator_type &a = allocator_type())
|
||||
: v_(first, last, a) {}
|
||||
|
||||
// -- Accessors --------------------------------------------------------------
|
||||
// This const accessor is useful in defining the comparison operators below.
|
||||
const ParentType& get() const { return v_; }
|
||||
const ParentType &get() const { return v_; }
|
||||
// The mutable accessor is useful when using auxiliar methods relying on
|
||||
// vector parameters such as JoinUsing(), SplitStringUsing(), etc. Methods
|
||||
// relying solely on iterators (e.g. STLDeleteElements) should work just fine
|
||||
// without the need for mutable_get(). NB: It should be used only in this
|
||||
// case and thus should not be abused to index the underlying vector without
|
||||
// the appropriate IntType.
|
||||
ParentType* mutable_get() { return &v_; }
|
||||
ParentType *mutable_get() { return &v_; }
|
||||
|
||||
// -- Modified methods -------------------------------------------------------
|
||||
reference operator[](IndexType i) { return v_[Value(i)]; }
|
||||
@@ -127,9 +127,8 @@ class ITIVector {
|
||||
const_reference at(IndexType i) const { return v_.at(Value(i)); }
|
||||
|
||||
// -- Pass-through methods to STL vector -------------------------------------
|
||||
void assign(size_type n, const value_type& val) { v_.assign(n, val); }
|
||||
template <typename InputIt>
|
||||
void assign(InputIt f, InputIt l) {
|
||||
void assign(size_type n, const value_type &val) { v_.assign(n, val); }
|
||||
template <typename InputIt> void assign(InputIt f, InputIt l) {
|
||||
v_.assign(f, l);
|
||||
}
|
||||
void assign(std::initializer_list<value_type> ilist) { v_.assign(ilist); }
|
||||
@@ -147,25 +146,26 @@ class ITIVector {
|
||||
size_type max_size() const { return v_.max_size(); }
|
||||
|
||||
void resize(size_type new_size) { v_.resize(new_size); }
|
||||
void resize(size_type new_size, const value_type& x) {
|
||||
void resize(size_type new_size, const value_type &x) {
|
||||
v_.resize(new_size, x);
|
||||
}
|
||||
|
||||
size_type capacity() const { return v_.capacity(); }
|
||||
bool empty() const { return v_.empty(); }
|
||||
void reserve(size_type n) { v_.reserve(n); }
|
||||
void push_back(const value_type& x) { v_.push_back(x); }
|
||||
void push_back(value_type&& x) { v_.push_back(std::move(x)); } // NOLINT
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args) {
|
||||
void push_back(const value_type &x) { v_.push_back(x); }
|
||||
void push_back(value_type &&x) {
|
||||
v_.push_back(std::move(x));
|
||||
} // NOLINT
|
||||
template <typename... Args> void emplace_back(Args &&... args) {
|
||||
v_.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(const_iterator pos, Args&&... args) {
|
||||
iterator emplace(const_iterator pos, Args &&... args) {
|
||||
return v_.emplace(pos, std::forward<Args>(args)...);
|
||||
}
|
||||
void pop_back() { v_.pop_back(); }
|
||||
void swap(ITIVector& x) { v_.swap(x.v_); }
|
||||
void swap(ITIVector &x) { v_.swap(x.v_); }
|
||||
void clear() { v_.clear(); }
|
||||
|
||||
reference front() { return v_.front(); }
|
||||
@@ -179,13 +179,13 @@ class ITIVector {
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
return v_.erase(first, last);
|
||||
}
|
||||
iterator insert(const_iterator pos, const value_type& x) {
|
||||
iterator insert(const_iterator pos, const value_type &x) {
|
||||
return v_.insert(pos, x);
|
||||
}
|
||||
iterator insert(const_iterator pos, value_type&& x) { // NOLINT
|
||||
iterator insert(const_iterator pos, value_type &&x) { // NOLINT
|
||||
return v_.insert(pos, std::move(x));
|
||||
}
|
||||
iterator insert(const_iterator pos, size_type n, const value_type& x) {
|
||||
iterator insert(const_iterator pos, size_type n, const value_type &x) {
|
||||
return v_.insert(pos, n, x);
|
||||
}
|
||||
template <typename IIt>
|
||||
@@ -196,32 +196,31 @@ class ITIVector {
|
||||
return v_.insert(pos, ilist);
|
||||
}
|
||||
|
||||
friend bool operator==(const ITIVector& x, const ITIVector& y) {
|
||||
friend bool operator==(const ITIVector &x, const ITIVector &y) {
|
||||
return x.get() == y.get();
|
||||
}
|
||||
friend bool operator!=(const ITIVector& x, const ITIVector& y) {
|
||||
friend bool operator!=(const ITIVector &x, const ITIVector &y) {
|
||||
return x.get() != y.get();
|
||||
}
|
||||
friend bool operator<(const ITIVector& x, const ITIVector& y) {
|
||||
friend bool operator<(const ITIVector &x, const ITIVector &y) {
|
||||
return x.get() < y.get();
|
||||
}
|
||||
friend bool operator>(const ITIVector& x, const ITIVector& y) {
|
||||
friend bool operator>(const ITIVector &x, const ITIVector &y) {
|
||||
return x.get() > y.get();
|
||||
}
|
||||
friend bool operator<=(const ITIVector& x, const ITIVector& y) {
|
||||
friend bool operator<=(const ITIVector &x, const ITIVector &y) {
|
||||
return x.get() <= y.get();
|
||||
}
|
||||
friend bool operator>=(const ITIVector& x, const ITIVector& y) {
|
||||
friend bool operator>=(const ITIVector &x, const ITIVector &y) {
|
||||
return x.get() >= y.get();
|
||||
}
|
||||
friend void swap(ITIVector& x, ITIVector& y) { x.swap(y); }
|
||||
friend void swap(ITIVector &x, ITIVector &y) { x.swap(y); }
|
||||
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, const ITIVector& v) {
|
||||
template <typename H> friend H AbslHashValue(H h, const ITIVector &v) {
|
||||
return H::combine(std::move(h), v.v_);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
static size_type Value(IndexType i) { return i.template value<size_type>(); }
|
||||
|
||||
ParentType v_;
|
||||
@@ -230,6 +229,6 @@ class ITIVector {
|
||||
int_type_indexed_vector_must_have_integral_index);
|
||||
};
|
||||
|
||||
} // namespace gtl
|
||||
} // namespace gtl
|
||||
|
||||
#endif // OR_TOOLS_BASE_INT_TYPE_INDEXED_VECTOR_H_
|
||||
#endif // OR_TOOLS_BASE_INT_TYPE_INDEXED_VECTOR_H_
|
||||
|
||||
@@ -16,25 +16,25 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <iostream> // NOLINT
|
||||
#include <iostream> // NOLINT
|
||||
|
||||
// Detect 64 bit.
|
||||
#undef ARCH_K8
|
||||
#if defined(_MSC_VER) && defined(_WIN64)
|
||||
#define ARCH_K8
|
||||
#elif defined(__APPLE__) && defined(__GNUC__)
|
||||
#define ARCH_K8 // We only support 64 bit on Mac OS X.
|
||||
#define ARCH_K8 // We only support 64 bit on Mac OS X.
|
||||
#elif defined(__GNUC__) && defined(__LP64__) && !defined(__aarch64__)
|
||||
#define ARCH_K8 // Linux x86_64
|
||||
#define ARCH_K8 // Linux x86_64
|
||||
#endif
|
||||
|
||||
typedef signed char int8;
|
||||
typedef short int16; // NOLINT
|
||||
typedef short int16; // NOLINT
|
||||
typedef int int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16; // NOLINT
|
||||
typedef unsigned short uint16; // NOLINT
|
||||
typedef unsigned int uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
@@ -65,19 +65,19 @@ static const int64 kint64max =
|
||||
#ifdef STLPORT
|
||||
#include <cstdio>
|
||||
// int64 output not present in STL port.
|
||||
inline std::ostream& operator<<(std::ostream& os, int64 i) {
|
||||
inline std::ostream &operator<<(std::ostream &os, int64 i) {
|
||||
char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%" PRId64, i);
|
||||
os << buffer;
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, uint64 i) {
|
||||
inline std::ostream &operator<<(std::ostream &os, uint64 i) {
|
||||
char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%llu", i);
|
||||
os << buffer;
|
||||
return os;
|
||||
}
|
||||
#endif // STLPORT
|
||||
#endif // STLPORT
|
||||
|
||||
#endif // OR_TOOLS_BASE_INTEGRAL_TYPES_H_
|
||||
#endif // OR_TOOLS_BASE_INTEGRAL_TYPES_H_
|
||||
|
||||
@@ -16,24 +16,23 @@
|
||||
|
||||
namespace gtl {
|
||||
|
||||
template <class Container>
|
||||
class ReverseView {
|
||||
public:
|
||||
template <class Container> class ReverseView {
|
||||
public:
|
||||
typedef typename Container::const_reverse_iterator const_it;
|
||||
|
||||
ReverseView(const Container& c) : c_(c) {}
|
||||
ReverseView(const Container &c) : c_(c) {}
|
||||
const_it begin() const { return c_.rbegin(); }
|
||||
const_it end() const { return c_.rend(); }
|
||||
|
||||
private:
|
||||
const Container& c_;
|
||||
private:
|
||||
const Container &c_;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
ReverseView<Container> reversed_view(const Container& c) {
|
||||
ReverseView<Container> reversed_view(const Container &c) {
|
||||
return ReverseView<Container>(c);
|
||||
}
|
||||
|
||||
} // namespace gtl
|
||||
} // namespace gtl
|
||||
|
||||
#endif // OR_TOOLS_BASE_ITERATOR_ADAPTORS_H_
|
||||
#endif // OR_TOOLS_BASE_ITERATOR_ADAPTORS_H_
|
||||
|
||||
@@ -21,21 +21,23 @@
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
class JNIUtil {
|
||||
public:
|
||||
public:
|
||||
// Creates a Java jstring from a null-terminated UTF-8 encoded C String.
|
||||
// The caller must delete the jstring reference.
|
||||
static jstring MakeJString(JNIEnv* env, const char* cstr) {
|
||||
if (cstr == NULL) return NULL;
|
||||
static jstring MakeJString(JNIEnv *env, const char *cstr) {
|
||||
if (cstr == NULL)
|
||||
return NULL;
|
||||
return env->NewStringUTF(cstr);
|
||||
}
|
||||
|
||||
// Creates a null-terminated UTF-8 encoded C string from a jstring.
|
||||
// The returned string should be "delete[]"-ed when no longer needed.
|
||||
static char* MakeCString(JNIEnv* env, jstring str) {
|
||||
if (str == NULL) return NULL;
|
||||
static char *MakeCString(JNIEnv *env, jstring str) {
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
jsize length = env->GetStringUTFLength(str);
|
||||
const char* src = env->GetStringUTFChars(str, NULL);
|
||||
char* dst = new char[length + 1];
|
||||
const char *src = env->GetStringUTFChars(str, NULL);
|
||||
char *dst = new char[length + 1];
|
||||
memcpy(dst, src, length);
|
||||
dst[length] = '\0';
|
||||
env->ReleaseStringUTFChars(str, src);
|
||||
@@ -44,25 +46,26 @@ class JNIUtil {
|
||||
|
||||
// Creates a new char array from a jbyteArray.
|
||||
// The caller must delete[] the returned array.
|
||||
static char* MakeCharArray(JNIEnv* env, jbyteArray a, int* size) {
|
||||
static char *MakeCharArray(JNIEnv *env, jbyteArray a, int *size) {
|
||||
jsize n = env->GetArrayLength(a);
|
||||
*size = n;
|
||||
jbyte* jba = new jbyte[n];
|
||||
jbyte *jba = new jbyte[n];
|
||||
|
||||
env->GetByteArrayRegion(a, 0, n, jba);
|
||||
// We make use of the fact that jbyte's are really just chars.
|
||||
// If this changes (different VM, etc.) things will break.
|
||||
return reinterpret_cast<char*>(jba);
|
||||
return reinterpret_cast<char *>(jba);
|
||||
}
|
||||
|
||||
// Produces a jbyteArray from a char array.
|
||||
static jbyteArray MakeJByteArray(JNIEnv* env, const char* a, int size) {
|
||||
static jbyteArray MakeJByteArray(JNIEnv *env, const char *a, int size) {
|
||||
// Create empty array object
|
||||
jbyteArray output = env->NewByteArray(size);
|
||||
// Fill it
|
||||
env->SetByteArrayRegion(output, 0, size, reinterpret_cast<const jbyte*>(a));
|
||||
env->SetByteArrayRegion(output, 0, size,
|
||||
reinterpret_cast<const jbyte *>(a));
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // OR_TOOLS_BASE_JNIUTIL_H_
|
||||
#endif // OR_TOOLS_BASE_JNIUTIL_H_
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
#include <mutex> // NOLINT
|
||||
#include <mutex> // NOLINT
|
||||
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
|
||||
@@ -22,10 +22,12 @@ DECLARE_bool(logtostderr);
|
||||
|
||||
namespace {
|
||||
std::once_flag init_done;
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
void FixFlagsAndEnvironmentForSwig() {
|
||||
std::call_once(init_done, [] { google::InitGoogleLogging("swig_helper"); });
|
||||
std::call_once(init_done, [] {
|
||||
google::InitGoogleLogging("swig_helper");
|
||||
});
|
||||
absl::SetFlag(&FLAGS_logtostderr, true);
|
||||
FLAGS_log_prefix = false;
|
||||
absl::SetFlag(&FLAGS_log_prefix, false);
|
||||
}
|
||||
|
||||
@@ -31,4 +31,4 @@
|
||||
// used by or-tools non C++ ports to bridge with the C++ layer.
|
||||
void FixFlagsAndEnvironmentForSwig();
|
||||
|
||||
#endif // OR_TOOLS_BASE_LOGGING_H_
|
||||
#endif // OR_TOOLS_BASE_LOGGING_H_
|
||||
|
||||
@@ -14,28 +14,26 @@
|
||||
#ifndef OR_TOOLS_BASE_MACROS_H_
|
||||
#define OR_TOOLS_BASE_MACROS_H_
|
||||
|
||||
#include <cstdlib> // for size_t.
|
||||
#include <cstdlib> // for size_t.
|
||||
|
||||
#define COMPILE_ASSERT(x, msg)
|
||||
|
||||
#ifdef NDEBUG
|
||||
const bool DEBUG_MODE = false;
|
||||
#else // NDEBUG
|
||||
#else // NDEBUG
|
||||
const bool DEBUG_MODE = true;
|
||||
#endif // NDEBUG
|
||||
#endif // NDEBUG
|
||||
|
||||
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
|
||||
// It goes in the private: declarations in a class.
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName &); \
|
||||
void operator=(const TypeName &)
|
||||
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
#ifndef COMPILER_MSVC
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(const T (&array)[N]))[N];
|
||||
template <typename T, size_t N> char (&ArraySizeHelper(const T (&array)[N]))[N];
|
||||
#endif
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
#endif // OR_TOOLS_BASE_MACROS_H_
|
||||
#endif // OR_TOOLS_BASE_MACROS_H_
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace gtl {
|
||||
// If the key is present in the map then the value associated with that
|
||||
// key is returned, otherwise the value passed as a default is returned.
|
||||
template <class Collection>
|
||||
const typename Collection::value_type::second_type& FindWithDefault(
|
||||
const Collection& collection,
|
||||
const typename Collection::value_type::first_type& key,
|
||||
const typename Collection::value_type::second_type& value) {
|
||||
const typename Collection::value_type::second_type &
|
||||
FindWithDefault(const Collection &collection,
|
||||
const typename Collection::value_type::first_type &key,
|
||||
const typename Collection::value_type::second_type &value) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
if (it == collection.end()) {
|
||||
return value;
|
||||
@@ -38,9 +38,9 @@ const typename Collection::value_type::second_type& FindWithDefault(
|
||||
// If the key is present a const pointer to the associated value is returned,
|
||||
// otherwise a NULL pointer is returned.
|
||||
template <class Collection>
|
||||
const typename Collection::value_type::second_type* FindOrNull(
|
||||
const Collection& collection,
|
||||
const typename Collection::value_type::first_type& key) {
|
||||
const typename Collection::value_type::second_type *
|
||||
FindOrNull(const Collection &collection,
|
||||
const typename Collection::value_type::first_type &key) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
if (it == collection.end()) {
|
||||
return 0;
|
||||
@@ -52,9 +52,9 @@ const typename Collection::value_type::second_type* FindOrNull(
|
||||
// Same as above but the returned pointer is not const and can be used to change
|
||||
// the stored value.
|
||||
template <class Collection>
|
||||
typename Collection::value_type::second_type* FindOrNull(
|
||||
Collection& collection, // NOLINT
|
||||
const typename Collection::value_type::first_type& key) {
|
||||
typename Collection::value_type::second_type *
|
||||
FindOrNull(Collection &collection, // NOLINT
|
||||
const typename Collection::value_type::first_type &key) {
|
||||
typename Collection::iterator it = collection.find(key);
|
||||
if (it == collection.end()) {
|
||||
return 0;
|
||||
@@ -67,9 +67,9 @@ typename Collection::value_type::second_type* FindOrNull(
|
||||
// returned, otherwise a NULL pointer is returned. This function does not
|
||||
// distinguish between a missing key and a key mapped to a NULL value.
|
||||
template <class Collection>
|
||||
const typename Collection::value_type::second_type FindPtrOrNull(
|
||||
const Collection& collection,
|
||||
const typename Collection::value_type::first_type& key) {
|
||||
const typename Collection::value_type::second_type
|
||||
FindPtrOrNull(const Collection &collection,
|
||||
const typename Collection::value_type::first_type &key) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
if (it == collection.end()) {
|
||||
return 0;
|
||||
@@ -83,8 +83,8 @@ const typename Collection::value_type::second_type FindPtrOrNull(
|
||||
// provided. True indicates that an insert took place, false indicates an
|
||||
// update.
|
||||
template <class Collection, class Key, class Value>
|
||||
bool InsertOrUpdate(Collection* const collection, const Key& key,
|
||||
const Value& value) {
|
||||
bool InsertOrUpdate(Collection *const collection, const Key &key,
|
||||
const Value &value) {
|
||||
std::pair<typename Collection::iterator, bool> ret =
|
||||
collection->insert(typename Collection::value_type(key, value));
|
||||
if (!ret.second) {
|
||||
@@ -100,8 +100,8 @@ bool InsertOrUpdate(Collection* const collection, const Key& key,
|
||||
// inserted, otherwise nothing happens. True indicates that an insert
|
||||
// took place, false indicates the key was already present.
|
||||
template <class Collection>
|
||||
bool InsertIfNotPresent(Collection* const collection,
|
||||
const typename Collection::value_type& value) {
|
||||
bool InsertIfNotPresent(Collection *const collection,
|
||||
const typename Collection::value_type &value) {
|
||||
std::pair<typename Collection::iterator, bool> ret =
|
||||
collection->insert(value);
|
||||
return ret.second;
|
||||
@@ -112,8 +112,8 @@ bool InsertIfNotPresent(Collection* const collection,
|
||||
// inserted, otherwise nothing happens. True indicates that an insert
|
||||
// took place, false indicates the key was already present.
|
||||
template <class Collection, class Key, class Value>
|
||||
bool InsertIfNotPresent(Collection* const collection, const Key& key,
|
||||
const Value& value) {
|
||||
bool InsertIfNotPresent(Collection *const collection, const Key &key,
|
||||
const Value &value) {
|
||||
std::pair<typename Collection::iterator, bool> ret =
|
||||
collection->insert(typename Collection::value_type(key, value));
|
||||
return ret.second;
|
||||
@@ -123,8 +123,8 @@ bool InsertIfNotPresent(Collection* const collection, const Key& key,
|
||||
// Insert a new key into a std::set or std::unordered_set.
|
||||
// Dies if the key is already present.
|
||||
template <class Collection>
|
||||
void InsertOrDieNoPrint(Collection* const collection,
|
||||
const typename Collection::value_type& value) {
|
||||
void InsertOrDieNoPrint(Collection *const collection,
|
||||
const typename Collection::value_type &value) {
|
||||
CHECK(collection->insert(value).second);
|
||||
}
|
||||
|
||||
@@ -132,17 +132,17 @@ void InsertOrDieNoPrint(Collection* const collection,
|
||||
// Insert a new key into a std::set or std::unordered_set.
|
||||
// Dies if the key is already present.
|
||||
template <class Collection>
|
||||
void InsertOrDie(Collection* const collection,
|
||||
const typename Collection::value_type& value) {
|
||||
void InsertOrDie(Collection *const collection,
|
||||
const typename Collection::value_type &value) {
|
||||
CHECK(collection->insert(value).second) << "duplicate value: " << value;
|
||||
}
|
||||
|
||||
// Inserts a new key/value into a std::map or std::unordered_map.
|
||||
// Dies if the key is already present.
|
||||
template <class Collection>
|
||||
void InsertOrDie(Collection* const collection,
|
||||
const typename Collection::value_type::first_type& key,
|
||||
const typename Collection::value_type::second_type& data) {
|
||||
void InsertOrDie(Collection *const collection,
|
||||
const typename Collection::value_type::first_type &key,
|
||||
const typename Collection::value_type::second_type &data) {
|
||||
typedef typename Collection::value_type value_type;
|
||||
CHECK(collection->insert(value_type(key, data)).second)
|
||||
<< "duplicate key: " << key;
|
||||
@@ -152,8 +152,8 @@ void InsertOrDie(Collection* const collection,
|
||||
// If the key is present and value is non-NULL then a copy of the value
|
||||
// associated with the key is made into *value. Returns whether key was present.
|
||||
template <class Collection, class Key, class Value>
|
||||
bool FindCopy(const Collection& collection, const Key& key,
|
||||
Value* const value) {
|
||||
bool FindCopy(const Collection &collection, const Key &key,
|
||||
Value *const value) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
if (it == collection.end()) {
|
||||
return false;
|
||||
@@ -167,15 +167,15 @@ bool FindCopy(const Collection& collection, const Key& key,
|
||||
// Test to see if a std::set, std::map, std::unordered_set or std::unordered_map
|
||||
// contains a particular key. Returns true if the key is in the collection.
|
||||
template <class Collection, class Key>
|
||||
bool ContainsKey(const Collection& collection, const Key& key) {
|
||||
bool ContainsKey(const Collection &collection, const Key &key) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
return it != collection.end();
|
||||
}
|
||||
|
||||
template <class Collection>
|
||||
const typename Collection::value_type::second_type& FindOrDie(
|
||||
const Collection& collection,
|
||||
const typename Collection::value_type::first_type& key) {
|
||||
const typename Collection::value_type::second_type &
|
||||
FindOrDie(const Collection &collection,
|
||||
const typename Collection::value_type::first_type &key) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
CHECK(it != collection.end()) << "Map key not found: " << key;
|
||||
return it->second;
|
||||
@@ -183,9 +183,9 @@ const typename Collection::value_type::second_type& FindOrDie(
|
||||
|
||||
// Same as FindOrDie above, but doesn't log the key on failure.
|
||||
template <class Collection>
|
||||
const typename Collection::value_type::second_type& FindOrDieNoPrint(
|
||||
const Collection& collection,
|
||||
const typename Collection::value_type::first_type& key) {
|
||||
const typename Collection::value_type::second_type &
|
||||
FindOrDieNoPrint(const Collection &collection,
|
||||
const typename Collection::value_type::first_type &key) {
|
||||
typename Collection::const_iterator it = collection.find(key);
|
||||
CHECK(it != collection.end()) << "Map key not found";
|
||||
return it->second;
|
||||
@@ -193,9 +193,9 @@ const typename Collection::value_type::second_type& FindOrDieNoPrint(
|
||||
|
||||
// Same as above, but returns a non-const reference.
|
||||
template <class Collection>
|
||||
typename Collection::value_type::second_type& FindOrDieNoPrint(
|
||||
Collection& collection, // NOLINT
|
||||
const typename Collection::value_type::first_type& key) {
|
||||
typename Collection::value_type::second_type &
|
||||
FindOrDieNoPrint(Collection &collection, // NOLINT
|
||||
const typename Collection::value_type::first_type &key) {
|
||||
typename Collection::iterator it = collection.find(key);
|
||||
CHECK(it != collection.end()) << "Map key not found";
|
||||
return it->second;
|
||||
@@ -204,13 +204,13 @@ typename Collection::value_type::second_type& FindOrDieNoPrint(
|
||||
// Lookup a key in a std::map or std::unordered_map, insert it if it is not
|
||||
// present. Returns a reference to the value associated with the key.
|
||||
template <class Collection>
|
||||
typename Collection::value_type::second_type& LookupOrInsert(
|
||||
Collection* const collection,
|
||||
const typename Collection::value_type::first_type& key,
|
||||
const typename Collection::value_type::second_type& value) {
|
||||
typename Collection::value_type::second_type &
|
||||
LookupOrInsert(Collection *const collection,
|
||||
const typename Collection::value_type::first_type &key,
|
||||
const typename Collection::value_type::second_type &value) {
|
||||
std::pair<typename Collection::iterator, bool> ret =
|
||||
collection->insert(typename Collection::value_type(key, value));
|
||||
return ret.first->second;
|
||||
}
|
||||
} // namespace gtl
|
||||
#endif // OR_TOOLS_BASE_MAP_UTIL_H_
|
||||
} // namespace gtl
|
||||
#endif // OR_TOOLS_BASE_MAP_UTIL_H_
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
namespace operations_research {
|
||||
class MathUtil {
|
||||
public:
|
||||
public:
|
||||
// CeilOfRatio<IntegralType>
|
||||
// FloorOfRatio<IntegralType>
|
||||
// Returns the ceil (resp. floor) of the ratio of two integers.
|
||||
@@ -91,16 +91,10 @@ class MathUtil {
|
||||
// for special floating point values.
|
||||
// Note: 0.0 and -0.0 are not differentiated by Abs (Abs(0.0) is -0.0),
|
||||
// which should be OK: see the comment for Max above.
|
||||
template <typename T>
|
||||
static T Abs(const T x) {
|
||||
return x > 0 ? x : -x;
|
||||
}
|
||||
template <typename T> static T Abs(const T x) { return x > 0 ? x : -x; }
|
||||
|
||||
// Returns the square of x.
|
||||
template <typename T>
|
||||
static T Square(const T x) {
|
||||
return x * x;
|
||||
}
|
||||
template <typename T> static T Square(const T x) { return x * x; }
|
||||
|
||||
// Euclid's Algorithm.
|
||||
// Returns: the greatest common divisor of two unsigned integers x and y.
|
||||
@@ -115,13 +109,11 @@ class MathUtil {
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T IPow(T base, int exp) {
|
||||
template <typename T> static T IPow(T base, int exp) {
|
||||
return pow(base, exp);
|
||||
}
|
||||
|
||||
template <class IntOut, class FloatIn>
|
||||
static IntOut Round(FloatIn x) {
|
||||
template <class IntOut, class FloatIn> static IntOut Round(FloatIn x) {
|
||||
// We don't use sgn(x) below because there is no need to distinguish the
|
||||
// (x == 0) case. Also note that there are specialized faster versions
|
||||
// of this function for Intel, ARM and PPC processors at the bottom
|
||||
@@ -137,6 +129,6 @@ class MathUtil {
|
||||
|
||||
static int64 FastInt64Round(double x) { return Round<int64>(x); }
|
||||
};
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_BASE_MATHUTIL_H_
|
||||
#endif // OR_TOOLS_BASE_MATHUTIL_H_
|
||||
|
||||
@@ -23,6 +23,6 @@ namespace util_hash {
|
||||
inline uint64 MurmurHash64(const char *buf, const size_t len) {
|
||||
return ::operations_research::ThoroughHash(buf, len);
|
||||
}
|
||||
} // namespace util_hash
|
||||
} // namespace util_hash
|
||||
|
||||
#endif // OR_TOOLS_BASE_MURMUR_H_
|
||||
#endif // OR_TOOLS_BASE_MURMUR_H_
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace protobuf {
|
||||
namespace util {
|
||||
// RepeatedPtrField version.
|
||||
template <typename T>
|
||||
inline void Truncate(RepeatedPtrField<T>* array, int new_size) {
|
||||
inline void Truncate(RepeatedPtrField<T> *array, int new_size) {
|
||||
const int size = array->size();
|
||||
DCHECK_GE(size, new_size);
|
||||
array->DeleteSubrange(new_size, size - new_size);
|
||||
@@ -36,8 +36,8 @@ inline void Truncate(RepeatedPtrField<T>* array, int new_size) {
|
||||
// 1] where N is the number of elements in 'array', and RepeatedType must be
|
||||
// RepeatedField or RepeatedPtrField.
|
||||
// Returns number of elements erased.
|
||||
template <typename RepeatedType, typename IndexContainer = std::vector<int>>
|
||||
int RemoveAt(RepeatedType* array, const IndexContainer& indices) {
|
||||
template <typename RepeatedType, typename IndexContainer = std::vector<int> >
|
||||
int RemoveAt(RepeatedType *array, const IndexContainer &indices) {
|
||||
if (indices.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -62,8 +62,8 @@ int RemoveAt(RepeatedType* array, const IndexContainer& indices) {
|
||||
Truncate(array, write_index);
|
||||
return num_indices;
|
||||
}
|
||||
} // namespace util
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
} // namespace util
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // OR_TOOLS_BASE_PROTOBUF_UTIL_H_
|
||||
#endif // OR_TOOLS_BASE_PROTOBUF_UTIL_H_
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
namespace util_time {
|
||||
|
||||
inline ::absl::StatusOr<google::protobuf::Duration> EncodeGoogleApiProto(
|
||||
absl::Duration d) {
|
||||
inline ::absl::StatusOr<google::protobuf::Duration>
|
||||
EncodeGoogleApiProto(absl::Duration d) {
|
||||
google::protobuf::Duration proto;
|
||||
const int64 d_in_nano = ToInt64Nanoseconds(d);
|
||||
proto.set_seconds(static_cast<int64>(d_in_nano / 1000000000));
|
||||
@@ -32,16 +32,16 @@ inline ::absl::StatusOr<google::protobuf::Duration> EncodeGoogleApiProto(
|
||||
}
|
||||
|
||||
inline ::absl::Status EncodeGoogleApiProto(absl::Duration d,
|
||||
google::protobuf::Duration* proto) {
|
||||
google::protobuf::Duration *proto) {
|
||||
*proto = EncodeGoogleApiProto(d).value();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
inline ::absl::StatusOr<absl::Duration> DecodeGoogleApiProto(
|
||||
const google::protobuf::Duration& proto) {
|
||||
inline ::absl::StatusOr<absl::Duration>
|
||||
DecodeGoogleApiProto(const google::protobuf::Duration &proto) {
|
||||
return absl::Seconds(proto.seconds() + 1e-9 * proto.nanos());
|
||||
}
|
||||
|
||||
} // namespace util_time
|
||||
} // namespace util_time
|
||||
|
||||
#endif // OR_TOOLS_BASE_PROTOUTIL_H_
|
||||
#endif // OR_TOOLS_BASE_PROTOUTIL_H_
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#endif // OR_TOOLS_BASE_PTR_UTIL_H_
|
||||
#endif // OR_TOOLS_BASE_PTR_UTIL_H_
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef OR_TOOLS_BASE_PYTHON_SWIG_H_
|
||||
#define OR_TOOLS_BASE_PYTHON_SWIG_H_
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000 // Py3.3+
|
||||
#if PY_VERSION_HEX >= 0x03030000 // Py3.3+
|
||||
// Use Py3 unicode str() type for C++ strings.
|
||||
#ifdef PyString_FromStringAndSize
|
||||
#undef PyString_FromStringAndSize
|
||||
@@ -40,10 +40,10 @@
|
||||
#ifdef PyString_AsStringAndSize
|
||||
#undef PyString_AsStringAndSize
|
||||
#endif
|
||||
static inline int PyString_AsStringAndSize(PyObject* obj, char** buf,
|
||||
Py_ssize_t* psize) {
|
||||
static inline int PyString_AsStringAndSize(PyObject *obj, char **buf,
|
||||
Py_ssize_t *psize) {
|
||||
if (PyUnicode_Check(obj)) {
|
||||
*buf = const_cast<char*>(PyUnicode_AsUTF8AndSize(obj, psize));
|
||||
*buf = const_cast<char *>(PyUnicode_AsUTF8AndSize(obj, psize));
|
||||
return *buf == NULL ? -1 : 0;
|
||||
} else if (PyBytes_Check(obj)) {
|
||||
return PyBytes_AsStringAndSize(obj, buf, psize);
|
||||
@@ -51,57 +51,55 @@ static inline int PyString_AsStringAndSize(PyObject* obj, char** buf,
|
||||
PyErr_SetString(PyExc_TypeError, "Expecting str or bytes");
|
||||
return -1;
|
||||
}
|
||||
#endif // Py3.3+
|
||||
#endif // Py3.3+
|
||||
|
||||
template <class T>
|
||||
inline bool PyObjAs(PyObject* pystr, T* cstr) {
|
||||
T::undefined; // You need to define specialization PyObjAs<T>
|
||||
template <class T> inline bool PyObjAs(PyObject *pystr, T *cstr) {
|
||||
T::undefined; // You need to define specialization PyObjAs<T>
|
||||
}
|
||||
template <class T>
|
||||
inline PyObject* PyObjFrom(const T& c) {
|
||||
T::undefined; // You need to define specialization PyObjFrom<T>
|
||||
template <class T> inline PyObject *PyObjFrom(const T &c) {
|
||||
T::undefined; // You need to define specialization PyObjFrom<T>
|
||||
}
|
||||
|
||||
#ifdef HAS_GLOBAL_STRING
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* pystr, ::std::string* cstr) {
|
||||
char* buf;
|
||||
template <> inline bool PyObjAs(PyObject *pystr, ::std::string *cstr) {
|
||||
char *buf;
|
||||
Py_ssize_t len;
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
if (PyUnicode_Check(pystr)) {
|
||||
buf = PyUnicode_AsUTF8AndSize(pystr, &len);
|
||||
if (!buf) return false;
|
||||
} else // NOLINT
|
||||
if (!buf)
|
||||
return false;
|
||||
} else // NOLINT
|
||||
#endif
|
||||
if (PyBytes_AsStringAndSize(pystr, &buf, &len) == -1)
|
||||
return false;
|
||||
if (cstr) cstr->assign(buf, len);
|
||||
if (cstr)
|
||||
cstr->assign(buf, len);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
template <class T>
|
||||
inline bool PyObjAs(PyObject* pystr, std::string* cstr) {
|
||||
char* buf;
|
||||
template <class T> inline bool PyObjAs(PyObject *pystr, std::string *cstr) {
|
||||
char *buf;
|
||||
Py_ssize_t len;
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
if (PyUnicode_Check(pystr)) {
|
||||
buf = const_cast<char*>(PyUnicode_AsUTF8AndSize(pystr, &len));
|
||||
if (!buf) return false;
|
||||
} else // NOLINT
|
||||
buf = const_cast<char *>(PyUnicode_AsUTF8AndSize(pystr, &len));
|
||||
if (!buf)
|
||||
return false;
|
||||
} else // NOLINT
|
||||
#endif
|
||||
if (PyBytes_AsStringAndSize(pystr, &buf, &len) == -1)
|
||||
return false;
|
||||
if (cstr) cstr->assign(buf, len);
|
||||
if (cstr)
|
||||
cstr->assign(buf, len);
|
||||
return true;
|
||||
}
|
||||
#ifdef HAS_GLOBAL_STRING
|
||||
template <>
|
||||
inline PyObject* PyObjFrom(const ::std::string& c) {
|
||||
template <> inline PyObject *PyObjFrom(const ::std::string &c) {
|
||||
return PyString_FromStringAndSize(c.data(), c.size());
|
||||
}
|
||||
#endif
|
||||
template <>
|
||||
inline PyObject* PyObjFrom(const std::string& c) {
|
||||
template <> inline PyObject *PyObjFrom(const std::string &c) {
|
||||
return PyString_FromStringAndSize(c.data(), c.size());
|
||||
}
|
||||
|
||||
@@ -109,65 +107,69 @@ inline PyObject* PyObjFrom(const std::string& c) {
|
||||
|
||||
#include <limits>
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, int* c) {
|
||||
long i = PyInt_AsLong(py); // NOLINT
|
||||
if (i == -1 && PyErr_Occurred()) // TypeError or OverflowError.
|
||||
return false; // Not a Python int.
|
||||
template <> inline bool PyObjAs(PyObject *py, int *c) {
|
||||
long i = PyInt_AsLong(py); // NOLINT
|
||||
if (i == -1 && PyErr_Occurred()) // TypeError or OverflowError.
|
||||
return false; // Not a Python int.
|
||||
if (i < std::numeric_limits<int>::min() ||
|
||||
i > std::numeric_limits<int>::max())
|
||||
return false; // Not C int.
|
||||
if (c) *c = static_cast<int>(i);
|
||||
return false; // Not C int.
|
||||
if (c)
|
||||
*c = static_cast<int>(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, unsigned int* c) {
|
||||
long i = PyInt_AsLong(py); // NOLINT
|
||||
if (i == -1 && PyErr_Occurred()) return false; // Not a Python int.
|
||||
if (i < 0 || i > std::numeric_limits<unsigned int>::max()) return false;
|
||||
if (c) *c = static_cast<unsigned int>(i);
|
||||
template <> inline bool PyObjAs(PyObject *py, unsigned int *c) {
|
||||
long i = PyInt_AsLong(py); // NOLINT
|
||||
if (i == -1 && PyErr_Occurred())
|
||||
return false; // Not a Python int.
|
||||
if (i < 0 || i > std::numeric_limits<unsigned int>::max())
|
||||
return false;
|
||||
if (c)
|
||||
*c = static_cast<unsigned int>(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, int64* c) { // NOLINT
|
||||
int64 i; // NOLINT
|
||||
template <> inline bool PyObjAs(PyObject *py, int64 *c) { // NOLINT
|
||||
int64 i; // NOLINT
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check(py)) {
|
||||
i = PyInt_AsLong(py);
|
||||
} else {
|
||||
if (!PyLong_Check(py)) return false; // Not a Python long.
|
||||
if (!PyLong_Check(py))
|
||||
return false; // Not a Python long.
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
i = PyLong_AsLongLong(py);
|
||||
if (i == -1 && PyErr_Occurred()) return false; // Not a C long long.
|
||||
if (i == -1 && PyErr_Occurred())
|
||||
return false; // Not a C long long.
|
||||
}
|
||||
if (c) *c = i;
|
||||
if (c)
|
||||
*c = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, uint64* c) { // NOLINT
|
||||
uint64 i; // NOLINT
|
||||
template <> inline bool PyObjAs(PyObject * py, uint64 * c) { // NOLINT
|
||||
uint64 i; // NOLINT
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check(py)) {
|
||||
i = PyInt_AsUnsignedLongLongMask(py);
|
||||
} else // NOLINT
|
||||
} else // NOLINT
|
||||
#endif
|
||||
{
|
||||
if (!PyLong_Check(py)) return false; // Not a Python long.
|
||||
{
|
||||
if (!PyLong_Check(py))
|
||||
return false; // Not a Python long.
|
||||
i = PyLong_AsUnsignedLongLong(py);
|
||||
if (i == (uint64)-1 && PyErr_Occurred()) // NOLINT
|
||||
if (i == (uint64) - 1 && PyErr_Occurred()) // NOLINT
|
||||
return false;
|
||||
}
|
||||
if (c) *c = i;
|
||||
if (c)
|
||||
*c = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, double* c) {
|
||||
template <> inline bool PyObjAs(PyObject * py, double * c) {
|
||||
double d;
|
||||
if (PyFloat_Check(py)) {
|
||||
d = PyFloat_AsDouble(py);
|
||||
@@ -175,80 +177,80 @@ inline bool PyObjAs(PyObject* py, double* c) {
|
||||
} else if (PyInt_Check(py)) {
|
||||
d = PyInt_AsLong(py);
|
||||
} else if (!PyLong_Check(py)) {
|
||||
return false; // float or int/long expected
|
||||
return false; // float or int/long expected
|
||||
#endif
|
||||
} else {
|
||||
d = PyLong_AsDouble(py);
|
||||
if (d == -1.0 && PyErr_Occurred()) {
|
||||
return false; // Overflow (or TypeError for PY3)
|
||||
return false; // Overflow (or TypeError for PY3)
|
||||
}
|
||||
}
|
||||
if (c) *c = d;
|
||||
if (c)
|
||||
*c = d;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline PyObject* PyObjFrom(const double& c) {
|
||||
template <> inline PyObject *PyObjFrom(const double & c) {
|
||||
return PyFloat_FromDouble(c);
|
||||
}
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, float* c) {
|
||||
template <> inline bool PyObjAs(PyObject * py, float * c) {
|
||||
double d;
|
||||
if (!PyObjAs(py, &d)) return false;
|
||||
if (c) *c = static_cast<float>(d);
|
||||
if (!PyObjAs(py, &d))
|
||||
return false;
|
||||
if (c)
|
||||
*c = static_cast<float>(d);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline PyObject* PyObjFrom(const float& c) {
|
||||
template <> inline PyObject *PyObjFrom(const float & c) {
|
||||
return PyFloat_FromDouble(c);
|
||||
}
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, bool* c) {
|
||||
if (!PyBool_Check(py)) return false; // Not a Python bool.
|
||||
if (c) *c = PyObject_Not(py) ? false : true;
|
||||
template <> inline bool PyObjAs(PyObject * py, bool * c) {
|
||||
if (!PyBool_Check(py))
|
||||
return false; // Not a Python bool.
|
||||
if (c)
|
||||
*c = PyObject_Not(py) ? false : true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int SwigPyIntOrLong_Check(PyObject* o) {
|
||||
inline int SwigPyIntOrLong_Check(PyObject * o) {
|
||||
return (PyLong_Check(o)
|
||||
#if PY_MAJOR_VERSION <= 2
|
||||
|| PyInt_Check(o)
|
||||
#endif
|
||||
); // NOLINT
|
||||
); // NOLINT
|
||||
}
|
||||
|
||||
inline PyObject* SwigString_FromString(const std::string& s) {
|
||||
inline PyObject *SwigString_FromString(const std::string & s) {
|
||||
return PyString_FromStringAndSize(s.data(), s.size());
|
||||
}
|
||||
|
||||
inline std::string SwigString_AsString(PyObject* o) {
|
||||
inline std::string SwigString_AsString(PyObject * o) {
|
||||
return std::string(PyString_AsString(o));
|
||||
}
|
||||
|
||||
// STL std::vector<T> for common types
|
||||
|
||||
namespace { // NOLINT
|
||||
namespace { // NOLINT
|
||||
|
||||
template <typename T>
|
||||
struct vector_pusher {
|
||||
typedef T* ptr;
|
||||
static void push(std::vector<T>* o, ptr e) { o->push_back(*e); }
|
||||
template <typename T> struct vector_pusher {
|
||||
typedef T *ptr;
|
||||
static void push(std::vector<T> *o, ptr e) { o->push_back(*e); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct vector_pusher<T*> {
|
||||
typedef T* ptr;
|
||||
static void push(std::vector<T*>* o, ptr e) { o->push_back(e); }
|
||||
template <typename T> struct vector_pusher<T *> {
|
||||
typedef T *ptr;
|
||||
static void push(std::vector<T *> *o, ptr e) { o->push_back(e); }
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
}; // namespace
|
||||
|
||||
template <class T>
|
||||
inline bool vector_input_helper(PyObject* seq, std::vector<T>* out,
|
||||
bool (*convert)(PyObject*, T* const)) {
|
||||
inline bool vector_input_helper(PyObject * seq, std::vector<T> * out,
|
||||
bool(*convert)(PyObject *, T * const)) {
|
||||
PyObject *item, *it = PyObject_GetIter(seq);
|
||||
if (!it) return false;
|
||||
if (!it)
|
||||
return false;
|
||||
T elem;
|
||||
while ((item = PyIter_Next(it))) {
|
||||
bool success = convert(item, &elem);
|
||||
@@ -257,15 +259,16 @@ inline bool vector_input_helper(PyObject* seq, std::vector<T>* out,
|
||||
Py_DECREF(it);
|
||||
return false;
|
||||
}
|
||||
if (out) out->push_back(elem);
|
||||
if (out)
|
||||
out->push_back(elem);
|
||||
}
|
||||
Py_DECREF(it);
|
||||
return static_cast<bool>(!PyErr_Occurred());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool vector_input_wrap_helper(PyObject* seq, std::vector<T>* out,
|
||||
swig_type_info* swig_Tp_type) {
|
||||
inline bool vector_input_wrap_helper(PyObject * seq, std::vector<T> * out,
|
||||
swig_type_info * swig_Tp_type) {
|
||||
PyObject *item, *it = PyObject_GetIter(seq);
|
||||
if (!it) {
|
||||
PyErr_SetString(PyExc_TypeError, "sequence expected");
|
||||
@@ -273,19 +276,20 @@ inline bool vector_input_wrap_helper(PyObject* seq, std::vector<T>* out,
|
||||
}
|
||||
typename vector_pusher<T>::ptr elem;
|
||||
while ((item = PyIter_Next(it))) {
|
||||
if (SWIG_ConvertPtr(item, reinterpret_cast<void**>(&elem), swig_Tp_type,
|
||||
if (SWIG_ConvertPtr(item, reinterpret_cast<void **>(&elem), swig_Tp_type,
|
||||
0) == -1) {
|
||||
Py_DECREF(it);
|
||||
it = PyObject_Repr(item);
|
||||
Py_DECREF(item);
|
||||
const char* repr = it ? PyString_AsString(it) : "not";
|
||||
const char *repr = it ? PyString_AsString(it) : "not";
|
||||
PyErr_Format(PyExc_TypeError, "'%s' expected, %s found",
|
||||
SWIG_TypePrettyName(swig_Tp_type), repr);
|
||||
Py_XDECREF(it);
|
||||
return false;
|
||||
}
|
||||
Py_DECREF(item);
|
||||
if (out) vector_pusher<T>::push(out, elem);
|
||||
if (out)
|
||||
vector_pusher<T>::push(out, elem);
|
||||
}
|
||||
Py_DECREF(it);
|
||||
return true;
|
||||
@@ -297,13 +301,15 @@ inline bool vector_input_wrap_helper(PyObject* seq, std::vector<T>* out,
|
||||
// The converter function converts a C++ object of type const T or const T&
|
||||
// into the corresponding Python object.
|
||||
template <class T, class Converter>
|
||||
inline PyObject* list_output_helper(const T* vec, Converter converter) {
|
||||
if (vec == NULL) Py_RETURN_NONE; // Return a nice out-of-band value.
|
||||
PyObject* lst = PyList_New(vec->size());
|
||||
if (lst == NULL) return NULL;
|
||||
inline PyObject *list_output_helper(const T * vec, Converter converter) {
|
||||
if (vec == NULL)
|
||||
Py_RETURN_NONE; // Return a nice out-of-band value.
|
||||
PyObject *lst = PyList_New(vec->size());
|
||||
if (lst == NULL)
|
||||
return NULL;
|
||||
int i = 0;
|
||||
for (typename T::const_reference pt : *vec) {
|
||||
PyObject* obj = converter(pt);
|
||||
PyObject *obj = converter(pt);
|
||||
if (!obj) {
|
||||
Py_DECREF(lst);
|
||||
return NULL;
|
||||
@@ -313,40 +319,40 @@ inline PyObject* list_output_helper(const T* vec, Converter converter) {
|
||||
return lst;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct OutConverter {
|
||||
PyObject* operator()(const T x) const {
|
||||
return SWIG_NewPointerObj((void*)x, type_, new_); // NOLINT
|
||||
template <class T> struct OutConverter {
|
||||
PyObject *operator()(const T x) const {
|
||||
return SWIG_NewPointerObj((void *)x, type_, new_); // NOLINT
|
||||
}
|
||||
swig_type_info* type_;
|
||||
swig_type_info *type_;
|
||||
int new_;
|
||||
OutConverter(swig_type_info* type, int new_object)
|
||||
OutConverter(swig_type_info *type, int new_object)
|
||||
: type_(type), new_(new_object) {}
|
||||
};
|
||||
|
||||
template <class T, class TR>
|
||||
inline PyObject* vector_output_helper(const std::vector<T>* vec,
|
||||
PyObject* (*converter)(const TR x)) {
|
||||
inline PyObject *vector_output_helper(const std::vector<T> * vec,
|
||||
PyObject * (*converter)(const TR x)) {
|
||||
return list_output_helper(vec, converter);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline PyObject* vector_output_helper(const std::vector<T*>* vec,
|
||||
const OutConverter<T*>& converter) {
|
||||
inline PyObject *vector_output_helper(const std::vector<T *> * vec,
|
||||
const OutConverter<T *> & converter) {
|
||||
return list_output_helper(vec, converter);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline PyObject* vector_output_wrap_helper(const std::vector<T*>* vec,
|
||||
swig_type_info* swig_Tp_type,
|
||||
inline PyObject *vector_output_wrap_helper(const std::vector<T *> * vec,
|
||||
swig_type_info * swig_Tp_type,
|
||||
bool newobj = false) {
|
||||
#if 1
|
||||
OutConverter<T*> converter(swig_Tp_type, newobj);
|
||||
OutConverter<T *> converter(swig_Tp_type, newobj);
|
||||
return vector_output_helper(vec, converter);
|
||||
#else // Lambda version
|
||||
auto converter = [](const T* x) {
|
||||
return SWIG_NewPointerObj((void*)x, swig_Tp_type, newobj); // NOLINT
|
||||
} return list_output_helper(vec, converter);
|
||||
#else // Lambda version
|
||||
auto converter = [](const T * x) {
|
||||
return SWIG_NewPointerObj((void *)x, swig_Tp_type, newobj); // NOLINT
|
||||
}
|
||||
return list_output_helper(vec, converter);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -359,4 +365,4 @@ inline PyObject* vector_output_wrap_helper(const std::vector<T*>* vec,
|
||||
#define PyInt_FromLong PyLong_FromLong
|
||||
#endif
|
||||
|
||||
#endif // OR_TOOLS_BASE_PYTHON_SWIG_H_
|
||||
#endif // OR_TOOLS_BASE_PYTHON_SWIG_H_
|
||||
|
||||
@@ -48,23 +48,23 @@ uint64 ACMRandom::operator()(uint64 val_max) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
static inline uint32 Word32At(const char* ptr) {
|
||||
static inline uint32 Word32At(const char *ptr) {
|
||||
return ((static_cast<uint32>(ptr[0])) + (static_cast<uint32>(ptr[1]) << 8) +
|
||||
(static_cast<uint32>(ptr[2]) << 16) +
|
||||
(static_cast<uint32>(ptr[3]) << 24));
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
int32 ACMRandom::HostnamePidTimeSeed() {
|
||||
char name[PATH_MAX + 20]; // need 12 bytes for 3 'empty' uint32's
|
||||
char name[PATH_MAX + 20]; // need 12 bytes for 3 'empty' uint32's
|
||||
assert(sizeof(name) - PATH_MAX > sizeof(uint32) * 3);
|
||||
|
||||
if (gethostname(name, PATH_MAX) != 0) {
|
||||
strcpy(name, "default-hostname"); // NOLINT
|
||||
strcpy(name, "default-hostname"); // NOLINT
|
||||
}
|
||||
const int namelen = strlen(name);
|
||||
for (size_t i = 0; i < sizeof(uint32) * 3; ++i) {
|
||||
name[namelen + i] = '\0'; // so we mix 0's once we get to end-of-string
|
||||
name[namelen + i] = '\0'; // so we mix 0's once we get to end-of-string
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
uint32 a = getpid();
|
||||
@@ -74,7 +74,7 @@ int32 ACMRandom::HostnamePidTimeSeed() {
|
||||
#elif defined(_MSC_VER)
|
||||
uint32 a = GetCurrentProcessId();
|
||||
uint32 b = GetTickCount();
|
||||
#else // Do not know what to do, returning 0.
|
||||
#else // Do not know what to do, returning 0.
|
||||
return 0;
|
||||
#endif
|
||||
uint32 c = 0;
|
||||
@@ -84,11 +84,11 @@ int32 ACMRandom::HostnamePidTimeSeed() {
|
||||
c += Word32At(name + i + sizeof(uint32) + sizeof(uint32));
|
||||
mix(a, b, c);
|
||||
}
|
||||
c += namelen; // one final mix
|
||||
c += namelen; // one final mix
|
||||
mix(a, b, c);
|
||||
return static_cast<int32>(c); // I guess the seed can be negative
|
||||
return static_cast<int32>(c); // I guess the seed can be negative
|
||||
}
|
||||
|
||||
int32 ACMRandom::DeterministicSeed() { return 0; }
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace operations_research {
|
||||
|
||||
// A wrapper around std::mt19937. Called ACMRandom for historical reasons.
|
||||
class ACMRandom {
|
||||
public:
|
||||
public:
|
||||
explicit ACMRandom(int32 seed) : generator_(seed) {}
|
||||
|
||||
// Unbounded generators.
|
||||
@@ -48,30 +48,30 @@ class ACMRandom {
|
||||
static constexpr result_type min() { return 0; }
|
||||
static constexpr result_type max() { return kuint32max; }
|
||||
|
||||
private:
|
||||
private:
|
||||
std::mt19937 generator_;
|
||||
};
|
||||
|
||||
class MTRandom : public ACMRandom {
|
||||
public:
|
||||
public:
|
||||
explicit MTRandom(int32 seed) : ACMRandom(seed) {}
|
||||
// MTRandom also supports a std::string seed.
|
||||
explicit MTRandom(const std::string& str_seed)
|
||||
explicit MTRandom(const std::string &str_seed)
|
||||
: ACMRandom(GenerateInt32SeedFromString(str_seed)) {}
|
||||
|
||||
MTRandom() : ACMRandom(ACMRandom::HostnamePidTimeSeed()) {}
|
||||
|
||||
private:
|
||||
int32 GenerateInt32SeedFromString(const std::string& str) {
|
||||
private:
|
||||
int32 GenerateInt32SeedFromString(const std::string &str) {
|
||||
uint32 seed = 1234567;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
seed *= 1000003; // prime
|
||||
seed *= 1000003; // prime
|
||||
seed += static_cast<uint32>(str[i]);
|
||||
}
|
||||
return seed >> 1; // Will fit into an int32.
|
||||
return seed >> 1; // Will fit into an int32.
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_BASE_RANDOM_H_
|
||||
#endif // OR_TOOLS_BASE_RANDOM_H_
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
namespace recordio {
|
||||
const int RecordWriter::kMagicNumber = 0x3ed7230a;
|
||||
|
||||
RecordWriter::RecordWriter(File* const file)
|
||||
RecordWriter::RecordWriter(File *const file)
|
||||
: file_(file), use_compression_(true) {}
|
||||
|
||||
bool RecordWriter::Close() { return file_->Close(); }
|
||||
@@ -32,16 +32,16 @@ void RecordWriter::set_use_compression(bool use_compression) {
|
||||
use_compression_ = use_compression;
|
||||
}
|
||||
|
||||
std::string RecordWriter::Compress(std::string const& s) const {
|
||||
const unsigned long source_size = s.size(); // NOLINT
|
||||
const char* source = s.c_str();
|
||||
std::string RecordWriter::Compress(std::string const &s) const {
|
||||
const unsigned long source_size = s.size(); // NOLINT
|
||||
const char *source = s.c_str();
|
||||
|
||||
unsigned long dsize = source_size + (source_size * 0.1f) + 16; // NOLINT
|
||||
unsigned long dsize = source_size + (source_size * 0.1f) + 16; // NOLINT
|
||||
std::unique_ptr<char[]> destination(new char[dsize]);
|
||||
// Use compress() from zlib.h.
|
||||
const int result =
|
||||
compress(reinterpret_cast<unsigned char*>(destination.get()), &dsize,
|
||||
reinterpret_cast<const unsigned char*>(source), source_size);
|
||||
compress(reinterpret_cast<unsigned char *>(destination.get()), &dsize,
|
||||
reinterpret_cast<const unsigned char *>(source), source_size);
|
||||
|
||||
if (result != Z_OK) {
|
||||
LOG(FATAL) << "Compress error occurred! Error code: " << result;
|
||||
@@ -49,21 +49,21 @@ std::string RecordWriter::Compress(std::string const& s) const {
|
||||
return std::string(destination.get(), dsize);
|
||||
}
|
||||
|
||||
RecordReader::RecordReader(File* const file) : file_(file) {}
|
||||
RecordReader::RecordReader(File *const file) : file_(file) {}
|
||||
|
||||
bool RecordReader::Close() { return file_->Close(); }
|
||||
|
||||
void RecordReader::Uncompress(const char* const source, uint64 source_size,
|
||||
char* const output_buffer,
|
||||
void RecordReader::Uncompress(const char *const source, uint64 source_size,
|
||||
char *const output_buffer,
|
||||
uint64 output_size) const {
|
||||
unsigned long result_size = output_size; // NOLINT
|
||||
// Use uncompress() from zlib.h
|
||||
unsigned long result_size = output_size; // NOLINT
|
||||
// Use uncompress() from zlib.h
|
||||
const int result =
|
||||
uncompress(reinterpret_cast<unsigned char*>(output_buffer), &result_size,
|
||||
reinterpret_cast<const unsigned char*>(source), source_size);
|
||||
uncompress(reinterpret_cast<unsigned char *>(output_buffer), &result_size,
|
||||
reinterpret_cast<const unsigned char *>(source), source_size);
|
||||
if (result != Z_OK) {
|
||||
LOG(FATAL) << "Uncompress error occurred! Error code: " << result;
|
||||
}
|
||||
CHECK_LE(result_size, static_cast<unsigned long>(output_size)); // NOLINT
|
||||
CHECK_LE(result_size, static_cast<unsigned long>(output_size)); // NOLINT
|
||||
}
|
||||
} // namespace recordio
|
||||
} // namespace recordio
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user