I understand the "trick" to use another series of variables together
with `AddMultiplicationEquality` to define the squares, but I also
need a "trick" to manage the square root (reason is: my objective is
actually a composite of multiple objectives, and the scaling is
important). I tried this:
n = 2
x_min = [ 1]*n
x_max = [10]*n
model = cp_model.CpModel()
x = {i:model.NewIntVar(x_min[i], x_max[i], f"x_{i}") for i in range(n)}
x_sq = {i:model.NewIntVar(x_min[i]**2, x_max[i]**2, f"x_sq_{i}") for i in range(n)}
for i in range(n):
model.AddMultiplicationEquality(x_sq[i], (x[i], x[i]))
mse = model.NewIntVar(np.mean(np.square(x_min), dtype=int), np.mean(np.square(x_max), dtype=int), f"mse")
model.AddDivisionEquality(mse, sum(x_sq), n)
rmse = model.NewIntVar(int(np.floor(np.sqrt(np.mean(np.square(x_min))))), int(np.ceil(np.sqrt(np.mean(np.square(x_max))))), f"rmse")
model.AddMultiplicationEquality(mse, (rmse, rmse))
model.Minimize(rmse)
solver = cp_model.CpSolver()
status = solver.solve(model)
print(f"Status = {solver.status_name()}")
print(f"Objective = {solver.objective_value}")
print("")
for i in range(n):
print(f"x_{i} = {solver.Value(x[i])}")
`.
I guess it makes sense, since in general there is no "integer"
square root to an integer… So I tried this:
n = 2
x_min = [ 1]*n
x_max = [10]*n
model = cp_model.CpModel()
x = {i:model.NewIntVar(x_min[i], x_max[i], f"x_{i}") for i in range(n)}
x_sq = {i:model.NewIntVar(x_min[i]**2, x_max[i]**2, f"x_sq_{i}") for i in range(n)}
for i in range(n):
model.AddMultiplicationEquality(x_sq[i], (x[i], x[i]))
mse = model.NewIntVar(np.mean(np.square(x_min), dtype=int), np.mean(np.square(x_max), dtype=int), f"mse")
model.AddDivisionEquality(mse, sum(x_sq), n)
rmse = model.NewIntVar(int(np.floor(np.sqrt(np.mean(np.square(x_min))))), int(np.ceil(np.sqrt(np.mean(np.square(x_max))))), f"rmse")
rmse_sq = model.NewIntVar(np.mean(np.square(x_min), dtype=int), np.mean(np.square(x_max), dtype=int), f"rmse_sq")
model.AddMultiplicationEquality(rmse_sq, (rmse, rmse))
rmse_diff = model.NewIntVar(-int(np.ceil(np.sqrt(np.mean(np.square(x_max))))), +int(np.ceil(np.sqrt(np.mean(np.square(x_max))))), f"rmse_diff")
model.Add(rmse_diff == mse - rmse_sq)
rmse_err = model.NewIntVar(0, int(np.ceil(np.sqrt(np.mean(np.square(x_max))))), f"rmse_err")
model.AddMultiplicationEquality(rmse_err, (rmse_diff, rmse_diff))
model.Minimize(rmse + rmse_err)
solver = cp_model.CpSolver()
status = solver.solve(model)
print(f"Status = {solver.status_name()}")
print(f"Objective = {solver.objective_value}")
print("")
for i in range(n):
print(f"x_{i} = {solver.Value(x[i])}")