diff --git a/src/Informedica.GenSolver.Lib/Equation.fs b/src/Informedica.GenSolver.Lib/Equation.fs index a08eed0..0c8cc52 100644 --- a/src/Informedica.GenSolver.Lib/Equation.fs +++ b/src/Informedica.GenSolver.Lib/Equation.fs @@ -241,12 +241,13 @@ module Equation = else true - let calculationToString b op1 op2 x y xs = + let calculationToString b op1 op2 y xs = let varToStr = Variable.toString b let opToStr op = $" {op |> Variable.Operators.toString} " - let filter x xs = xs |> List.filter (Variable.eqName x >> not) + let cost = xs |> List.map Variable.count |> List.reduce (*) + let x1 = xs |> List.head - $"""{x |> varToStr} = {y |> varToStr}{op2 |> opToStr}{xs |> filter x |> List.map varToStr |> String.concat (op1 |> opToStr)} """ + $"""{y |> varToStr} = {x1 |> varToStr}{op2 |> opToStr}{xs |> List.map varToStr |> String.concat (op1 |> opToStr)} (cost: {cost})""" /// Solve an equation **e**, return a list of @@ -275,12 +276,13 @@ module Equation = | _ -> if x |> Variable.isSolved then x else + let xs = xs |> without x // log the calculation - (op1, op2, x, y, xs) + (op1, op2, x, y::xs) |> Events.EquationStartCalculation |> Logging.logInfo log // recalculate x - x <== (y |> op2 <| (xs |> without x |> List.reduce op1)) + x <== (y |> op2 <| (xs |> List.reduce op1)) (xChanged || (x.Values |> ValueRange.eqs newX.Values |> not)) |> calcXs op1 op2 y (xs |> replAdd newX) tail @@ -290,7 +292,7 @@ module Equation = y, false else // log the calculation - (op1, op1, y, (xs |> List.head), (xs |> List.tail)) + (op1, op1, y, xs) |> Events.EquationStartCalculation |> Logging.logInfo log // recalculate y diff --git a/src/Informedica.GenSolver.Lib/Scripts/Tests.fsx b/src/Informedica.GenSolver.Lib/Scripts/Tests.fsx index 4781eb3..ff56498 100644 --- a/src/Informedica.GenSolver.Lib/Scripts/Tests.fsx +++ b/src/Informedica.GenSolver.Lib/Scripts/Tests.fsx @@ -714,7 +714,7 @@ module Tests = | Some min', Some max' -> let min = min' |> BigRational.fromInt |> ValueUnit.singleWithUnit Units.Count.times |> Minimum.create minIncl let max = max' |> BigRational.fromInt |> ValueUnit.singleWithUnit Units.Count.times |> Maximum.create maxIncl - min |> ValueRange.minSTEmax max + min |> minSTEmax max let min1Options = [None, false; Some -2, true; Some -2, false; Some 0, true; Some 0, false; Some 2, true; Some 2, false] let max1Options = [None, false; Some -1, true; Some -1, false; Some 0, true; Some 0, false; Some 3, true; Some 3, false] @@ -745,10 +745,10 @@ module Tests = |> List.distinct - let mult = Variable.ValueRange.MinMaxCalculator.multiplication - let div = Variable.ValueRange.MinMaxCalculator.division - let add = Variable.ValueRange.MinMaxCalculator.addition - let sub = Variable.ValueRange.MinMaxCalculator.subtraction + let mult = MinMaxCalculator.multiplication + let div = MinMaxCalculator.division + let add = MinMaxCalculator.addition + let sub = MinMaxCalculator.subtraction let createVuOpt (intOpt, b) = @@ -772,7 +772,7 @@ module Tests = (min2 |> createVuOpt) (max2 |> createVuOpt) |> fun (min, max) -> Variable.ValueRange.create true min None max None - |> Variable.ValueRange.toString true + |> toString true |> String.replace "x" "" |> String.replace "<" "< " |> String.replace ">" " >" @@ -1358,7 +1358,7 @@ module Tests = |> Expect.isTrue "should all be Zero" } - test "when add/, sub is applied to x1 = Zero and x2 = Unrestricted" { + test "when add, sub is applied to x1 = Zero and x2 = Unrestricted" { let x1 = 0N |> ValueUnit.singleWithUnit Units.Count.times |> ValueRange.createValSet let x2 = ValueRange.Unrestricted [ @@ -1732,7 +1732,6 @@ Tests.tests module MinMaxTestScenarios = - open Informedica.Utils.Lib open Informedica.Utils.Lib.BCL open Informedica.GenUnits.Lib open Informedica.GenSolver.Lib diff --git a/src/Informedica.GenSolver.Lib/SolverLogging.fs b/src/Informedica.GenSolver.Lib/SolverLogging.fs index 2b706ed..33296c6 100644 --- a/src/Informedica.GenSolver.Lib/SolverLogging.fs +++ b/src/Informedica.GenSolver.Lib/SolverLogging.fs @@ -124,8 +124,8 @@ module SolverLogging = | EquationStartedSolving eq -> $"=== Start solving Equation ===\n{eq |> toString}" - | EquationStartCalculation (op1, op2, y, x, xs) -> - $"start calculating: {Equation.calculationToString true op1 op2 y x xs}" + | EquationStartCalculation (op1, op2, y, xs) -> + $"start calculating: {Equation.calculationToString true op1 op2 y xs}" | EquationFinishedCalculation (xs, changed) -> if not changed then "No Changes" diff --git a/src/Informedica.GenSolver.Lib/Types.fs b/src/Informedica.GenSolver.Lib/Types.fs index 425a4c0..36aa15a 100644 --- a/src/Informedica.GenSolver.Lib/Types.fs +++ b/src/Informedica.GenSolver.Lib/Types.fs @@ -169,7 +169,6 @@ module rec Types = | EquationStartCalculation of op1: (Variable -> Variable -> Variable) * op2: (Variable -> Variable -> Variable) * - x: Variable * y: Variable * xs: Variable List | EquationFinishedCalculation of Variable list * changed : bool diff --git a/src/Informedica.GenSolver.Lib/Variable.fs b/src/Informedica.GenSolver.Lib/Variable.fs index 74017d8..01f4d1d 100644 --- a/src/Informedica.GenSolver.Lib/Variable.fs +++ b/src/Informedica.GenSolver.Lib/Variable.fs @@ -2747,6 +2747,19 @@ module Variable = /// The infix operator /// The first `ValueRange` /// The second `ValueRange` + /// The resulting `ValueRange` + /// + /// + /// let vs1 = [| 1N; 2N; 3N |] |> ValueUnit.withUnit Units.Count.times |> ValueSet.create + /// let vs2 = [| 4N; 5N; 6N |] |> ValueUnit.withUnit Units.Count.times |> ValueSet.create + /// let vr1 = create true None None None (Some vs1) + /// let vr2 = create true None None None (Some vs2) + /// calc true (*) (vr1, vr2) + /// // returns a ValueRange with a Minimum = 4 and a Maximum = 18 + /// calc false (*) (vr1, vr2) + /// // returns a ValueRange [|4N; 5N; 6N; 8N; 10N; 12N; 15N; 18N|] + /// + /// let calc onlyMinIncrMax op (vr1, vr2) = let calcMinMax min1 max1 min2 max2 = @@ -2837,14 +2850,50 @@ module Variable = | _ -> create onlyMinIncrMax min incr max None + /// /// Checks whether a `ValueRange` vr1 is a subset of /// `ValueRange` vr2. + /// + /// The first `ValueRange` + /// The second `ValueRange` + /// + /// Only checks whether the `ValueSet` of vr1 is a subset of + /// the `ValueSet` of vr2. + /// + /// + /// + /// let vs1 = [| 1N; 2N; 3N |] |> ValueUnit.withUnit Units.Count.times |> ValueSet.create + /// let vs2 = [| 2N; 3N |] |> ValueUnit.withUnit Units.Count.times |> ValueSet.create + /// let vr1 = create true None None None (Some vs1) + /// let vr2 = create true None None None (Some vs2) + /// vr1 |> isSubSetOf vr2 // returns false + /// vr2 |> isSubSetOf vr1 // returns true + /// + /// let isSubSetOf vr2 vr1 = match vr1, vr2 with | ValSet s1, ValSet s2 -> s2 |> ValueSet.isSubsetOf s1 | _ -> false + /// + /// Create a set of `Properties` from a `ValueRange`. + /// + /// + /// + /// + /// let min = 2N |> ValueUnit.singleWithUnit Units.Count.times |> Minimum.create true + /// let incr = 2N |> ValueUnit.singleWithUnit Units.Count.times |> Increment.create + /// let max = 8N |> ValueUnit.singleWithUnit Units.Count.times |> Maximum.create false + /// let vr = create true (Some min) (Some incr) (Some max) None + /// vr |> toProperties + /// // returns + /// // set + /// // [MinProp (MinIncl (ValueUnit ([|2N|], Count (Times 1N)))); + /// // MaxProp (MaxIncl (ValueUnit ([|6N|], Count (Times 1N)))); + /// // IncrProp (Increment (ValueUnit ([|2N|], Count (Times 1N))))] + /// + /// let toProperties vr = let unr = set [] @@ -2877,14 +2926,51 @@ module Variable = |> apply unr nonZero fMin fMax fMinMax fIncr fMinIncr fIncrMax fMinIncrMax fVs + /// + /// Get the difference between two `ValueRange`s. + /// + /// The first ValueRange + /// The second ValueRange + /// The difference between the two ValueRanges + /// + /// + /// let min = 2N |> ValueUnit.singleWithUnit Units.Count.times |> Minimum.create true + /// let incr = 2N |> ValueUnit.singleWithUnit Units.Count.times |> Increment.create + /// let max = 8N |> ValueUnit.singleWithUnit Units.Count.times |> Maximum.create false + /// let vr1 = create true (Some min) (Some incr) None None + /// let vr2 = create true None None (Some max) None + /// vr1 |> diffWith vr2 + /// + /// let diffWith vr1 vr2 = vr1 |> toProperties |> Set.difference (vr2 |> toProperties) + /// /// Set a `ValueRange` expr to a `ValueRange` y. /// So, the result is equal to or more restrictive than the original `y`. + /// + /// Whether only min incr max are calculated + /// The `ValueRange` to apply to + /// The `ValueRange` to apply + /// The resulting (restricted) `ValueRange` + /// + /// + /// let min = 2N |> ValueUnit.singleWithUnit Units.Count.times |> Minimum.create true + /// let incr = 2N |> ValueUnit.singleWithUnit Units.Count.times |> Increment.create + /// let max = 8N |> ValueUnit.singleWithUnit Units.Count.times |> Maximum.create false + /// let vr1 = create true (Some min) (Some incr) None None + /// let vr2 = create true None None (Some max) None + /// vr1 |> applyExpr true vr2 + /// // returns + /// // MinIncrMax + /// // (MinIncl (ValueUnit ([|2N|], Count (Times 1N))), + /// // Increment (ValueUnit ([|2N|], Count (Times 1N))), + /// // MaxIncl (ValueUnit ([|6N|], Count (Times 1N)))) + /// + /// let applyExpr onlyMinIncrMax y expr = let appl _ get set vr = //printfn $"{s}" @@ -2943,6 +3029,7 @@ module Variable = let createSucc = create id + /// A Variable with a Unrestricted `ValueRange` let empty n = Unrestricted |> createSucc n @@ -2962,6 +3049,7 @@ module Variable = let get = apply id + /// Get the string representation of a `Variable`. let toString exact ({ Name = n; Values = vs }: Variable) = vs |> ValueRange.toString exact @@ -2976,6 +3064,8 @@ module Variable = let getValueRange v = (v |> get).Values + /// Check whether a `Variable` **v** contains + /// a value **v**. let contains v vr = vr |> getValueRange |> ValueRange.contains v @@ -3024,6 +3114,8 @@ module Variable = let eqName v1 v2 = v1 |> getName = (v2 |> getName) + /// Check whether the `ValueRange` of **v1** + /// and **v2** are equal. let eqValues var1 var2 = var1 |> getValueRange = (var2 |> getValueRange) @@ -3047,6 +3139,8 @@ module Variable = getValueRange >> ValueRange.isUnrestricted + /// Checks whether the ValueRange of a Variable + /// is a MinIncrMax let isMinIncrMax = getValueRange >> ValueRange.isMinIncrMax @@ -3081,6 +3175,12 @@ module Variable = Values = var.Values |> ValueRange.increaseIncrement lim incr } + /// + /// Calculate a ValueSet for a Variable if the Value of the + /// Variable is a MinIncrMax + /// + /// + /// A Variable with a ValueSet if this can be calculated let minIncrMaxToValues var = if var |> isMinIncrMax |> not then var else @@ -3099,12 +3199,19 @@ module Variable = raise e + /// + /// 'Prunes' the ValueRange of a Variable + /// + /// let prune var = { var with Values = var.Values |> ValueRange.prune } + /// + /// Set the unit of a Variable + /// let setUnit unit var = { var with Values = var.Values |> ValueRange.setUnit unit diff --git a/tests/Informedica.GenSolver.Tests/Tests.fs b/tests/Informedica.GenSolver.Tests/Tests.fs index e6666b1..f85a693 100644 --- a/tests/Informedica.GenSolver.Tests/Tests.fs +++ b/tests/Informedica.GenSolver.Tests/Tests.fs @@ -97,8 +97,8 @@ module TestSolver = open Informedica.GenUnits.Lib open Informedica.GenSolver.Lib - module Api = Informedica.GenSolver.Lib.Api - module Solver = Informedica.GenSolver.Lib.Solver + module Api = Api + module Solver = Solver module Name = Variable.Name module ValueRange = Variable.ValueRange module Minimum = ValueRange.Minimum @@ -118,12 +118,12 @@ module TestSolver = let printEqs = function | Ok eqs -> eqs |> Solver.printEqs true procss - | Error errs -> failwith "errors" + | Error _ -> failwith "errors" let printEqsWithUnits = function | Ok eqs -> eqs |> Solver.printEqs false procss - | Error errs -> failwith "errors" + | Error _ -> failwith "errors" let setProp n p eqs = @@ -160,7 +160,7 @@ module TestSolver = let setValues u n vals = vals |> createValSet u |> ValsProp |> setProp n let logger = - fun (s : string) -> + fun (_ : string) -> () //File.AppendAllLines("examples.log", [s]) |> SolverLogging.logger @@ -701,7 +701,7 @@ module Tests = | Some min', Some max' -> let min = min' |> BigRational.fromInt |> ValueUnit.singleWithUnit Units.Count.times |> Minimum.create minIncl let max = max' |> BigRational.fromInt |> ValueUnit.singleWithUnit Units.Count.times |> Maximum.create maxIncl - min |> ValueRange.minSTEmax max + min |> minSTEmax max let min1Options = [None, false; Some -2, true; Some -2, false; Some 0, true; Some 0, false; Some 2, true; Some 2, false] let max1Options = [None, false; Some -1, true; Some -1, false; Some 0, true; Some 0, false; Some 3, true; Some 3, false] @@ -732,10 +732,10 @@ module Tests = |> List.distinct - let mult = Variable.ValueRange.MinMaxCalculator.multiplication - let div = Variable.ValueRange.MinMaxCalculator.division - let add = Variable.ValueRange.MinMaxCalculator.addition - let sub = Variable.ValueRange.MinMaxCalculator.subtraction + let mult = MinMaxCalculator.multiplication + let div = MinMaxCalculator.division + let add = MinMaxCalculator.addition + let sub = MinMaxCalculator.subtraction let createVuOpt (intOpt, b) = @@ -759,7 +759,7 @@ module Tests = (min2 |> createVuOpt) (max2 |> createVuOpt) |> fun (min, max) -> Variable.ValueRange.create true min None max None - |> Variable.ValueRange.toString true + |> toString true |> String.replace "x" "" |> String.replace "<" "< " |> String.replace ">" " >"