maxtremblay / sparse-binary-matrix

A rust implementation of sparse binary matrix
2 stars 0 forks source link

Updating entry in place? #1

Open AWildSugar opened 3 years ago

AWildSugar commented 3 years ago

Hey nice package. I don't know if I'm missing something obvious but for my use case it'd be super helpful to have a simple function to update individual entries in place on a mutable matrix.

I'm coming from C++ and I have a simple sparse matrix there I use sometimes, I copied the logic over to rust. What do you think about something like this?

`

pub fn emplace_at(&mut self, row: usize, column: usize, val: BinaryNumber) -> Option<()> {                                          

    if (row + 1 >= self.row_ranges.len()) || (column >= self.number_of_columns) {                                                   
        return None;                                                                                                                
    }                                                                                                                               

    let start_ind = *self.row_ranges.get(row)?;                                                                                     
    let stop_ind = *self.row_ranges.get(row + 1)?;                                                                                  

    // If the row is nonempty, check if the location is already nonzero                                                             
    for i in start_ind..stop_ind {                                                                                                  
        let col_val = *self.column_indices.get(i)?;                                                                                 

        if col_val > column { break; }                                                                                              

        if col_val == column {                                                                                                      
            if val == 0 {                                                                                                           
                self.column_indices.remove(i);                                                                                      

                for j in (row+1)..self.row_ranges.len() {                                                                           
                    self.row_ranges[j] -= 1;                                                                                        
                }                                                                                                                   
            }                                                                                                                       

           return Some(());                                                                                                         
        }                                                                                                                           
    }                                                                                                                               

    // If we're here, the location is zero                                                                                          
    if val == 0 { return Some(()); }                                                                                                

    let mut i = start_ind;                                                                                                          
    while (i < stop_ind) && (self.column_indices[i] < column) { i += 1 };                                                           

    self.column_indices.insert(i, column);                                                                                          

    for j in (row+1)..self.row_ranges.len() {                                                                                       
        self.row_ranges[j] += 1;                                                                                                    
    }                                                                                                                               

    Some(())                                                                                                                        
}                                                                                                                                   

`

With a little test:

`

#[test]                                                                                                                             
fn simple_emplace_test() {                                                                                                          
    let mut mat = SparseBinMat::zeros(5, 5);                                                                                        

    assert_eq!(mat.emplace_at(1, 1, 1), Some(()));                                                                                  
    assert_eq!(mat.get(1, 1), Some(1));                                                                                             

    mat.emplace_at(1, 1, 0);                                                                                                        
    assert_eq!(mat.get(1, 1), Some(0));                                                                                             

    mat.emplace_at(3, 1, 1);                                                                                                        
    mat.emplace_at(4, 1, 1);                                                                                                        
    mat.emplace_at(3, 2, 1);                                                                                                        
    mat.emplace_at(4, 3, 1);                                                                                                        

    let mut iter = NonTrivialElements::new(&mat);                                                                                   

    assert_eq!(iter.next(), Some((3, 1)));                                                                                          
    assert_eq!(iter.next(), Some((3, 2)));                                                                                          
    assert_eq!(iter.next(), Some((4, 1)));                                                                                          
    assert_eq!(iter.next(), Some((4, 3)));                                                                                          

}

`

Obviously not super efficient but honestly not too bad either, I couldn't see an obvious way to construct it row by row or anything similar, anyways this atleast just gets you coding.

maxtremblay commented 3 years ago

Hey, thank you!

I can implement something like this. I think that your algorithm is roughly the best we can hope for since this is not a natural operation for sparse binary matrices.

However, out of curiosity, can you just add a second matrix with the value 1 at the positions you want to change to do the modifications? This is the way I am doing it right now in other projects using this one.

If this is not possible for you, I will also implement a constructor taking into account the capacity of the matrix. That is, the expected maximum of ones it will ever contain. That way, you won't have to pay for vector resizing to make room for new elements.

AWildSugar commented 3 years ago

Ohh that's how you're doing it, makes sense. That does work for me, though I think it might still be useful, or at least nice, to have a function that will set a position to value as opposed to toggling a set value.

Also just a suggestion: maybe add an example of constructing a matrix by adding unit matrices to the readme, just to help any future confused people.

Thanks!

maxtremblay commented 3 years ago

Yes, I don't have much time for this in the next few days, but I will give it a shot next week.

maxtremblay commented 3 years ago

This is now implemented. This required version 0.6.0 since I also implemented a wrapper for binary numbers. Normally, this should be mostly transparent because it implements From<u8> and vice-versa.

What you want is the emplace_at method.

AWildSugar commented 3 years ago

Cool, thanks