-
-
Notifications
You must be signed in to change notification settings - Fork 357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Gaussian Elimination in Haskell #193
Added Gaussian Elimination in Haskell #193
Conversation
jiegillet
commented
Jul 1, 2018
- Added implementation
- Corrected "Eschelon" to "Echelon" (which comes from a French word meaning step of a ladder)
- Minor typos
- Removed "Reduced Row Eschelon Form is diagonal" since it's not true in general
- Removed "You can do basically anything you want" since if you start from the upper left you'll have to do several passes. While technically you can always do what you want, maybe it's best to suggest the most efficient route.
- James refers to his Julia code as "pseudocode", I didn't want to change the text so my crazy haskell code is now "pseudocode" as well (it works for crazy FP people maybe)
I just realized that the text for this chapter was written way back when I wanted to use pseudocode for this project, as such, line 264 can probably be deleted. If we are doing chapter edits anyway, maybe we can do this here? (sorry for the extra work) |
Happy to do it. |
Is there any way to get lulucca12 (#152 -- I cannot address him for some reason) to help with haskell review? |
That'd be great. (I cannot address him either, maybe because he never submitted a PR). |
6479bd2
to
dbde5c2
Compare
dbde5c2
to
57b8e7a
Compare
===> Changed pseudocode to code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be beautified automatically with "hindent" and an extension to atom (atom-beautify) or vscode.
go (r, c) m = foldr (combRows (r, c) 1) (multRow r (1/(m!(r, c))) m) [r1..r-1] | ||
|
||
|
||
printM :: (Show a) => Matrix a -> String |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ghc-mod suggested that this could be defaulted to Double to add a constraint:
printM :: Matrix Double -> String
printM m =
let ((r1, c1), (rn, cn)) = bounds m
in unlines
[ intercalate "\t" [take 7 $ show $ m ! (r, c) | c <- [c1 .. cn]]
| r <- [r1 .. rn]
]
7ceb7bd
to
05639f3
Compare
Updated my code, it's cleaner now, also added back substitution. Got rid of chapter edits. |
I think before merging this PR(now that #219 is merged) we could format and factor out the code, passing it through a linter (and maybe a beautifier) to verify that all the added code it's all optimized and good. |
I ran it through hlint and got no hints. I tried a beautifier (hindent I think) but it made it worse :) |
…archive into gaussianElimination
We should do some refactoring and make the code as clean and declarative as possible, so, when possible, we could substitute one letter parameters to the functions and make there purpose clear. Also, I saw that you created two print functions (printM for matrix and printV for vectors) if you made the type synonyms type declarations we could derive them (using instance) of the Show type class. |
Single letter parameters are mostly matrix indices, Do you have concrete suggestions for making the code clean and declarative? Honestly I've done my best here. I'll look into the Show instances, I actually started doing it that way, but it got complicated I don't remember exactly why, maybe because I need a |
I think there is just a little issue on how this code is written, and from the fact that you created type synonyms to make the code more understandable: There is no way of someone with just the type declaration of any function rewrite that function with given context. To see what I mean with that take an example: the function compose compose :: (b -> c) -> (a -> b) -> a -> c it's really clear(even if you don't give an explicit name) that you can use techniques for programming with context (like using Category Theory, or in a much simpler way Hole-driven Haskell) and generate the function compose f g x = f (g x) and I understand that this is not always possible with complex algorithms like this one, but making polymorphic types and explicit notations can make this way of Haskell programming better. |
And for the Show instances, could you write your approach in code for me to understand it? |
It's absolutely impossible to write algorithms with the intent of letting people be able to rewrite the code from the signature alone. For example if you're computing convex hulls, there are many methods, but all have the same signature. In the case of code like this, the signature is there to explain how the code is used. For example About polymorphism, the only non-polymorphic type I have is for Show, I suggest you try yourself to make While I appreciate your criticisms, they are a bit vague and disconnected from the actual algorithm. I'd like to have some concrete ideas on what I can do better. |
I said that because it's a common use and necessity for giving your code context (creating new type classes and deriving from existing ones), but as I said not all code is possible to be created that way, it's just a common practice when refactoring code trying to make the context of a function like the other functions. |
I know it's not very readable, haskell is not super suited for matrix operations (my FFT code is horrendous). I'm very happy to try and make it better, do you have concrete suggestions? |
Have you seen Data.Matrix? I think we could rewrite the code using its functions and make it more clear. For example, they have a |
You could use Data.Vector with it (but it can cause ambiguity) |
Data.Matrix is not in the standard distribution, I don't think I should use that, even though it has some functions that I could use (switchRows, combineRows). Even if I did use these functions, the core algorithm (gaussJordan, backSubstitution) wouldn't change at all. All really, if you look at the source course for switchRows for example, it's not much more beautiful than my code. Data.Vector is in the standard distribution, I think, but it's based on Data.Array to begin with, and it doesn't have many function I could use. All in all, none of my functions are longer than 11 lines. They may be dense and a bit hard to read, but it's the reality of the algorithm. |
Being on the standard distribution or not is not a problem if you say clearly this information in the code and I really think that you could shorten your code without defining again what was already defined and can be standard. And it's good to use Haskell libraries because it gives your code more readability. Do you prefer to use "\f g -> (\x -> f(g x))" or "."? |
Have you seen the functions inverse :: (Fractional a, Eq a) => Matrix a -> Either String (Matrix a) and rref :: (Fractional a, Eq a) => Matrix a -> Either String (Matrix a) the last one converts a matrix to reduced row echelon form using Gaussian elimination and thus solving a system of linear equations. |
Of course I can't use a built-in algorithm, that defeats the purpose. Overall I'm pretty reluctant to rely too much on outside source, even if it makes my code shorter. I like the idea of having everything you need in one file. And so far, that's been the philosophy of the AAA for all languages. |
I would like to say that the point of this chapter is to show how the algorithm could be implemented in haskell, so using an in-built I don't know how many people use the That said, using a vector/matrix library it might be overkill for this case. |
The purpose was just to shorten the code, I mentioned this functions for you to look at their source and how they are implemented and to get an idea, but for factoring the code I think you can use the library just for their print and prettier functions but other ones that can help too, like the switchRows and switchCols that you don't have to implement them by yourself. |
Yeah I understand your purpose. I also love short, point-free style functions. It's the beauty of Haskell. But I can't refactor a 10 lines function down to 2 even with other people's build in functions. I've already said that I prefer using my own code, especially that my functions are not especially worse than the ones in Data.Matrix. You haven't criticized a single specific line of code, just the general look of it. You gave me no reason to believe that you actually read my code at all past the signatures. If it all comes down to esthetics and personal preference, I ask that you respect my choices as the author of the code. If you read the code and have no specific improvements in mind, provide a positive review. If you haven't, leave it to someone else to do the review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, the problem isn't specific to your code, but to the fact that you step back when using other's code. If you really want to use your code and make the file bigger, do so, if not implement it with the library. I don't see how I can improve the code apart from that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If @lulucca12 is happy, I'm happy. I understand that there is still discussion to be had here, but it seems like that discussion boils down to personal preference.