Skip to content
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

Better doc for beginners? #778

Closed
liufuyang opened this issue Jan 21, 2020 · 24 comments · Fixed by #785
Closed

Better doc for beginners? #778

liufuyang opened this issue Jan 21, 2020 · 24 comments · Fixed by #785

Comments

@liufuyang
Copy link
Contributor

Hi there,

As a beginner for our nice looking tool, it seems that I cannot find a good beginner level, step by step tutorial for rust ndarry in our repo?

To draw some users from python numpy to start using rust numpy, I feel that it could be very helpful to have some readme doc or a site presenting some information like this?
https://numpy.org/devdocs/user/quickstart.html

A quickstart doc would be very helpful, isn't it? Not sure how busy you are, but maybe I can try start making some simple doc, basically port some or all the example operations from https://numpy.org/devdocs/user/quickstart.html to some read me here would be helpful.

Also, I wonder, how a site/book like this is generated https://doc.rust-lang.org/rust-by-example/hello.html
I guess it would be nice that eventually we have a doc site for rust-ndarry (or perhaps there is already some?)

Thank you :)

@LukeMathWalker
Copy link
Member

A quickstart doc would be very helpful, isn't it? Not sure how busy you are, but maybe I can try start making some simple doc, basically port some or all the example operations from https://numpy.org/devdocs/user/quickstart.html to some read me here would be helpful.

That would be extremely valuable!

Also, I wonder, how a site/book like this is generated https://doc.rust-lang.org/rust-by-example/hello.html

They are using mdBook

@jondo2010
Copy link

Hi, don't forget that there is already a very nicely done section on ndarray for users that already have experience with numpy: https://docs.rs/ndarray/0.13.0/ndarray/doc/ndarray_for_numpy_users/index.html

@liufuyang
Copy link
Contributor Author

Thank you both. Good to know. I guess the above link is a bit hidden in the root readme(though it is very valuable). And it seem not very beginner friendly. Let me see whether I can find some time to work on making a simple doc first see how it looks. I am not using numpy everyday and also very new to our rust-ndarry. So even for me it feels a bit difficult to just start writing the doc as what I need is a doc that I am about to write 😄

@yutiansut
Copy link

Hi, don't forget that there is already a very nicely done section on ndarray for users that already have experience with numpy: https://docs.rs/ndarray/0.13.0/ndarray/doc/ndarray_for_numpy_users/index.html

wow this is definitely great!

@liufuyang
Copy link
Contributor Author

liufuyang commented Feb 22, 2020

@LukeMathWalker Hey Luke, I tried to make a small but hopefully beginner tutorial readme file. Perhaps you think it is is nice to have?

All the example code there is runnable in https://play.integer32.com/ playground :)

@LukeMathWalker
Copy link
Member

Hey!
I'll have a look tomorrow 😁 @liufuyang

@psotos
Copy link

psotos commented Apr 8, 2020

I am switching from C++ to Rust and I need this tutorial badly! I am really stuck. I am getting errors like this:

error[E0271]: type mismatch resolving `<(u16, u16) as ndarray::shape_builder::ShapeBuilder>::Dim == ndarray::dimension::dim::Dim<[usize; 2]>`
  --> src/advanced_noise.rs:44:20
   |
44 |     let mut grid = Array2::zeros((width, height));
   |                    ^^^^^^^^^^^^^ expected struct `ndarray::dimension::dim::Dim`, found tuple

Does this mean that I need to use:
let mut grid = Array2::zeros(width*height,2); ??

Also how to return a grid from a function is confusing as well. A tutorial would be very helpful!

@andy-thomason
Copy link

andy-thomason commented Apr 8, 2020 via email

@psotos
Copy link

psotos commented Apr 8, 2020

@andy-thomason Please do that write up! I am so new to Rust and struggling with Arrays. In other languages Arrays are easy. In Rust, so confusing. Its mostly what is the correct syntax? I still have no idea.

@andy-thomason
Copy link

andy-thomason commented Apr 8, 2020 via email

@psotos
Copy link

psotos commented Apr 8, 2020

I don't even know what those two things are. Sounds like I need to watch the full Udemy tutorial that I bought. LOL

Turbofish sounds like something that can be bought at a NAPA store.

@andy-thomason
Copy link

andy-thomason commented Apr 9, 2020 via email

@psotos
Copy link

psotos commented Apr 9, 2020

I still cannot get this syntax right. Nothing works. I feel like 10,000 monkeys on 10,000 pianos at this point. Let me just ask anyone. How do I create a multi-dimensional (two dimensions only of row, col) array of type boolean, that has an unknown size at compile time? It's killing me. Why is this so hard?

I want to create this array in a function and then return it back to the caller. This function is sort of a factory class.

Any help is much appreciated!

@psotos
Copy link

psotos commented Apr 9, 2020

Here is the code that I am trying to get to compile with no success:
`pub fn threshold_noise(height :u16, width: u16, threshold: f32) -> Array2 {
let perlin = Perlin::new();

// let mut grid = [width][height];
let mut grid= Array2::zeros((4, 2));
for x in 0..width {
    for y in 0..height {
        let ary = [x as f64,y as f64];
        let noise_val = perlin.get(([1.0f64, 1.0f64]));
        println!("noise value: {}", noise_val);
        if noise_val <= ((threshold * 2.0f32) -1.0f32) as f64 {
            grid[[x as f64,y as f64]] = true;
        }
    }
}
return grid;

}`

Here is the error that I get:

    grid[[x as f64,y as f64]] = true;
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ndarray::dimension::ndindex::NdIndex<ndarray::dimension::dim::Dim<[usize; 2]>>` is not implemented for `[f64; 2]`

Any help is greatly appreciated! I am at my wits end here and ready to abort on this library.

@jturner314
Copy link
Member

@psotos You have to use usize to index into an array, not f64. (Another issue is that Array2::zeros doesn't work for bool since bool doesn't implement the Zero trait. You have to use from_elem instead.) This compiles successfully:

pub fn threshold_noise(height: u16, width: u16, threshold: f32) -> Array2<bool> {
    let perlin = Perlin::new();

    // let mut grid = [width][height];
    let mut grid = Array2::from_elem((4, 2), false);
    for x in 0..width {
        for y in 0..height {
            let ary = [x as f64, y as f64]; // unused?
            let noise_val = perlin.get([1.0f64, 1.0f64]);
            println!("noise value: {}", noise_val);
            if noise_val <= ((threshold * 2.0f32) - 1.0f32) as f64 {
                grid[[x as usize, y as usize]] = true;
            }
        }
    }
    return grid;
}

Regarding your earlier question --

@andy-thomason is correct that the issue is due to the type of the width and height variables and that you may need to specify the element type of the array if it can't be inferred.

You can use a tuple to specify the shape, but the elements in the tuple need to be of type usize. (The error message indicates that the width and height variables you're using have type u16, not usize.) Unlike C/C++, Rust does not implicitly cast between numeric types. This has the advantage of reducing bugs, but it means that you have to explicitly convert between numeric types when necessary with the as keyword or From/Into/TryFrom/TryInto traits. In other words, the following should fix the issue:

let mut grid = Array2::zeros((usize::from(width), usize::from(height)));

Usually, the compiler can infer the element type of the array from the surrounding context, but if it can't, you'll need to explicitly specify the element type, e.g. for f64:

let mut grid = Array2::<f64>::zeros((usize::from(width), usize::from(height)));

To return it from a function, you use have a function signature like this:

// Using the `Array2` type alias for convenience:
fn return_grid() -> Array2<f64> { ... }

// Or, using the `Array` and `Ix2` type aliases:
fn return_grid() -> Array<f64, Ix2> { ... }

// Or, without any type aliases (you may need to `use ndarray::OwnedRepr;`):
fn return_grid() -> ArrayBase<OwnedRepr<f64>, Dim<[usize; 2]>> { ... }

One thing that's nice about Rust is that the error messages are typically useful. Given the error message you posted, I'd diagnose the issue as follows:

  1. The error message says, "type mismatch resolving <(u16, u16) as ndarray::shape_builder::ShapeBuilder>::Dim == ndarray::dimension::dim::Dim<[usize; 2]>", and it points to Array2::zeros with the little ^^^^^ arrows. At first glance, it's not clear entirely why there's a type mismatch.

  2. Let's look at the docs for the ArrayBase::zeros method. (If you're unaware that ndarray's various *Array* type aliases are aliases for ArrayBase with specific storage/dimension types, you might have to trace through the docs for the type aliases -- first go to the Array2 docs, see that it's an alias for Array<A, Ix2>, so click on Array to go to its docs, see that it's an alias for ArrayBase<OwnedRepr<A>, D>, so click on ArrayBase to go to its docs. Alternatively, you could type zeros into the search bar and select the relevant one.) The signature of the method is the following:

    pub fn zeros<Sh>(shape: Sh) -> Self
    where
        A: Clone + Zero,
        Sh: ShapeBuilder<Dim = D>,

    We can see where ShapeBuilder comes up -- the argument we pass to zeros must implement the ShapeBuilder trait, and the Dim associated type determines the dimension D of the created array.

  3. Okay, so let's look at the docs for ShapeBuilder. (You can click on ShapeBuilder in the docs for zeros or navigate manually.) There are two implementors for this trait. We're trying to use a tuple, not a Shape struct, so the relevant one is

    impl<T> ShapeBuilder for T
    where
        T: IntoDimension,
    {
        type Dim = T::Dim;
        ...
    }

    In order for this implementation to apply, our tuple type (T in the signature) must implement the IntoDimension trait.

  4. Okay, so let's look at the docs for IntoDimension. (You can click on IntoDimension in the docs for ShapeBuilder or navigate manually.) There are multiple implementors, including tuples and arrays of Ix (which you can see is an alias for usize if you click on the link in the docs). Note, in particular, that there are no implementations for tuples of u16. (If you wanted, you could follow the impl<D> IntoDimension for D where D: Dimension to see if that applies, but it turns out that it doesn't.) So, we can fix the error by converting the u16 to usize so that the type of the tuple is (usize, usize) instead of (u16, u16).

The point I'm trying to make is not related to this specific issue, but rather that it's generally possible to diagnose a compilation issue by carefully reading the error message and going to the relevant docs. (This particular case was unusually complicated, and the error message is slightly misleading. (I'd prefer the error message to be "ShapeBuilder is not implemented for (u16, u16)".) However, tracing through the docs is still sufficient to figure it out.) Once you've become familiar with ndarray, tracing through the docs like this shouldn't be necessary, but when using a new library, it takes a little while to familiarize yourself with the important types and traits.

I'd recommend reading through the Rust book if you haven't already. ndarray uses many of Rust's more advanced type features, so it's helpful to be familiar with the language before diving into ndarray.

I hope this is helpful. Please feel free to ask additional questions if you have trouble.

@psotos
Copy link

psotos commented Apr 10, 2020

@jturner314
Jim, this is an amazing and through answer. The problem with reading the docs, and I did in fact try, is that the syntax is so foreign to me. It was extremely hard for me to grasp what they were trying to tell me. Your fix of my code, and your very complete descriptions are a huge help for me.

Thank you very much!

One thing I should add... Jim's post is exactly what you are missing. Why not edit this slightly and turn it into a tutorial? I mean it REALLY helped me to understand how to use this library. Edit it, slap it up on the crate site, and you at least have a very strong start. Your thoughts?

@liufuyang
Copy link
Contributor Author

liufuyang commented Apr 11, 2020

@psotos when you say Jim's post is exactly what you are missing, do you refer to my PR - the one which adding some simple step by step guide or?
https://github.com/rust-ndarray/ndarray/pull/785/files?short_path=7ff0517#diff-7ff05179e7ecf39233791882ec74fc1f

Have you read the doc I am trying to add to the repo?

Regarding to your question How do I create a multi-dimensional (two dimensions only of row, col) array of type boolean, that has an unknown size at compile time?
And isn't the second example there like this helping to answer your question?

use ndarray::prelude::*;
use ndarray::{Array, Ix3};
fn main() {
  let a = Array::<f64, Ix3>::zeros((3, 2, 4).f());
  println!("{:?}", a);
}

Otherwise, I hope my PR #785 can be merged soon, and if you think there is any other nice small and runnable cases can be added there, we can make extra PR's to make the doc more friendly and more informative.


BTW, I just added this example in my PR:

use ndarray::{Array, Ix3};
fn main() {
  let a = Array::<bool, Ix3>::from_elem((3, 2, 4), false);
  println!("{:?}", a);
}

I think it helps answer your questions well.

@psotos
Copy link

psotos commented Apr 11, 2020

@liufuyang
No Sir I was not referring to your PR. I did not know that you had a PR to update the document. I will most gladly read it and provide you some feedback. Thanks for taking the time to create it in the first place.

@psotos
Copy link

psotos commented Apr 15, 2020

@liufuyang I just went through your Quickstart guide. It's amazing! Why isn't this PR getting processed? It would help so many coders trying to use ndarray!

@LukeMathWalker
Copy link
Member

I have been quite busy recently, not a lot of time to dedicate here - I have done a first review of @liufuyang's PR and I'll try to do a pass on the revised version.
If you can take a peek as well @jturner314 I think we could merge it afterwards.

@janroden
Copy link

janroden commented Apr 20, 2020

Thanks a lot for your efforts to write up these tutorials! I think it will be very helpful and probably result in more people using rust-ndarray, if there are some clear recipes. Coming from Numpy, I already found https://docs.rs/ndarray/0.13.0/ndarray/doc/ndarray_for_numpy_users/index.html extremely helpful.

One thing that isn't obvious to me (and I'm currently trying to figure this out), is how to create an ArrayView providing a different element type than the underlying array referenced by the view. In Numpy, if I have e.g. an array of element type uint16, and I want a view of this array, but with element type float32, I can simply use view = myarray.astype("float32", copy=False). How would I do that with rust-ndarray?
I guess one could simply use myarray.mapv(|el| el as f32) ...

I think such type conversions in views are probably a common use case and maybe it would be good to include an example for this in the tutorial as well?

Thank you very much for your work on this nice crate!

@bluss
Copy link
Member

bluss commented Apr 20, 2020

@janroden Element type conversions like that are yet to be implemented. For Raw views we have a cast method, and this means that users have the building blocks for implementing such views if they want, with the restriction that the cast only allows very similar types to be translated - same size and alignment, so for example not u16 to f32, but possibly u32 to f32 and definitely f32 to Cell<f32> (this was the envisioned use case).

And I guess.. it's only in master, will be in next point release.

@liufuyang
Copy link
Contributor Author

Yes I need to update my PR in order to get it merged. Let me try find some time for it soon. Not much left to change I suppose.

@janroden
Copy link

Thanks a lot for the explanation @bluss

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants