Advertisement
Guest User

Untitled

a guest
Jun 13th, 2017
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
F# 13.30 KB | None | 0 0
  1. module ExprToPoly
  2.  
  3. (*#load "ExprParse.fs"*)
  4.  
  5. open ExprParse
  6. type expr = ExprParse.expr
  7.  
  8. let rec ppExpr = function
  9.   | FNum c -> string(c)
  10.   | FVar s -> s
  11.   | FAdd(e1,e2) -> "(" + (ppExpr e1) + " + " + (ppExpr e2) + ")"
  12.   | FMult(e1,e2) -> (ppExpr e1) + " * " + (ppExpr e2)
  13.   | FExponent(e,n) -> "(" + (ppExpr e) + ")^" + string(n)
  14.   | FDiv(e1, e2) -> "(" + (ppExpr e1) + ")/(" + (ppExpr e2) + ")"
  15.   | FRoot(e, n) -> "√" + (string n) + "(" + (ppExpr e) + ")"
  16.  
  17. let rec subst e (x,ex) =
  18.   match e with  
  19.   | FNum c -> FNum c
  20.   | FVar s -> if s = x then ex else e
  21.   | FMult(e1,e2) -> FMult(subst e1 (x,ex), subst e2 (x,ex))
  22.   | FAdd(e1,e2)  -> FAdd(subst e1 (x,ex), subst e2 (x,ex))
  23.   | FExponent(e, n) -> FExponent(subst e (x,ex), n)
  24.   | FDiv(e1, e2) -> FDiv(subst e1 (x,ex), subst e2 (x,ex))
  25.   | FRoot(e, n) -> FRoot(subst e (x,ex), n)
  26.  
  27. // Root atom is not really an atom because it cannot immediatly be reduced
  28. type atom = ANum of float | AExponent of string * int | ARoot of (atom list list) * (atom list list) * int
  29. type atomGroup = atom list  
  30. type simpleExpr = SE of atomGroup list
  31. let isSimpleExprEmpty (SE ags) = ags = [] || ags = [[]]
  32.  
  33. let ppAtom = function
  34.   | ANum c -> string(c)
  35.   | AExponent(s,1) -> s
  36.   | AExponent(s,n) -> s+"^"+(string(n))
  37. let ppAtomGroup ag = String.concat "*" (List.map ppAtom ag)
  38. let ppSimpleExpr (SE ags) = String.concat "+" (List.map ppAtomGroup ags)
  39.  
  40. let rec combine xss = function
  41.   | [] -> []
  42.   | ys::yss -> List.map ((@) ys) xss @ combine xss yss
  43.  
  44. /// <summary>
  45. /// takes an expression and puts it to the nth power. (e)^n
  46. /// </summary>
  47. /// <param name="original">expression to be combined</param>
  48. /// <param name="n">nth power</param>
  49. let combineN original n =
  50.     let rec combineHelper (original:atom list list) (acc: atom list list) = function // Basically original^n
  51.         | 1 -> acc
  52.         | n -> combineHelper original (combine original acc) (n-1)
  53.     combineHelper original original n
  54.  
  55.  
  56. let rec simplify = function
  57.   | FNum c -> ([[ANum c]], [[ANum 1.]])
  58.   | FVar s -> ([[AExponent(s,1)]], [[ANum 1.]])
  59.   | FAdd(e1,e2) -> let num1, div1 = simplify e1 // simplify each part of addition
  60.                    let num2, div2 = simplify e2
  61.                    let numres1 = combine num1 div2 // add fractions together a/b+c/d = (ad+bc)/bd
  62.                    let numres2 = combine num2 div1
  63.                    let divres = combine div1 div2
  64.                    (numres1 @ numres2, divres)
  65.   | FMult(e1,e2) -> let num1, div1 = simplify e1 // simplify each part of multiplication
  66.                     let num2, div2 = simplify e2
  67.                     (combine num1 num2, combine div1 div2) // multiply fractions a/b*c/d = ab/cd
  68.   | FDiv(e1, e2) -> let num1, div1 = simplify e1 // simplify each part of division
  69.                     let num2, div2 = simplify e2
  70.                     let numres = combine num1 div2 // divide fractions (a/b)/(c/d) = ad/bc
  71.                     let divres = combine div1 num2
  72.                     (numres, divres)
  73.   | FExponent(e1,0) -> ([[ANum 1.]], [[ANum 1.]])
  74.   | FExponent(e1,1) -> simplify e1
  75.   | FExponent(e1,n) -> let num, div = simplify e1
  76.                        let numr, divr = FExponent(e1, n-1) |> simplify
  77.                        (combine num numr, combine div divr) // multiply fractions together
  78.   | FRoot(e, n)     -> let num, div = simplify e
  79.                        ([[ARoot(num, div, n)]], [[ANum 1.]]) // create root atom
  80.  
  81.  
  82. let simplifyAtomGroup ag =
  83.   let mapCount = fun ((m:Map<string,int>), c) e ->
  84.     match e with
  85.     | AExponent(v, n) ->
  86.       match Map.tryFind v m with
  87.       | Some x -> (Map.add v (x+n) m, c)
  88.       | None   -> (Map.add v n m, c)
  89.     | ANum c' -> (m, c*c')
  90.     | _ -> (m, c)
  91.  
  92.   let roots = List.filter (fun e -> match e with
  93.                                     | ARoot _ -> true
  94.                                     | _ -> false) ag
  95.  
  96.  
  97.   let (collectedTerms, c) = List.fold mapCount (Map.empty, 1.0) ag
  98.   let terms = roots @ List.map AExponent (Map.toList collectedTerms)
  99.  
  100.   match c with
  101.     | 0.0 -> []
  102.     | 1.0 -> terms
  103.     | _ -> ANum c :: terms                    
  104.  
  105.  
  106. let simplifySimpleExpr (SE ags) =
  107.   let ags' = List.map simplifyAtomGroup ags
  108.  // Add atom groups with only constants together.
  109.  let collectConst = fun (acc, (li:atom list list)) e ->
  110.    match e with
  111.    | [ANum c] -> (acc+c, li)
  112.    | _        -> (acc, e::li)
  113.  let (total, agsV) = List.fold collectConst (0.0, []) ags'
  114.  
  115.   // Last task is to group similar atomGroups into one group.
  116.   let groupCount = fun (m:Map<atomGroup,int>) e ->
  117.     match Map.tryFind e m with
  118.     | Some x -> Map.add e (x+1) m
  119.     | None   -> Map.add e 1 m
  120.      
  121.   let agsG = List.fold groupCount Map.empty agsV
  122.  
  123.   let foldToList = fun li k v ->
  124.     match v with
  125.     | 1 -> k::li
  126.     | _ -> (ANum (float(v)) :: k) :: li
  127.      
  128.   let agsL = List.rev <| Map.fold foldToList [] agsG // I have to reverse the list
  129.  
  130.   SE( if total <> 0.0
  131.       then [ANum total] :: agsL
  132.       else agsL )
  133.  
  134.  
  135. /// <summary>
  136. /// Simplify roots that are set to a power greater than 1
  137. /// </summary>
  138. let simplifyRoots (SE exp) =
  139.     // Helper function for counting number of roots in a atom list
  140.     let rootCount = fun (r:Map<((atom list list)*(atom list list)*int),int>) e ->
  141.         match e with
  142.         | ARoot(num, div, n) -> // If a root is found, increase the counter in the map by one. Otherwise add it to the map
  143.             match Map.tryFind (num, div, n) r with
  144.             | Some x -> Map.add (num, div, n) (x+1) r
  145.             | None   -> Map.add (num, div, n) 1 r
  146.         | _ -> r
  147.  
  148.     // Count total number of roots
  149.     let atomGroupRoots = List.fold rootCount (Map.empty)
  150.     // Count for each atom group
  151.     let rootMaps = List.map atomGroupRoots exp
  152.  
  153.    
  154.     let genRootTerm e = // What it actually does is simplify the roots for a single term
  155.         let removeTermsFromRoot (root:atom list) = List.partition (fun e -> match e with
  156.                                                                         | ARoot _ -> false
  157.                                                                         | _ -> true) root
  158.         let terms, roots = removeTermsFromRoot e // Split term into non-root part and root-part
  159.  
  160.         let rootMap =  List.fold rootCount (Map.empty) roots // Create a rootmap (which counts the occurences of each root)
  161.  
  162.         // Depending on the exponent of the root, either just return the root, or remove the root.
  163.         // The exponent has to be a multiple of the root degree, since we will otherwise just get a new root.
  164.         // Example: sqrt(2)^4 = 2^2
  165.         let extractRoot ((num, div, n):((atom list list)*(atom list list)*int)) count =
  166.             let root = (num, div, n)
  167.             match count with
  168.             | a when a < n -> ([[ARoot(num, div, n)]], [[ANum 1.]]) // Because the exponent is too low, we just return the original root
  169.             | a when a % n = 0 -> let exp = a/n // Get exponent of part of expression without root
  170.                                   (combineN num exp, combineN div exp) // Calculate the fraction of the root exponentiated to the (a/n)th power
  171.             | _ -> failwith "root exponent didn't match" // Panic if they don't match
  172.  
  173.         // Extract root and multiply with fraction - (num, div) is the accumulator for the fraction expression so far.
  174.         let multiplyRoot (num, div) root count =
  175.             let (rootnum, rootdiv) = extractRoot root count
  176.             (combine rootnum num, combine rootdiv div) // Multiply the extracted root onto the accumulator
  177.  
  178.         // This is where everything begins.
  179.         // Take every root in the current term and map the multiplyRoot function over it.
  180.         // The result is effectively that every exponentiated root is simplified
  181.         Map.fold multiplyRoot ([terms], [[ANum 1.]]) rootMap
  182.  
  183.     // Simplify the roots of every term in the expression
  184.     let simplifiedTerms = List.map genRootTerm exp
  185.     // Combine the terms so we get a new term which is a single fraction
  186.     // (which makes it possible to discard the denominator)
  187.     let combinedTerms =
  188.         List.reduce (fun (resnum, resdiv) (num, div) ->
  189.                             (combine resnum div @ combine num resdiv, combine resdiv div) // Add current fraction to accumulator
  190.                     ) simplifiedTerms
  191.     SE(fst combinedTerms) // Only return the numerator
  192.  
  193. let exprToSimpleExpr e = simplifySimpleExpr (SE (simplify e |> fst))
  194.  
  195. let exprToSimpleDivExpr e = let (num, div) = simplify e
  196.                             (simplifySimpleExpr (SE num), simplifySimpleExpr (SE div))
  197.  
  198. /// Simplifies roots of simpleExpr by repeatedly isolating a root on one side of the expression and exponentiating both sides.
  199. let solveRoots (SE exp) =
  200.     // Returns a bool indicating whether there is a root in the group or not
  201.     let isRootGroup (group:atom list) = List.exists (fun e -> match e with
  202.                                                               | ARoot _ -> true
  203.                                                               | _ -> false) group
  204.  
  205.     let partitionTermIntoRootAndRest (root:atom list) = List.partition (fun e -> match e with
  206.                                                                         | ARoot _ -> false
  207.                                                                         | _ -> true) root
  208.     // This is the actual method doing the simplification
  209.     let rec simplify (before: atom list list) = function
  210.     | x::xs when isRootGroup x -> // Only simplify atomGroups containing roots
  211.         let complete = before @ xs // Creates an exp that contains the whole expression except the current root term
  212.         let rootTerms, root = partitionTermIntoRootAndRest x // Split root term into root and rest
  213.         match root with
  214.         | ARoot(rnum, rdiv, n) :: ot ->
  215.             // Move the expression "complete" to other side of the equal sign by multiplying by -1
  216.             let cominv = List.map (fun e -> if e <> [] then ANum -1. :: e else e) complete
  217.             // Now both the root needs to be isolated. Because there might be more terms in the root group
  218.             // than the root itself, we need to divide by it on both sides on the equal sign.
  219.             // This is done here by simply making complete the numerator and the rootterms the denominator of a fraction.
  220.             // Example: sqrt(2)*4x = 5y <-> sqrt(2) = 5y/4x
  221.             let cnum, cdiv = cominv, [rootTerms@ot] // Divide cominv by the rootterms
  222.             // Now we remove the root by exponentiating both sides of the equation by the degree of the root
  223.             let newcnum, newcdiv = combineN cnum n, combineN cdiv n // put expression to the power of the root
  224.             // Now we need to move the (complete/rootterms) expression back to the other side of the equation.
  225.             // This is done by multiplying by -1 for both the numerator and denominator.
  226.             let newcnuminv = List.map (fun e -> ANum -1. :: e) newcnum
  227.             let newcdivinv = List.map (fun e -> ANum -1. :: e) newcdiv
  228.             // Now we add the two expressions together.
  229.             // We also call the simplifyRoots function to check if any new nested
  230.             // roots can be simplified.
  231.             // Finally we simplify the expression because a lot of extra atoms have emerged from the repeated combinations
  232.             let (SE fcnum) = (combine newcnuminv rdiv) @ (combine rnum newcdiv) |> SE |> simplifyRoots |> simplifySimpleExpr
  233.             let (SE fcdiv) = combine rdiv newcdiv |> SE |> simplifyRoots |> simplifySimpleExpr
  234.             // Because we have encountered a root, we need to start from the beginning. This is done in order to
  235.             // cover a case when a root contains another nested root.
  236.             // Note: this method doesn't work if a term contains roots with different degrees (could be solved by only using the first root but is not required by any of the tested formulas)
  237.             simplify [] fcnum
  238.         | _ -> (x::xs)  
  239.     | x::xs -> simplify (x::before) xs // Simply skip groups without roots
  240.     | _ -> before // We are done and return the simplified expression
  241.  
  242.     // Run the simplify function and simplify the result (it contains a lot of 1's)
  243.     simplifySimpleExpr (SE(simplify [] exp))
  244.  
  245. type poly = P of Map<int,simpleExpr>
  246.  
  247. let ppPoly v (P p) =
  248.   let pp (d,ags) =
  249.     let prefix = if d=0 then "" else ppAtom (AExponent(v,d))
  250.     let postfix = if isSimpleExprEmpty ags then "" else "(" + (ppSimpleExpr ags) + ")"
  251.     prefix + postfix
  252.   String.concat "+" (List.map pp (Map.toList p))
  253.  
  254. (* Collect atom groups into groups with respect to one variable v *)
  255. let splitAG v m = function
  256.   | [] -> m
  257.   | ag ->
  258.     let eqV = function AExponent(v',_) -> v = v' | _ -> false
  259.     let addMap d ag m =
  260.       match Map.tryFind d m with
  261.       | Some (SE x) -> Map.add d (SE (ag::x) ) m
  262.       | None   -> Map.add d (SE [ag]) m
  263.     match List.tryFind eqV ag with
  264.       | Some (AExponent(_,d)) ->
  265.         let ag' = List.filter (not << eqV) ag
  266.        addMap d ag' m
  267.       | Some _ -> failwith "splitAG: Must never come here! - ANum will not match eqV"
  268.       | None -> addMap 0 ag m
  269.  
  270. let simpleExprToPoly (SE ags) v =
  271.   P (List.fold (splitAG v) Map.empty ags)
  272.  
  273. let exprToPoly e v = (exprToSimpleExpr >> simplifySimpleExpr >> simplifyRoots >> solveRoots >> simpleExprToPoly) e v
  274.  
  275. let stringToPoly (s:string) = exprToPoly (parseStr s)
  276.  
  277. let polyMap (P m) = m
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement