I made the following test (extracted from a larger app) which does not not result in the optimal solution (the binary is 0, while 1 would deliver the optimal result).
// absolute value handling method for constraint |d| >= C:
// introduce binary B and large constant N (> maximal d + C)
// (a) d:min -> d >= C - NB
// (b) d:max -> d <= -C + N(1-B)
// (c) d can be negative, so must be set to 'unrestricted'
// absolute value handling method for optimizing b: introduce b+ and b-
// (a): replace b by b+ - b-;
// (b): replace |b| by b+ + b-;
// (c): (implicit) -> b+ >= 0;
// (d): (implicit) -> b- >= 0
let result = solver.Solve(model);
console.log(JSON.stringify(result));
// result:
// {"feasible":true,"result":420,"bounded":true,"isIntegral":true,"b2_neg":120,
// "I1":400,"b1_pos":300,"I2":180,"d12":220}
// so |b1| + |b2| = 300 + 120 = 420
// this is because bin = 0
// expected result:
// {"feasible":true,"result":20,"bounded":true,"isIntegral":true,
// "I1":80,"d12":220,"I2":300,"b1_neg":20 ,"bin":1}
// so |b1| + |b2| = 20 + 0 = 20 which is OK!
The method descibed in https://optimization.cbe.cornell.edu/index.php?title=Optimization_with_absolute_values uses a binary (and Mixed-Integer Linear Programming) to resolve the non convexity issue.
I made the following test (extracted from a larger app) which does not not result in the optimal solution (the binary is 0, while 1 would deliver the optimal result).
Help is appreciated. Rob
--------------------------- %< ----------------------------------
// absolute value handling method for constraint |d| >= C: // introduce binary B and large constant N (> maximal d + C) // (a) d:min -> d >= C - NB // (b) d:max -> d <= -C + N(1-B) // (c) d can be negative, so must be set to 'unrestricted'
// absolute value handling method for optimizing b: introduce b+ and b- // (a): replace b by b+ - b-; // (b): replace |b| by b+ + b-; // (c): (implicit) -> b+ >= 0; // (d): (implicit) -> b- >= 0
let W = 600; let p1 = W/3; let p2 = W/3*2;
let w1 = 200; let w2 = 200;
let sep = 20;
let N = W * 2; let mind12 = (w1 + w2)/2+sep;
let model = { variables: { 'I1': { 'I1:max': 1, 'd12:def': -1, 'b1_def': -1, }, 'I2': { 'I2:max': 1, 'd12:def': 1, 'b2_def': -1, }, 'd12': { 'd12:def': 1, 'd12:min': 1, 'd12:max': 1, }, 'bin': { 'd12:min': N, 'd12:max': N, }, 'b1_pos': { 'b1_def': 1, 'bndOpt': 1, }, 'b1_neg': { 'b1_def': -1, 'bndOpt': 1, }, 'b2_pos': { 'b2_def': 1, 'bndOpt': 1, }, 'b2_neg': { 'b2_def': -1, 'bndOpt': 1, },
}, constraints: { 'I1:max': { max: W - w1 }, 'I2:max': { max: W - w2 }, 'd12:def': { equal: (w1 - w2)/2 }, 'd12:min': { min: mind12 }, 'd12:max': { max: N-mind12 }, 'b1_def': { equal: w1/2 - p1 }, 'b2_def': { equal: w2/2 - p2 }, }, binaries: { 'bin': 1 }, unrestricted: { 'd12': 1, }, optimize: 'bndOpt', opType: 'min', };
let result = solver.Solve(model); console.log(JSON.stringify(result));
// result: // {"feasible":true,"result":420,"bounded":true,"isIntegral":true,"b2_neg":120, // "I1":400,"b1_pos":300,"I2":180,"d12":220} // so |b1| + |b2| = 300 + 120 = 420 // this is because bin = 0
// expected result: // {"feasible":true,"result":20,"bounded":true,"isIntegral":true, // "I1":80,"d12":220,"I2":300,"b1_neg":20 ,"bin":1} // so |b1| + |b2| = 20 + 0 = 20 which is OK!