< 返回版块

eweca 发表于 2020-12-31 17:08

请教一下,如何把Vec<Vec<f64>>转化为 &[[T]]

想做一点科学计算,需要用到ndarray来加速矩阵的计算。写了如下的Enum并使用raw2array来互相转化:

#[derive(Debug, PartialEq)]
pub enum Matrix {
    Raw(Vec<Vec<f64>>),
    Array(Array2<f64>),
}

impl Matrix {
    pub fn raw2array(&self) -> Self {
        match self {
            Matrix::Raw(x) => {
                let m = x.len();
                let n = x[0].len();
                let arr: Vec<f64> = x.into_iter().flatten().cloned().collect();
                let foo = Array::from_iter(arr.into_iter()).into_shape((m, n)).unwrap();
                Matrix::Array(foo)
            },
            _ => {panic!("It is not a Matrix::Raw so that cannot be converted to a Matrix::Array using function raw2array!")},
        }
    } 
}

ndarray无法接收一个Vec<Vec<f64>>来进行转化,而是需要使用如arr2(&[[0., 1.], [2., 3.]])来生成一个二维矩阵。我知道as_slice()有类似功能,但是对于二维Vec来说,貌似会变成&[Vec<f64>]这种形式。在这里,我目前的做法是想把二维的Vec压成一维,然后使用ndarray自带的into_shape功能进行转化。

但我总觉得这样的效率可能有些低,请问有更直接一点的方法吗?

PS:因为我希望使用的时候只用Vec<T>进行输入输出,内部转化和加速计算细节不对外暴露,所以输入是强制性的Vec<T>, Vec<Vec<T>>,或Vec<Vec<Vec<T>>>

评论区

写评论
作者 eweca 2021-01-01 12:50

非常感谢你的回复。我现在意识到了不能转化了。一行行复制的话,ndarray库有现成的运行时定长的数组生成功能,实测还蛮好用。

--
👇
Aya0wind: 不能直接转化,这两个内存布局都不一样。你只能创建一个新的二维数组,然后一行行的复制。 如果你要一个不定长的内存连续的二维数组,那就用一维的vec,然后外面包一层wrapper,提供一套使用二维下标的接口,你在里面把二维的下标换算成一维的,vec长度就是长乘宽。

作者 eweca 2021-01-01 12:48

感谢你持续关注这个帖子。不过,vec自带len计数,所以没必要添加额外的dimension来吧。目前看下来,估计只有重建这一条路了。你的这个into_array2函数的想法非常好用。实测,大概效率是我原先的三倍。感谢!

--
👇
ezlearning: 关键还是要知道矩阵维度。这样做吧:

use ndarray::{Array2};

#[derive(Debug, PartialEq)]
struct SquareVec {
    dimension: usize,
    data: Vec<Vec<f64>>
}

impl SquareVec {
    fn new(dimension: usize) -> SquareVec {
        let data: Vec<Vec<f64>>= vec![vec![0.0; dimension]; dimension];
        SquareVec {
            dimension,
            data,
        }
    }

    fn into_array2(&self) -> Array2<f64> {
        let mut array2: Array2<f64> = Array2::zeros((self.dimension, self.dimension));
        for (row, column_data) in self.data.iter().enumerate() {
            for (column, value) in column_data.iter().enumerate() {
                array2[[row, column]] = *value;
            }
        }
        array2
    }
}

fn main() {
    let square_vec = SquareVec::new(3);
    let array2 = square_vec.into_array2();
    println!("{:?}", square_vec);
    println!("{:?}", array2);
}
Aya0wind 2021-01-01 11:45

不能直接转化,这两个内存布局都不一样。你只能创建一个新的二维数组,然后一行行的复制。 如果你要一个不定长的内存连续的二维数组,那就用一维的vec,然后外面包一层wrapper,提供一套使用二维下标的接口,你在里面把二维的下标换算成一维的,vec长度就是长乘宽。

作者 eweca 2021-01-01 11:44

是的,估计这也是为什么要求转成array的原因吧。1.51 stable还没出吧?之后我会关注下的。谢谢你,学到了可能会用到的新知识。

--
👇
Neutron3529: 试试1.51的常量泛型好了。

反正如果用Vec<Vec>,你几乎没办法获得任何加速的。

作者 eweca 2021-01-01 11:42

感谢你的回复,学到了很多关于内存布局的细节。但是原函数要求输入是&[[f64]]而不是&[&[f64]],想来原库是要求完全按照array来布局数据,然后传入&array引用。在你科普之后,我个人的想法是,估计还是只能老办法,重建一个array了。

--
👇
cnwzhjs: 很不幸,Vec<Vec<f64>>是无法转化为&[[f64]]的。 根本原因在于Vec的内存布局:

pub struct Vec<T> {
    buf: RawVec<T>,
    len: usize,
}

pub struct RawVec<T> {
    ptr: Unique<T>,
    cap: usize,
}

可以看到,在内存中,它有一个pointer、一个cap,一个len。而slice的在内存中额数据结构是一个pointer、一个len。

所以,当你持有一个Vec<Vec<f64>>时,是可以生成一个&[Vec<64>]的slice的,做法上只需把外层Vec的ptr和len拿出来即可生成slice。

但是,由于内层每个也都是Vec的时候,没法直接生成slice of array的。

对应的,你可以生成Vec<&[f64]>,进而再生成&[&[f64]],也就是slice of slice:

fn get_vec_of_slice(v: &Vec<Vec<f64>>) -> Vec<&[f64]> {
    v.iter().map(|x| x.as_slice()).collect()
}

fn main()
{
    let v = vec![vec![1f64, 2f64, 3f64], vec![4f64, 5f64, 6f64], vec![7f64, 8f64, 9f64]];
    let vec_of_slice = get_vec_of_slice(&v);
    let slice_of_slice : &[&[f64]] = vec_of_slice.as_slice();
    println!("{:?}", slice_of_slice);
}
ezlearning 2021-01-01 08:51

关键还是要知道矩阵维度。这样做吧:

use ndarray::{Array2};

#[derive(Debug, PartialEq)]
struct SquareVec {
    dimension: usize,
    data: Vec<Vec<f64>>
}

impl SquareVec {
    fn new(dimension: usize) -> SquareVec {
        let data: Vec<Vec<f64>>= vec![vec![0.0; dimension]; dimension];
        SquareVec {
            dimension,
            data,
        }
    }

    fn into_array2(&self) -> Array2<f64> {
        let mut array2: Array2<f64> = Array2::zeros((self.dimension, self.dimension));
        for (row, column_data) in self.data.iter().enumerate() {
            for (column, value) in column_data.iter().enumerate() {
                array2[[row, column]] = *value;
            }
        }
        array2
    }
}

fn main() {
    let square_vec = SquareVec::new(3);
    let array2 = square_vec.into_array2();
    println!("{:?}", square_vec);
    println!("{:?}", array2);
}
Neutron3529 2021-01-01 00:50

试试1.51的常量泛型好了。

反正如果用Vec<Vec>,你几乎没办法获得任何加速的。

cnwzhjs 2020-12-31 23:27

很不幸,Vec<Vec<f64>>是无法转化为&[[f64]]的。 根本原因在于Vec的内存布局:

pub struct Vec<T> {
    buf: RawVec<T>,
    len: usize,
}

pub struct RawVec<T> {
    ptr: Unique<T>,
    cap: usize,
}

可以看到,在内存中,它有一个pointer、一个cap,一个len。而slice的在内存中额数据结构是一个pointer、一个len。

所以,当你持有一个Vec<Vec<f64>>时,是可以生成一个&[Vec<64>]的slice的,做法上只需把外层Vec的ptr和len拿出来即可生成slice。

但是,由于内层每个也都是Vec的时候,没法直接生成slice of array的。

对应的,你可以生成Vec<&[f64]>,进而再生成&[&[f64]],也就是slice of slice:

fn get_vec_of_slice(v: &Vec<Vec<f64>>) -> Vec<&[f64]> {
    v.iter().map(|x| x.as_slice()).collect()
}

fn main()
{
    let v = vec![vec![1f64, 2f64, 3f64], vec![4f64, 5f64, 6f64], vec![7f64, 8f64, 9f64]];
    let vec_of_slice = get_vec_of_slice(&v);
    let slice_of_slice : &[&[f64]] = vec_of_slice.as_slice();
    println!("{:?}", slice_of_slice);
}
作者 eweca 2020-12-31 22:53

我可能会出现500 x 500或者300 x 300的矩阵。这取决于问题。不能固定这个数值。

👇
ezlearning: 不要用Vec, 这表示不了维度。 用:Vec<[f64; 2]>。

use ndarray::{Array2};

fn main() {
    let v: Vec<[f64; 2]> = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
    let a: Array2<f64> = Array2::from(v);

    println!("{:?}", a);
} 
作者 eweca 2020-12-31 22:50

感谢你的回复!但是问题在于,我的v是一个n x n的方阵。不仅如此,我的这个n是无法固定的,由运行时输入的参数决定的。所以我希望的是能够使用Vec<Vec<f64>>来表达数组。比如一个3 x 3的数组,可以写成如vec![vec![0., 1., 2.], vec![3., 4., 5.], vec![6., 7., 8.]] 。 字面值的一个问题是我无法设定一个类似[[0; n]; n]的数组字面值。

--
👇
ezlearning: 不要用Vec, 这表示不了维度。 用:Vec<[f64; 2]>。

use ndarray::{Array2};

fn main() {
    let v: Vec<[f64; 2]> = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
    let a: Array2<f64> = Array2::from(v);

    println!("{:?}", a);
} 
ezlearning 2020-12-31 21:20

不要用Vec, 这表示不了维度。 用:Vec<[f64; 2]>。

use ndarray::{Array2};

fn main() {
    let v: Vec<[f64; 2]> = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
    let a: Array2<f64> = Array2::from(v);

    println!("{:?}", a);
} 
作者 eweca 2020-12-31 18:06

非常感谢你的回复,这个quick-start的帮助很大。但是我看了下,里面仍然没有可以满足我想法的写法例子。ndarray的指南中,基本都是用数组字面也就是[T]来生成Array。而为了方便,我的输入强制要求是Vec。当然,一维的Vec很容易转化为&[T],但是Vec<Vec<T>>我一直没找到好的办法。

👇
ezlearning: https://github.com/rust-ndarray/ndarray/blob/master/README-quick-start.md

也许这个已经能满足你的需要了哈

ezlearning 2020-12-31 17:27

https://github.com/rust-ndarray/ndarray/blob/master/README-quick-start.md

也许这个已经能满足你的需要了哈

1 共 13 条评论, 1 页