Quantcast
Viewing latest article 1
Browse Latest Browse All 2

Answer by sascha for ORTOOLS - CPSAT - Objective to minimize a value by intervals

This task looks rather strange, there is not much context and some parts of the task might touch some not-so-nice areas of finite-domain based solvers (large domains or scaling / divisions during solving).

Therefore: consider this as an idea / template!

Code

from ortools.sat.python import cp_model# DataINPUT = 30000INPUT_UB = 1000000TAX_A = 11TAX_B = 30TAX_C = 41# Helpers# new variable which is constrained to be equal to: given input-var MINUS constant# can get negative / wrap-arounddef aux_var_offset(model, var, offset):    aux_var = model.NewIntVar(-INPUT_UB, INPUT_UB, "")    model.Add(aux_var == var - offset)    return aux_var# new variable which is equal to the given input-var IFF >= 0; else 0def aux_var_nonnegative(model, var):    aux_var = model.NewIntVar(0, INPUT_UB, "")    model.AddMaxEquality(aux_var, [var, model.NewConstant(0)])    return aux_var# Modelmodel = cp_model.CpModel()# varssalary_var = model.NewIntVar(0, INPUT_UB, "salary")tax_component_a = model.NewIntVar(0, INPUT_UB, "tax_11")tax_component_b = model.NewIntVar(0, INPUT_UB, "tax_30")tax_component_c = model.NewIntVar(0, INPUT_UB, "tax_41")# constraintsmodel.AddMinEquality(tax_component_a, [    aux_var_nonnegative(model, aux_var_offset(model, salary_var, 10085)),    model.NewConstant(25710 - 10085)])model.AddMinEquality(tax_component_b, [    aux_var_nonnegative(model, aux_var_offset(model, salary_var, 25711)),    model.NewConstant(73516 - 25711)])model.Add(tax_component_c == aux_var_nonnegative(model,                                aux_var_offset(model, salary_var, 73516)))tax_full_scaled = tax_component_a * TAX_A + tax_component_b * TAX_B + tax_component_c * TAX_C# Demomodel.Add(salary_var == INPUT)solver = cp_model.CpSolver()status = solver.Solve(model)print(list(map(lambda x: solver.Value(x), [tax_component_a, tax_component_b, tax_component_c, tax_full_scaled])))

Output

[15625, 4289, 0, 300545]

Remarks

As implemented:

  • uses scaled solving
  • produces scaled solution (300545)
  • no fiddling with non-integral / ratio / rounding stuff BUT large domains

Alternative:

  • Maybe something around AddDivisionEquality

Edit in regards to Laurents comments

In some scenarios, solving the scaled problem but being able to reason about the real unscaled values easier might make sense.

If i interpret the comment correctly, the following would be a demo (which i was not aware of and it's cool!):

Updated Demo Code (partial)

# Demo -> Attempt of demonstrating the objective-scaling suggestionmodel.Add(salary_var >= 30000)model.Add(salary_var <= 40000)model.Minimize(salary_var)model.Proto().objective.scaling_factor = 0.001   # DEFINE INVERSE SCALINGsolver = cp_model.CpSolver()solver.parameters.log_search_progress = True     # SCALED BACK OBJECTIVE PROGRESSstatus = solver.Solve(model)print(list(map(lambda x: solver.Value(x), [tax_component_a, tax_component_b, tax_component_c, tax_full_scaled])))print(solver.ObjectiveValue())                   # SCALED BACK OBJECTIVE

Output (excerpt)

......#1       0.00s best:30    next:[30,29.999]  fixed_bools:0/1#Done    0.00s  CpSolverResponse summary:status: OPTIMALobjective: 30best_bound: 30booleans: 1conflicts: 0branches: 1propagations: 0integer_propagations: 2restarts: 1lp_iterations: 0walltime: 0.0039022usertime: 0.0039023deterministic_time: 8e-08primal_integral: 1.91832e-07[15625, 4289, 0, 300545]30.0

Viewing latest article 1
Browse Latest Browse All 2

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>