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.
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 yourmodel.AddGoal()
statement is thatGoalFunction
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:Hopefully this could lead you in the direction in formulating your goal function.