diff --git a/src/Informedica.GenOrder.Lib/OrderVariable.fs b/src/Informedica.GenOrder.Lib/OrderVariable.fs index dc99323..49b1b85 100644 --- a/src/Informedica.GenOrder.Lib/OrderVariable.fs +++ b/src/Informedica.GenOrder.Lib/OrderVariable.fs @@ -129,7 +129,7 @@ module OrderVariable = else vr |> ValueRange.setOptVs ovar.Constraints.Values - |> Variable.setValueRange true ovar.Variable + |> Variable.setValueRange ovar.Variable } diff --git a/src/Informedica.GenSolver.Lib/Api.fs b/src/Informedica.GenSolver.Lib/Api.fs index acb88b2..4f2ff93 100644 --- a/src/Informedica.GenSolver.Lib/Api.fs +++ b/src/Informedica.GenSolver.Lib/Api.fs @@ -16,7 +16,11 @@ module Api = module Name = Variable.Name - /// Initialize the solver returning a set of equations + /// + /// Create a list of Equations from a list of strings + /// + /// List of strings + /// List of Equations let init eqs = let notEmpty = String.IsNullOrWhiteSpace >> not let prodEqs, sumEqs = eqs |> List.partition (String.contains "*") @@ -34,7 +38,17 @@ module Api = (parse prodEqs '*' |> createProdEqs) @ (parse sumEqs '+' |> createSumEqs) - let setVariableValues onlyMinIncrMax n p eqs = + /// + /// Apply a variable property to a list of equations + /// + /// Name of the variable + /// Property of the variable + /// List of equations + /// The variable option where the property is applied to + /// + /// The combination of n and p is equal to a constraint + /// + let setVariableValues n p eqs = eqs |> List.collect (Equation.findName n) |> function @@ -42,23 +56,27 @@ module Api = | var::_ -> p |> Property.toValueRange - |> Variable.setValueRange onlyMinIncrMax var + |> Variable.setValueRange var |> Some + /// Solve an `Equations` list let solveAll = Solver.solveAll + /// /// Solve an `Equations` list with - /// - /// * f: function used to process string message - /// * n: the name of the variable to be updated - /// * p: the property of the variable to be updated - /// * vs: the values to update the property of the variable - /// * eqs: the list of equations to solve + /// + /// True if only min, incr and max values are to be used + /// The algorithm to sort the equations + /// The logger to log operations + /// Name of the variable to be updated + /// Property of the variable to be updated + /// List of equations to solve + /// A result type of the solved equations let solve onlyMinIncrMax sortQue log n p eqs = eqs - |> setVariableValues onlyMinIncrMax n p + |> setVariableValues n p |> function | None -> eqs |> Ok | Some var -> @@ -66,16 +84,24 @@ module Api = |> Solver.solveVariable onlyMinIncrMax log sortQue var - /// Make a list of `EQD` - /// to contain only positive - /// values as solutions + /// + /// Set all variables in a list of equations to a non zero or negative value + /// + /// The list of Equations let nonZeroNegative eqs = eqs |> List.map Equation.nonZeroOrNegative - let applyConstraints onlyMinIncrMax log eqs cs = - let apply = Constraint.apply onlyMinIncrMax log + /// + /// Apply a list of constraints to a list of equations + /// + /// The logger to log operations + /// A list of Equations + /// A list of constraints + /// A list of Equations + let applyConstraints log eqs cs = + let apply = Constraint.apply log cs |> List.fold (fun acc c -> @@ -87,8 +113,16 @@ module Api = ) eqs + /// + /// Solve a list of equations using a list of constraints + /// + /// True if only min, incr and max values are to be used + /// The logger to log operations + /// A list of Equations + /// A list of constraints + /// A list of Equations let solveConstraints onlyMinIncrMax log cs eqs = cs |> Constraint.orderConstraints log - |> applyConstraints false log eqs + |> applyConstraints log eqs |> Solver.solveAll onlyMinIncrMax log diff --git a/src/Informedica.GenSolver.Lib/Constraint.fs b/src/Informedica.GenSolver.Lib/Constraint.fs index 06f64eb..9fcc1f9 100644 --- a/src/Informedica.GenSolver.Lib/Constraint.fs +++ b/src/Informedica.GenSolver.Lib/Constraint.fs @@ -12,12 +12,23 @@ module Constraint = module Name = Variable.Name + /// + /// Check whether constraint c1 has the same name as constraint c2. + /// let eqsName (c1 : Constraint) (c2 : Constraint) = c1.Name = c2.Name + /// + /// Print the constraint as a string + /// let toString { Name = n; Property = p } = $"{n |> Name.toString}: {p}" + /// + /// Give a constraint a score based on the property. + /// Low scores should be solved first. + /// + /// The constraint let scoreConstraint c = match c.Property with | ValsProp vs -> @@ -29,6 +40,11 @@ module Constraint = | _ -> -2, c + /// + /// Order constraints based on their score. + /// + /// The logger to log operations + /// The list of constraints let orderConstraints log cs = cs // calc min and max from valsprop constraints @@ -63,7 +79,15 @@ module Constraint = |> List.map snd - let apply onlyMinIncrMax log (c : Constraint) eqs = + /// + /// Apply a constraint to the matching variables + /// in the list of equations. + /// + /// The logger + /// The constraint + /// The list of Equations + /// The variable the constraint is applied to + let apply log (c : Constraint) eqs = eqs |> List.collect (Equation.findName c.Name) @@ -76,7 +100,7 @@ module Constraint = | vr::_ -> c.Property |> Property.toValueRange - |> Variable.setValueRange onlyMinIncrMax vr + |> Variable.setValueRange vr |> fun var -> c |> Events.ConstraintApplied @@ -85,8 +109,17 @@ module Constraint = var + /// + /// Apply a constraint to the matching variables and solve the + /// list of equations. + /// + /// Whether only min incr max should be calculated + /// The logger + /// The algorithm to sort the equations + /// The constraint + /// The list of Equations let solve onlyMinIncrMax log sortQue (c : Constraint) eqs = - let var = apply onlyMinIncrMax log c eqs + let var = apply log c eqs eqs |> Solver.solveVariable onlyMinIncrMax log sortQue var diff --git a/src/Informedica.GenSolver.Lib/Equation.fs b/src/Informedica.GenSolver.Lib/Equation.fs index b7a3e22..a156b49 100644 --- a/src/Informedica.GenSolver.Lib/Equation.fs +++ b/src/Informedica.GenSolver.Lib/Equation.fs @@ -336,8 +336,6 @@ module Equation = | y::xs -> y |> op2 <| (xs |> List.reduce op1) |> Some - // select the right application operator - let (<==) = if onlyMinIncrMax then (@<-) else (^<-) // perform the calculations on the vars let calcVars op1 op2 vars = vars @@ -365,7 +363,7 @@ module Equation = None | Some var -> - let yNew = y <== var + let yNew = y @<- var if yNew <> y then // log finishing the calculation diff --git a/src/Informedica.GenSolver.Lib/Solver.fs b/src/Informedica.GenSolver.Lib/Solver.fs index 81b37d6..fb17f3c 100644 --- a/src/Informedica.GenSolver.Lib/Solver.fs +++ b/src/Informedica.GenSolver.Lib/Solver.fs @@ -78,17 +78,27 @@ module Solver = , rst - let sortQue onlyMinMax que = - if que |> List.length = 0 then que + /// + /// Sort a list of equations by the number of + /// total values in the equation. + /// + /// Whether only min incr max is calculated + /// The list of Equations + let sortQue onlyMinMax eqs = + if eqs |> List.length = 0 then eqs else - que + eqs |> List.sortBy (Equation.count onlyMinMax) //Equation.countProduct - /// Create the equation solver using a - /// product equation and a sum equation solver - /// and function to determine whether an - /// equation is solved + /// + /// Solve a set of equations. + /// + /// Whether to only use min, incr and max + /// The log function + /// The sort function for the que + /// An option variable to solve for + /// The equations to solve let solve onlyMinIncrMax log sortQue var eqs = let solveE n eqs eq = @@ -222,7 +232,14 @@ module Solver = Error (eqs, m) - //TODO: need to clean up the number check + /// + /// Solve a set of equations for a specific variable. + /// + /// Whether to only use min, incr and max + /// The log function + /// The sort function for the que + /// The variable to solve for + /// The equations to solve let solveVariable onlyMinIncrMax log sortQue vr eqs = let n1 = eqs |> List.length let solve = @@ -231,12 +248,18 @@ module Solver = match solve eqs with | Error (eqs, errs) -> Error (eqs, errs) | Ok eqs -> + //TODO: need to clean up the number check let n2 = eqs |> List.length if n2 <> n1 then failwith $"not the same number of eqs, was: {n1}, now {n2}" else Ok eqs - //TODO: need to clean up the number check + /// + /// Solve a set of equations for all variables. + /// + /// Whether to only use min, incr and max + /// The log function + /// The equations to solve let solveAll onlyMinIncrMax log eqs = let n1 = eqs |> List.length let solve = @@ -245,6 +268,7 @@ module Solver = match solve eqs with | Error (eqs, errs) -> Error (eqs, errs) | Ok eqs -> + //TODO: need to clean up the number check let n2 = eqs |> List.length if n2 <> n1 then failwith $"not the same number of eqs, was: {n1}, now {n2}" else Ok eqs diff --git a/src/Informedica.GenSolver.Lib/Types.fs b/src/Informedica.GenSolver.Lib/Types.fs index 36aa15a..d617829 100644 --- a/src/Informedica.GenSolver.Lib/Types.fs +++ b/src/Informedica.GenSolver.Lib/Types.fs @@ -124,8 +124,8 @@ module rec Types = /// /// Represents a constraint on a `Variable`. - /// I.e. either a set of values, or an increment - /// or a minimum of maximum. + /// I.e. either a set of values, or an increment, + /// minimum or maximum. /// type Constraint = { diff --git a/src/Informedica.GenSolver.Lib/Variable.fs b/src/Informedica.GenSolver.Lib/Variable.fs index 3203524..fd74810 100644 --- a/src/Informedica.GenSolver.Lib/Variable.fs +++ b/src/Informedica.GenSolver.Lib/Variable.fs @@ -2989,8 +2989,7 @@ module Variable = /// /// let applyExpr y expr = - let appl _ get set vr = - //printfn $"{s}" + let appl get set vr = match expr |> get with | Some m -> vr |> set m | None -> vr @@ -3000,9 +2999,9 @@ module Variable = | ValSet vs -> y |> setValueSet vs | _ -> y - |> appl "incr" getIncr setIncr - |> appl "min" getMin setMin - |> appl "max" getMax setMax + |> appl getIncr setIncr + |> appl getMin setMin + |> appl getMax setMax module Operators = @@ -3015,8 +3014,6 @@ module Variable = let inline (^-) vr1 vr2 = calc false (-) (vr1, vr2) - let inline (^<-) vr1 vr2 = applyExpr vr1 vr2 - let inline (@*) vr1 vr2 = calc true (*) (vr1, vr2) @@ -3093,14 +3090,11 @@ module Variable = /// Apply a `ValueRange` **vr** to /// `Variable` **v**. - let setValueRange onlyMinIncrMax var vr = - let op = - if onlyMinIncrMax then (@<-) else (^<-) - + let setValueRange var vr = try { var with Values = - (var |> get).Values |> op <| vr + (var |> get).Values @<- vr } with @@ -3239,8 +3233,6 @@ module Variable = let inline (^-) vr1 vr2 = calc (^-) (vr1, vr2) - let inline (^<-) vr1 vr2 = vr2 |> getValueRange |> setValueRange false vr1 - let inline (@*) vr1 vr2 = calc (@*) (vr1, vr2) @@ -3250,7 +3242,7 @@ module Variable = let inline (@-) vr1 vr2 = calc (@-) (vr1, vr2) - let inline (@<-) vr1 vr2 = vr2 |> getValueRange |> setValueRange true vr1 + let inline (@<-) vr1 vr2 = vr2 |> getValueRange |> setValueRange vr1 /// Constant 1 diff --git a/tests/Informedica.GenSolver.Tests/Tests.fs b/tests/Informedica.GenSolver.Tests/Tests.fs index 1ebc250..3a37cf9 100644 --- a/tests/Informedica.GenSolver.Tests/Tests.fs +++ b/tests/Informedica.GenSolver.Tests/Tests.fs @@ -128,7 +128,7 @@ module TestSolver = let setProp n p eqs = let n = n |> Name.createExc - match eqs |> Api.setVariableValues true n p with + match eqs |> Api.setVariableValues n p with | Some var -> eqs |> List.map (fun e ->