Boost logo

Boost :

From: Joerg Walter (jhr.walter_at_[hidden])
Date: 2002-06-29 07:13:35


----- Original Message -----
From: "Marc Duflot" <m.duflot_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Thursday, June 27, 2002 4:17 PM
Subject: [boost] uBLAS: problem with sparse_vector

> There is a bug in uBLAS that is illustrated with the following code:
>
> #include <iostream>
> #include <boost/numeric/ublas/vector_sp.h>
> #include <boost/numeric/ublas/io.h>
>
> int main () {
> numerics::sparse_vector<double> v (3);
> v(1) = v(0) = 42;
> std::cout << v << std::endl;
> }
>
> The output of the program is (with g++ 2.95.2 and g++ 3.0.4 on Linux)
> [3](42,0,0)
>
> The element v(1) is 0 instead of 42. The bug comes from the fact that v(1)
is
> evaluated before v(0) = 42. The bug is not present with the Intel compiler
on
> the same platform.
>
> Note: there is the same problem with Loki::AssocVector.
>
> I think that this feature must be documented (or corrected if it is
possible)
> before uBLAS is accepted into boost.

I've just tried to fix this problem following Dave Abraham's hint (thanks,
Dave!). First of all I extended your sample to check std::map, map_array, ,
sparse_vector<..., std::map>, sparse_vector<..., map_array> and realized
this way, that the real problem was the semantic difference of std::map and
map_array.

Then I embedded the following nested proxy class in map_array:

        class proxy:
            public container_reference<map_array> {
        public:
            typedef typename map_array::index_type index_type;
            typedef typename map_array::data_value_type data_value_type;
            typedef const typename map_array::data_value_type
&data_const_reference;
            typedef typename map_array::data_value_type &data_reference;
            typedef std::pair<index_type, data_value_type> *pointer;

            // Construction and destruction
            proxy (map_array &a, pointer it):
                container_reference<map_array> (a), it_ (it), i_
(it->first), d_ (it->second) {
            }
            proxy (map_array &a, index_type i):
                container_reference<map_array> (a), i_ (i), d_ () {
                it_ = (*this) ().find (i_);
                if (it_ == (*this) ().end ())
                    it_ = (*this) ().insert ((*this) ().end (), value_type
(i_, d_));
                d_ = it_->second;
            }
            ~proxy () {
                if (it_->first != i_)
                    it_ = (*this) ().find (i_);
                it_->second = d_;
            }

            // Assignment
            template<class D>
            proxy &operator = (const D &d) {
                d_ = d;
                return *this;
            }
            proxy &operator = (const proxy &p) {
                d_ = p.d_;
                return *this;
            }
            template<class D>
            proxy &operator += (const D &d) {
                d_ += d;
                return *this;
            }
            proxy &operator += (const proxy &p) {
                d_ += p.d_;
                return *this;
            }
            template<class D>
            proxy &operator -= (const D &d) {
                d_ -= d;
                return *this;
            }
            proxy &operator -= (const proxy &p) {
                d_ -= p.d_;
                return *this;
            }
            template<class D>
            proxy &operator *= (const D &d) {
                d_ *= d;
                return *this;
            }
            proxy &operator *= (const proxy &p) {
                d_ *= p.d_;
                return *this;
            }
            template<class D>
            proxy &operator /= (const D &d) {
                d_ /= d;
                return *this;
            }
            proxy &operator /= (const proxy &p) {
                d_ /= p.d_;
                return *this;
            }

            // Conversion
            operator data_reference () {
                return d_;
            }

        private:
            pointer it_;
            index_type i_;
            data_value_type d_;
        };

and changed typedefs

        class proxy;
        typedef proxy data_reference;

and subscription operator

        // Element access
        NUMERICS_INLINE
        data_reference operator [] (index_type i) {
            // This fixes a [1] = a [0] = 1.
            // Thanks to Marc Duflot for spotting this.
            return data_reference (*this, i);
        }

With this changes, the test for map_array seemed to work (compiled with GCC
3.1), but: the interfaces of std::map and map_array are slightly different
now. So I
had to add the following map_traits

    template<class A>
    class map_traits {
        typedef void proxy;
    };
    template<class I, class T>
    class map_traits<std::map<I, T> > {
        typedef typename std::map<I, T>::data_type &proxy;
    };
    template<class I, class T>
    class map_traits<map_array<I, T> > {
        typedef typename map_array<I, T>::data_reference proxy;
    };

which use PTS (so the patch won't work on broken compilers) and then to
change in sparse_vector and sparse_matrix typedefs

        typedef typename map_traits<A>::proxy reference;

and mutable iterator dereference

            // Dereference
            NUMERICS_INLINE
            reference operator * () const {
                return reference ((*this) ().data (), it_);
            }

> Sorry if it is indeed documented somewhere or if it is a known bug.

May be, it would have been easier to only document that bug ;-)

Thanks for your feedback

Joerg


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk