diff --git a/.gitignore b/.gitignore
index a35e434c31..496b9a2d5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,3 +77,7 @@ cache/
tools/netstandard/CreateSigningKey/bin
tools/netstandard/CreateSigningKey/obj
+
+ortools/fsharp/**/bin
+ortools/fsharp/**/obj
+ortools/fsharp/**/packages
\ No newline at end of file
diff --git a/ortools/fsharp/src/Google.sln b/ortools/fsharp/src/Google.sln
new file mode 100644
index 0000000000..77de237283
--- /dev/null
+++ b/ortools/fsharp/src/Google.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "OrTools.FSharp", "OrTools.FSharp\OrTools.FSharp.fsproj", "{B7DC098E-1334-47D2-88D4-7DA1EF8A4076}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Debug|x64.ActiveCfg = Debug|x64
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Debug|x64.Build.0 = Debug|x64
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Debug|x86.ActiveCfg = Debug|x86
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Debug|x86.Build.0 = Debug|x86
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Release|x64.ActiveCfg = Release|x64
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Release|x64.Build.0 = Release|x64
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Release|x86.ActiveCfg = Release|x86
+ {B7DC098E-1334-47D2-88D4-7DA1EF8A4076}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+EndGlobal
diff --git a/ortools/fsharp/src/OrTools.FSharp/OrTools.FSharp.fsproj b/ortools/fsharp/src/OrTools.FSharp/OrTools.FSharp.fsproj
new file mode 100644
index 0000000000..7115a45536
--- /dev/null
+++ b/ortools/fsharp/src/OrTools.FSharp/OrTools.FSharp.fsproj
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Library
+ Google.OrTools.FSharp
+ netstandard2.0;net462
+ Google.OrTools.FSharp
+ OrTools.FSharp.nuspec
+
+
+
+
+
+
+
diff --git a/ortools/fsharp/src/OrTools.FSharp/OrTools.FSharp.nuspec b/ortools/fsharp/src/OrTools.FSharp/OrTools.FSharp.nuspec
new file mode 100644
index 0000000000..ac7cb0a140
--- /dev/null
+++ b/ortools/fsharp/src/OrTools.FSharp/OrTools.FSharp.nuspec
@@ -0,0 +1,22 @@
+
+
+
+ Google.OrTools.FSharp
+ VVVV
+ Google
+ https://github.com/google/or-tools/blob/master/LICENSE-2.0.txt
+ https://developers.google.com/optimization
+ F# wrapper for the Operations Research Tools project
+ Copyright 2018 Google, Inc
+ Operations Research Math Linear Constraint Programming F#
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ortools/fsharp/src/OrTools.FSharp/OrTools.fsx b/ortools/fsharp/src/OrTools.FSharp/OrTools.fsx
new file mode 100644
index 0000000000..ffb602e894
--- /dev/null
+++ b/ortools/fsharp/src/OrTools.FSharp/OrTools.fsx
@@ -0,0 +1,359 @@
+module Google.OrTools.FSharp
+
+#I "../../../../bin"
+#r "Google.OrTools.dll"
+
+open System
+open Google.OrTools.Algorithms
+open Google.OrTools.LinearSolver
+
+type Goal =
+ /// Maximize the Objective Function
+ | Maximize
+ /// Minimize the Objective Function
+ | Minimize
+
+/// Linear Programming Algorithm
+type LinearProgramming =
+ /// Coin-Or (Recommended default)
+ | CLP
+ /// GNU Linear Programming Kit
+ | GLPK
+ /// Google Linear Optimization
+ | GLOP
+ /// Gurobi Optimizer
+ | GUROBI
+ /// IBM CPLEX
+ | CPLEX
+ /// Solver Name
+ override this.ToString() =
+ match this with
+ | CLP -> "CLP_LINEAR_PROGRAMMING"
+ | GLPK -> "GLPK_LINEAR_PROGRAMMING"
+ | GLOP -> "GLOP_LINEAR_PROGRAMMING"
+ | GUROBI -> "GUROBI_LINEAR_PROGRAMMING"
+ | CPLEX -> "CPLEX_LINEAR_PROGRAMMING"
+ /// Solver Id
+ member this.Id =
+ match this with
+ | CLP -> 0
+ | GLPK -> 1
+ | GLOP -> 2
+ | GUROBI -> 6
+ | CPLEX -> 10
+
+/// Integer Programming Algoritm
+type IntegerProgramming =
+ /// Solving Constraint Integer Programs (Recommended default)
+ | SCIP
+ /// GNU Linear Programming Kit
+ | GLPK
+ /// Coin-Or Branch and Cut
+ | CBC
+ /// Gurobi Optimizer
+ | GUROBI
+ /// IBM CPLEX
+ | CPLEX
+ /// Binary Optimizer
+ | BOP
+ /// Solver Name
+ override this.ToString() =
+ match this with
+ | SCIP -> "SCIP_MIXED_INTEGER_PROGRAMMING"
+ | GLPK -> "GLPK_MIXED_INTEGER_PROGRAMMING"
+ | CBC -> "CBC_MIXED_INTEGER_PROGRAMMING"
+ | GUROBI -> "GUROBI_MIXED_INTEGER_PROGRAMMING"
+ | CPLEX -> "CPLEX_MIXED_INTEGER_PROGRAMMING"
+ | BOP -> "BOP_INTEGER_PROGRAMMING"
+ /// Solver Id
+ member this.Id =
+ match this with
+ | SCIP -> 3
+ | GLPK -> 4
+ | CBC -> 5
+ | GUROBI -> 7
+ | CPLEX -> 11
+ | BOP -> 12
+
+type LinearSolverAlgorithm =
+ | LP of LinearProgramming
+ | IP of IntegerProgramming
+
+/// Max Flow Solver Result
+type MaximumFlowResult =
+ | Optimal
+ | IntegerOverflow
+ | BadInput
+ | BadResult
+ member this.Id =
+ match this with
+ | Optimal -> 0
+ | IntegerOverflow -> 1
+ | BadInput -> 2
+ | BadResult -> 3
+
+/// Minimum Cost Flow Result
+type MinimumCostFlowResult =
+ | NotSolved
+ | Optimal
+ | Feasible
+ | Infeasible
+ | Unbalanced
+ | BadResult
+ | BadCostRange
+ member this.Id =
+ match this with
+ | NotSolved -> 0
+ | Optimal -> 1
+ | Feasible -> 2
+ | Infeasible -> 3
+ | Unbalanced -> 4
+ | BadResult -> 5
+ | BadCostRange -> 6
+
+
+/// Knapsack Solver Algorithm
+type KnapsackSolverAlgorithm =
+ | BruteForce
+ | SixtyFourItems
+ | DynamicProgramming
+ | MultidimensionCBC
+ | MultidimensionGLPK
+ | MultidimensionBranchAndBound
+ | MultidimensionSCIP
+ /// Solver Id
+ member this.Id =
+ match this with
+ | BruteForce -> 0
+ | SixtyFourItems -> 1
+ | DynamicProgramming -> 2
+ | MultidimensionCBC -> 3
+ | MultidimensionGLPK -> 4
+ | MultidimensionBranchAndBound -> 5
+ | MultidimensionSCIP -> 6
+
+let knapsackSolve (name: string) (solverAlgorithm:KnapsackSolverAlgorithm) (profits:int64 list) (weights:int64 list) (capacities:int64 list) =
+ // extract the specific algorithm so its Id can be used to create solver
+ let algorithm =
+ match solverAlgorithm with
+ | MultidimensionBranchAndBound ->
+ MultidimensionBranchAndBound.Id
+ | BruteForce ->
+ BruteForce.Id
+ | SixtyFourItems ->
+ SixtyFourItems.Id
+ | DynamicProgramming ->
+ DynamicProgramming.Id
+ | MultidimensionCBC ->
+ MultidimensionCBC.Id
+ | MultidimensionGLPK ->
+ MultidimensionGLPK.Id
+ | MultidimensionSCIP ->
+ MultidimensionSCIP.Id
+
+ let solver = new KnapsackSolver(algorithm, name)
+
+ // transform lists to compatible structures for C++ Solver
+ let profits = new KInt64Vector( List.toArray profits )
+
+ let weights =
+ let tempVector = new KInt64VectorVector(1)
+ let tempWeights = new KInt64Vector(List.toArray weights)
+ tempVector.Add(tempWeights)
+ tempVector
+
+ let capacities = new KInt64Vector (List.toArray capacities)
+
+ solver.Init(profits, weights, capacities)
+ solver
+
+type SolverOpts = {
+ /// Name of the solver
+ SolverName: string;
+ /// Linear objective function vector
+ ObjectiveFunction: float list;
+ /// Matrix for linear inequality constraints
+ ConstraintMatrix: float list list option;
+ /// Upper Bound vector for linear inequality constraints
+ ConstraintVectorUpperBound: float list option;
+ /// Lower Bound vector for linear inequality constraints
+ ConstraintVectorLowerBound: float list option;
+ /// Vector of variable upper bounds
+ VariableUpperBound: float list;
+ /// Vector of variable lower bounds
+ VariableLowerBound: float list;
+ /// Matrix for linear equality constraints
+ ConstraintMatrixEq: float list list option;
+ /// Vector for linear equality constraints
+ ConstraintVectorEq: float list option;
+ /// Solver Algorithm to use
+ SolverAlgorithm: LinearSolverAlgorithm;
+ /// Solver Goal (Minimize/Maximize)
+ SolverGoal: Goal
+} with
+ member this.Name(nm:string)=
+ {this with SolverName = nm}
+ member this.Objective(objVector:float list) =
+ {this with ObjectiveFunction = objVector}
+ member this.Matrix(mat:float list list) =
+ {this with ConstraintMatrix = Some(mat)}
+ member this.VectorUpperBound(vec:float list) =
+ {this with ConstraintVectorUpperBound = Some(vec)}
+ member this.VectorLowerBound(vec:float list) =
+ {this with ConstraintVectorLowerBound = Some(vec)}
+ member this.MatrixEq(mat:float list list) =
+ {this with ConstraintMatrixEq = Some(mat)}
+ member this.VectorEq(vec:float list) =
+ {this with ConstraintVectorEq = Some(vec)}
+ member this.VarUpperBound(ub:float list) =
+ {this with VariableUpperBound = ub}
+ member this.VarLowerBound(lb:float list) =
+ {this with VariableLowerBound = lb}
+ member this.Algorithm(algo:LinearSolverAlgorithm) =
+ {this with SolverAlgorithm = algo}
+ member this.Goal(goal:Goal) =
+ {this with SolverGoal = goal}
+ static member Default =
+ {
+ SolverName = "Solver";
+ ObjectiveFunction = [];
+ ConstraintMatrix = None;
+ ConstraintVectorUpperBound = None;
+ ConstraintVectorLowerBound = None;
+ VariableUpperBound = [];
+ VariableLowerBound = [];
+ ConstraintMatrixEq = None;
+ ConstraintVectorEq = None;
+ SolverAlgorithm = LP CLP
+ SolverGoal = Maximize
+ }
+
+let lpSolve (solverOptions:SolverOpts) =
+ // extract the specific algorithm so its Id can be used to create solver
+ let algorithm =
+ match solverOptions.SolverAlgorithm with
+ | LP lp -> lp.Id
+ | IP ip -> ip.Id
+
+ let solver = new Solver(solverOptions.SolverName, algorithm)
+
+ // Detect errors on required parameters
+ if (solverOptions.ConstraintMatrix.IsNone && solverOptions.ConstraintVectorUpperBound.IsNone && solverOptions.ConstraintVectorLowerBound.IsNone)
+ && (solverOptions.ConstraintMatrixEq.IsNone && solverOptions.ConstraintVectorEq.IsNone) then
+ failwith "Must provide at least one Matrix/Vector pair for inequality/equality contraints"
+
+ if solverOptions.ObjectiveFunction.IsEmpty then
+ failwith "Objective function cannot be empty"
+
+ if solverOptions.VariableUpperBound.IsEmpty then
+ failwith "Variable upper bound values cannot be empty"
+
+ if solverOptions.VariableLowerBound.IsEmpty then
+ failwith "Variable lower bound values cannot be empty"
+
+ // check inequality matrix dimensions against bounds
+ match (solverOptions.ConstraintMatrix, solverOptions.ConstraintVectorLowerBound, solverOptions.ConstraintVectorUpperBound) with
+ | _, Some lb, Some ub when lb.Length <> ub.Length ->
+ failwithf "Constraint vector dimensions should be equal.\nLower Bound Length: %i\nUpper Bound Length: %i" lb.Length ub.Length
+ | _ -> ()
+
+ // Check Objective function dimensions with variable bounds dimensions
+ match (solverOptions.ObjectiveFunction.Length, solverOptions.VariableLowerBound.Length, solverOptions.VariableUpperBound.Length) with
+ | _ , lb, ub when lb <> ub ->
+ failwithf "Variable vector dimensions should be equal.\nLower Bound Length: %i\nUpper Bound Length: %i" lb ub
+ | obj, _ , ub when obj <> ub ->
+ failwithf "Variable vector dimensions should be equal.\nUpper Bound Length: %i\nObjective Function Length: %i" ub obj
+ | obj, lb, _ when obj <> lb ->
+ failwithf "Variable vector dimensions should be equal.\nLower Bound Length: %i\nObjective Function Length: %i" lb obj
+ | _ -> ()
+
+ // Variables
+ let vars =
+ match solverOptions.SolverAlgorithm with
+ | LP lp ->
+ [ for i in 0 .. (solverOptions.VariableLowerBound.Length-1) -> solver.MakeNumVar(solverOptions.VariableLowerBound.[i], solverOptions.VariableUpperBound.[i], (sprintf "var[%i]" i ) ) ]
+ | IP ip ->
+ [ for i in 0 .. (solverOptions.VariableLowerBound.Length-1) -> solver.MakeIntVar(solverOptions.VariableLowerBound.[i], solverOptions.VariableUpperBound.[i], (sprintf "var[%i]" i ) ) ]
+
+ // Constraints
+ let cols = [ for i in 0 .. (solverOptions.VariableLowerBound.Length-1) -> i ] // generate column index selectors
+
+ // Inequality Constraints
+ match (solverOptions.ConstraintMatrix, solverOptions.ConstraintVectorLowerBound, solverOptions.ConstraintVectorUpperBound) with
+ | (None, Some lb, Some ub) ->
+ failwithf "Matrix for Equality Constraints undefined."
+ | (Some mat, None, Some ub) ->
+ for row = 0 to (ub.Length-1) do
+ // generate constraint operands based on indices
+ let constraintOperands = List.map (fun c -> vars.[c] * mat.[c].[row]) cols
+ let linearExp = List.reduce (+) constraintOperands
+
+ // create the constraint
+ let rangeConstraint = RangeConstraint(linearExp, Double.NegativeInfinity, ub.[row])
+ solver.Add(rangeConstraint) |> ignore
+ | (Some mat, Some lb, None) ->
+ for row = 0 to (lb.Length-1) do
+ // generate constraint operands based on indices
+ let constraintOperands = List.map (fun c -> vars.[c] * mat.[c].[row]) cols
+ let linearExp = List.reduce (+) constraintOperands
+
+ // create the constraint
+ let rangeConstraint = RangeConstraint(linearExp, lb.[row], Double.PositiveInfinity)
+ solver.Add(rangeConstraint) |> ignore
+ | (Some mat, Some lb, Some ub) ->
+ for row = 0 to (ub.Length-1) do
+ // generate constraint operands based on indices
+ let constraintOperands = List.map (fun c -> vars.[c] * mat.[c].[row]) cols
+ let linearExp = List.reduce (+) constraintOperands
+
+ // create the constraint
+ let rangeConstraint = RangeConstraint(linearExp, lb.[row], ub.[row])
+ solver.Add(rangeConstraint) |> ignore
+ | _ -> ()
+
+
+ // Equality Constraints
+ match (solverOptions.ConstraintMatrixEq, solverOptions.ConstraintVectorEq) with
+ | (None, Some b) ->
+ failwithf "Matrix for Equality Constraints undefined."
+ | (Some mat, Some vec) ->
+ for row = 0 to (vec.Length-1) do
+ // generate constraint operands based on indices
+ let constraintOperands = List.map (fun c -> vars.[c] * mat.[c].[row]) cols
+ let linearExp = List.reduce (+) constraintOperands
+
+ // create the constraint
+ let equalityConstraint = RangeConstraint(linearExp, vec.[row], vec.[row])
+ solver.Add(equalityConstraint) |> ignore
+ | _ -> ()
+
+ // Objective
+ let objectiveOperands = List.map (fun c -> solverOptions.ObjectiveFunction.[c] * vars.[c]) cols
+ let objectiveExp = List.reduce (+) objectiveOperands
+
+ match solverOptions.SolverGoal with
+ | Minimize ->
+ solver.Minimize(objectiveExp)
+ | Maximize ->
+ solver.Maximize(objectiveExp)
+
+ solver
+
+/// Solves the optimization problem and prints the results to console
+let SolverSummary (solver:Solver) =
+ let resultStatus = solver.Solve();
+
+ match resultStatus with
+ | status when status <> Solver.OPTIMAL ->
+ printfn "The problem does not have an optimal solution!"
+ exit 0
+ | _ ->
+ printfn "\nProblem solved in %d milliseconds" (solver.WallTime())
+ printfn "Iterations: %i\n" (solver.Iterations())
+
+ printfn "Objective: %f" (solver.Objective().Value())
+ for i in 0 .. (solver.NumVariables()-1) do
+ printfn "%-10s: %f " (sprintf "var[%i]" i) ((solver.LookupVariableOrNull(sprintf "var[%i]" i)).SolutionValue())
+
+ solver
+
diff --git a/ortools/fsharp/src/README.md b/ortools/fsharp/src/README.md
new file mode 100644
index 0000000000..3bfd407ba6
--- /dev/null
+++ b/ortools/fsharp/src/README.md
@@ -0,0 +1 @@
+Referenced this [github issue](https://github.com/morelinq/MoreLINQ/pull/420/files) to fix compilation issue with dotnet-cli and targeting net 4.X
\ No newline at end of file
diff --git a/ortools/fsharp/src/netfx.props b/ortools/fsharp/src/netfx.props
new file mode 100644
index 0000000000..7a0f31765a
--- /dev/null
+++ b/ortools/fsharp/src/netfx.props
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+ true
+
+
+ /Library/Frameworks/Mono.framework/Versions/Current/lib/mono
+ /usr/lib/mono
+ /usr/local/lib/mono
+
+
+ $(BaseFrameworkPathOverrideForMono)/4.5-api
+ $(BaseFrameworkPathOverrideForMono)/4.5.1-api
+ $(BaseFrameworkPathOverrideForMono)/4.5.2-api
+ $(BaseFrameworkPathOverrideForMono)/4.6-api
+ $(BaseFrameworkPathOverrideForMono)/4.6.1-api
+ $(BaseFrameworkPathOverrideForMono)/4.6.2-api
+ $(BaseFrameworkPathOverrideForMono)/4.7-api
+ $(BaseFrameworkPathOverrideForMono)/4.7.1-api
+ true
+
+
+ $(FrameworkPathOverride)/Facades;$(AssemblySearchPaths)
+
+
\ No newline at end of file