|
|
Hi: I was trying to push a solution to a DataTable, but I hit a problem I don't completely understand. Here is the program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.OleDb;
using System.Data.Linq;
using System.Text;
using Microsoft.SolverFoundation.Services;
using System.IO;
namespace OML1
{
class Test
{
/// <summary>
/// Called by the OS
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Test t = new Test();
t.Solve();
}
/// <summary>
/// Holds the OML model
/// </summary>
string strModel = @"Model[
Parameters[Sets,I],
Parameters[Reals,p[I]],
Decisions[Reals[0,Infinity],x[I]],
Constraints[
Foreach[{i,I}, x[i]==p[i]]
]
]";
/// <summary>
/// SFS
/// </summary>
SolverContext context;
/// <summary>
/// Constructor
/// </summary>
public Test()
{
context = SolverContext.GetContext();
}
/// <summary>
/// Solve the problem
/// </summary>
public void Solve()
{
context.LoadModel(FileFormat.OML, new StringReader(strModel));
DataTable dt = new DataTable();
dt.Columns.Add("index", System.Type.GetType("System.String"));
dt.Columns.Add("value", System.Type.GetType("System.Double"));
dt.Rows.Add("a", 1);
dt.Rows.Add("b", 2);
DataTable dt2 = new DataTable();
dt2.Columns.Add("index", System.Type.GetType("System.String"));
dt2.Columns.Add("value", System.Type.GetType("System.Double"));
dt2.Rows.Add("a", -1);
dt2.Rows.Add("b", -1);
Parameter p = context.CurrentModel.Parameters.First(q => q.Name == "p");
p.SetBinding(dt.AsEnumerable(), "value", new string[] { "index" });
Decision x = context.CurrentModel.Decisions.First(d => d.Name == "x");
// next statement gives:
// The property or field 'value' was not found
x.SetBinding(dt2.AsEnumerable(), "value", new string[] { "index" }); // <<<<<<< exception
Solution solution = context.Solve();
Console.Write("{0}", solution.GetReport());
context.PropagateDecisions();
}
}
}
The statement where I try to bind dt2 to x gives an exception with the message:
The property or field 'value' was not found
I have problem interpreting this error, as I really see a column 'value'. Note that the same thing works fine for parameter bindings. Note: Of course it is not a show stopper (I can get the binding to work with a List data structure, so I can update the DataTable myself as a work around). Thanks! ---------------------------------------------------------------- Erwin Kalvelagen Amsterdam Optimization Modeling Group erwin.kalvelagen@gmail.com http://amsterdamoptimization.com ----------------------------------------------------------------
|
|
|
|
Hey Erwin, It looks like you have found a bug. Currently, decision bindings only can be made against properties and fields, but there is no reason why it should not work with DataColumns too. We will open a work item and fix this issue in a future release. Also, we will try to improve our error messages in this area. As you said, you can work around this problem by using Lists. Direct use of LINQ is the most flexible, powerful way to do Solver Foundation data binding. The idea is similar to what I described in this blog post: http://blogs.msdn.com/natbr/archive/2009/05/05/creating-parameterized-solver-foundation-models-using-linq-to-sql.aspx. First create a .dbml file with the DataContext, then use it for data binding. If the DataContext is called "data" then your sample would like something like this. (The full sample is on my blog for now: http://blogs.msdn.com/natbr/archive/2009/06/02/solver-foundation-linq-to-sql-example.aspx
context.LoadModel(FileFormat.OML, new StringReader(strModel));
Parameter p = context.CurrentModel.Parameters.First(q => q.Name == "p");
p.SetBinding(data.P, "value", new string[] { "index" });
Decision x = context.CurrentModel.Decisions.First(d => d.Name == "x");
x.SetBinding(data.X, "value", new string[] { "index" });
Solution solution = context.Solve();
Console.Write("{0}", solution.GetReport());
context.PropagateDecisions();
We will also upload this as a sample soon. Thanks again for the feedback. I'm sorry that you hit this problem. Best, Nathan
|
|
|
|
No problem! Yes, direct LINQ is certainly preferably, as your example shows. I had data available in Access so I tried that through OleDbDataAdapter. I believe LINQ is more geared towards SQL Server than to Access. Thanks again! ---------------------------------------------------------------- Erwin Kalvelagen Amsterdam Optimization Modeling Group erwin.kalvelagen@gmail.com http://amsterdamoptimization.com ----------------------------------------------------------------
|
|
Coordinator
Edited Jun 5 2009 at 4:19 PM
|
|
|
|
I am trying to build a transportation model in SFS and bind it to SQL Express DB. I followed exactly the guidelines in Nathan's post, and got the following error: No coercion operator is defined between types 'System.Nullable`1 System.Single' and 'Microsoft.SolverFoundation.Common.Rational'. Can you please shed some light on this error? Best, John
|
|
|
|
Hi John - I think the issue relates to converting the input data into the data type required by Solver Foundation. Solver Foundation uses the Rational data type to prevent any conversion errors when bringing data into the system. In my example, I was using (non-nullable) double data - relying on the fact that Rational supports implicit conversion from double. In your case, there are two problems: 1) the DB column appears to be nullable, 2) it is single precision rather than double precision. (The C# syntax is "float?".) The Rational data type does not have implicit conversion from nullable types, nor from "float", hence the error. If you are able to transform the DB data into double before binding, then you should be okay. For example, you might be able to tag the LINQ expression ".Cast<double>()" on the end. (Or if that doesn't work, ".Cast<float>()" might. Hope this helps, Nathan
|
|
|
|
Thank you Nathan, It worked! you really helped me, One more simple and annoying thing - when I write (exactly like in your post): Solution solution = context.Solve(new SimplexDirective()); Report report = solution.GetReport(); Console.WriteLine(report); from some reason I get the following errors ('Transport' is my namespace): Error 1 Cannot convert type 'Microsoft.SolverFoundation.Services.Solution' to 'Transport.Solution' Error 2 'Transport.Solution' does not contain a definition for 'GetReport' and no extension method 'GetReport' accepting a first argument of type 'Transport.Solution' could be found (are you missing a using directive or an assembly reference?) What could be the reason for that? John
|
|
|
|
Glad to hear it! There is probably another class called "Solution" somewhere in your project - either one that you wrote or one that was generated by Visual Studio. In either case, you can either write the fully qualified name for the Solver Foundation object: Microsoft.SolverFoundation.Services.Solution solution = context.Solve(new SimplexDirective()); OR you could stick a "using" at the top of your source file: using MSFSolution = Microsoft.SolverFoundation.Services.Solution; //... MSFSolution solution = context.Solve(new SimplexDirective()); OR you could just take advantage of .Net 3.0 stuff and do: var solution = context.Solve(new SimplexDirective()); Any of those three should work - I guess the last one is the easiest. Nathan
|
|
|
|
Thank you Nathan, It was the first thing, I had a table named solution... You are a true savior :-)
|
|