1

It's my first time with Solver Foundation, and I don't understand how to specify goal function. Problem I'm trying to solve using Solver is finding optimal point on 2D surface basing on goal function. As input data, I have 3 point on this surface and difference between times that sound wave needs to get from source (optimal point) to those three points. This time difference leads to distance difference.

And here is my code:

var solver = SolverContext.GetContext();
var model = solver.CreateModel();

decisionX = new Decision( Domain.Real, "X" );
decisionY = new Decision( Domain.Real, "Y" );

model.AddDecision( decisionX );
model.AddDecision( decisionY );

model.AddGoal( "Goal", GoalKind.Minimize, GoalFunction() );

var solution = solver.Solve();
Console.WriteLine("X " + decisionX.GetDouble());
Console.WriteLine("Y " + decisionY.GetDouble());

GoalFunction() is defined as follows:

double GoalFunction() {
    Location X = new Location( decisionX.ToDouble(), decisionY.ToDouble() );
    var rA = A.Location.Distance( X );
    var rB = B.Location.Distance( X );
    var rC = C.Location.Distance( X );

    rA = (Distance)( rA - dsA );
    rB = (Distance)( rB - dsA );
    rC = (Distance)( rC - dsA );

    return ( rA * rA + rB * rB + rC * rC ) / 3;
}

The code above throws exception (decisionX.ToDouble()), because decisions are not initialized at this point.

Anybody can help me rewrite it ?


I have rewritten my GoalFunction to all-Model.methods-calls.

var solver = SolverContext.GetContext();
var model = solver.CreateModel();

decisionX = new Decision( Domain.Real, "X" );
decisionY = new Decision( Domain.Real, "Y" );

model.AddDecision( decisionX );
model.AddDecision( decisionY );

var rA = Model.Difference(
    Model.Sqrt(
        Model.Sum(
            Model.Power( Model.Difference( decisionX, A.Location.X ), 2 ),
            Model.Power( Model.Difference( decisionY, A.Location.Y ), 2 )
        )
    ),
    dsA.Value
);
var rB = Model.Difference(
    Model.Sqrt(
        Model.Sum(
            Model.Power( Model.Difference( decisionX, B.Location.X ), 2 ),
            Model.Power( Model.Difference( decisionY, B.Location.Y ), 2 )
        )
    ),
    dsB.Value
);
var rC = Model.Difference(
    Model.Sqrt(
        Model.Sum(
            Model.Power( Model.Difference( decisionX, C.Location.X ), 2 ),
            Model.Power( Model.Difference( decisionY, C.Location.Y ), 2 )
        )
    ),
    dsC.Value
);
var miner = Model.Min( rA, rB, rC );
rA = Model.Difference( rA, miner );
rB = Model.Difference( rB, miner );
rC = Model.Difference( rC, miner );
var goal = Model.Sum(
    Model.Power( rA, 2 ),
    Model.Power( rB, 2 ),
    Model.Power( rC, 2 )
);
model.AddGoal( "Goal", GoalKind.Minimize, goal );

var solution = solver.Solve();
var q = solution.Quality;
double x = decisionX.GetDouble();
double y = decisionY.GetDouble();

solution.GetNext();
x = decisionX.GetDouble();
y = decisionY.GetDouble();

This code works, but returns {0.0} as a LocalOptimal solution, while optimal is {2,2} (I checked, GoalFunction returns 0 for {2,2} and much higher value for {0,0}. Probably {0,0} is starting point when decisions are Domain.Real.

Solution.GetNext() changes nothing.


decisionX = new Decision( Domain.RealRange( -10, 10 ), "X" );
decisionY = new Decision( Domain.RealRange( -10, 10 ), "Y" );

if I limit the Domain, solution returned is {1.9999999984154413,1.9999999990963979} so it's correct.

but why the solver doesn't start for full real domain ? still dunno

maybe someone will answer someday ... I hope, but I'm marking the answer below as correct.

Soul Reaver
  • 2,012
  • 3
  • 37
  • 52
  • Is Distance a scalar or vector type? Are you positive that A, B and C are defined when creating the model? In `GoalFunction` you are subtracting _rA_, _rB_ and _rC_ with _dsA_, whereas in your rewritten code you are subtracting with _dsA_, _dsB_ and _dsC_, respectively. Is this simply a typo? – Anders Gustafsson May 25 '12 at 11:21
  • I have now applied the _GoalFunction_ as you have rewritten it, but replaced A.Location.X, ..., dsA, ... with compile-time double constants. When I execute, I get a reasonable answer. If you have not already done so, please ensure that A, B, C, dsA, dsB and dsC are defined before you define rA, rB and rC. – Anders Gustafsson May 25 '12 at 16:25

1 Answers1

1

I am no expert on MSF myself, but as far as I can tell your model.AddGoal() statement is incorrect. According to the documentation the third argument should be a Term. Term has an implicit cast operator from double to Term so the only thing that happens in your model.AddGoal() statement is that GoalFunction is invoked once (and throws exception because decisions are not initialized at first).

In the MSF samples there are some examples of how goals can be created.

UPDATE

Based on these samples I have created a simple goal (Rosenbrock banana function) and incorporated this goal in the AddGoal call instead, like this:

    var goal = Model.Sum(Model.Power(1.0 - decisionX, 2.0),
                         Model.Product(100.0, Model.Power(decisionY - Model.Power(decisionX, 2.0), 2.0)));
    model.AddGoal( "Goal", GoalKind.Minimize, goal);

Hopefully this could lead you in the direction in formulating your goal function.

Anders Gustafsson
  • 15,837
  • 8
  • 56
  • 114
  • I've seen this example but im not sure if I can close this function inside one expression ... anyway, I'll try. – Soul Reaver May 25 '12 at 10:02
  • I have updated my answer with an approach that I hope will work. No guarantees, but I'll give it a try myself right away :-) – Anders Gustafsson May 25 '12 at 10:22
  • This results in the same exception - decisions are not initialized. I've turned my function into Model.Method() calls, so i have Term in the end, but after Solve i get Infinities ;) so something is still wrong – Soul Reaver May 25 '12 at 10:34
  • Yes, tried it myself and came to the same conclusion, sorry for leading you in the wrong direction. But hopefully you'll eventually manage to get rid of the infinities in the `Model.Method()` calls in the end. – Anders Gustafsson May 25 '12 at 10:41
  • Ok, to me rewriting to Model.method calls is the answer here, so I'm marking your answer as best answer ... well it's the only answer anyway ^_^ Thanks. Unfortunately, I still can't get optimal answer - {0,0} instead of optimal {2,2}. – Soul Reaver May 29 '12 at 05:24
  • I get optimal values but with limited domain – Soul Reaver May 29 '12 at 05:47
  • I can't manage to see which solver class that is instantiated when trying to solve the above model, perhaps SolverContext selects an insufficient solver for your problem when it is unbounded? Maybe it is worthwhile to explicitly apply specific solvers to see which one solves your model best? – Anders Gustafsson May 29 '12 at 07:01