4

I am using gonum to perform a few linear algebra calculations. After extending the original mat.VecDense struct I am getting a "bad region: identical" panic when applying a method on itself. This error does not occur when I am using the original setup gonum provides.

Here is my implementation:

type Vector struct {
    mat.VecDense
}
func NewVector(n int, data []float64) *Vector {
    return &Vector{*mat.NewVecDense(n, data)}
}

I am testing it using the following snippet:

func main() {
    u, v := mat.NewVecDense(3, []float64{1, 2, 3}), mat.NewVecDense(3, []float64{4, 5, 6})
    fmt.Printf("[U - NewVecDense]\tADDRESS: %v, VALUE: %v\n", &u, u)
    fmt.Printf("[V - NewVecDense]\tADDRESS: %v, VALUE: %v\n", &v, v)
    u.AddVec(u, v)

    fmt.Println("-------------------------")
    x, y := NewVector(3, []float64{1, 2, 3}), NewVector(3, []float64{4, 5, 6})
    fmt.Printf("[X - NewVector]\tADDRESS: %v, VALUE: %v\n", &x, x)
    fmt.Printf("[Y - NewVector]\tADDRESS: %v, VALUE: %v\n", &y, y)
    x.AddVec(x, y)
    fmt.Println(x)
}

While the first addition executes fine, the second fails:

[U - NewVecDense]   ADDRESS: 0xc42000c028, VALUE: &{{[1 2 3] 1} 3}
[V - NewVecDense]   ADDRESS: 0xc42000c030, VALUE: &{{[4 5 6] 1} 3}
-------------------------
[X - NewVector] ADDRESS: 0xc42000c040, VALUE: &{{{[1 2 3] 1} 3}}
[Y - NewVector] ADDRESS: 0xc42000c048, VALUE: &{{{[4 5 6] 1} 3}}
panic: mat: bad region: identical

AddVec is a method implemented by gonum:

func (v *VecDense) AddVec(a, b Vector)

Why is this happening, hand what is the right way of implementing this?


Edit:

Thanks to @Himanshu I managed to solve the problem.

I created pass-through methods for each method I am using, passing the right level of the struct through:

type Vector struct {
    *mat.VecDense
}

func NewVector(n int, data []float64) Vector {
    return Vector{mat.NewVecDense(n, data)}
}

func (v *Vector) AddVec(a, b Vector) {
    v.VecDense.AddVec(a.VecDense, b.VecDense)
}

func (v *Vector) SubVec(a, b Vector) {
    v.VecDense.SubVec(a.VecDense, b.VecDense)
}

func (v *Vector) ScaleVec(alpha float64, a Vector) {
    v.VecDense.ScaleVec(alpha, a.VecDense)
}

func (v *Vector) AddScaledVec(a Vector, alpha float64, b Vector) {
    v.VecDense.AddScaledVec(a.VecDense, alpha, b.VecDense)
}

In addition - I am not sure if this is the right approach or not - I have also changed the return type for NewVector from pointer to value, since it holds a pointer to the mat.VecDense anyway. Note that *mat.VecDense satisfies the Vector interface from gonum, so passing this internal field on to the methods worked fine, as the example above shows.

Himanshu
  • 12,071
  • 7
  • 46
  • 61
snufkin
  • 43
  • 3
  • Please post the code for `AddVec` function where you are passing the Vector struct. – Himanshu Aug 21 '18 at 14:27
  • `AddVec` is implemented by gonum: https://github.com/gonum/gonum/blob/master/mat/vector.go#L333. I'll update the main post with this refernece. – snufkin Aug 21 '18 at 14:30
  • Probably I am doing something very dumb. However, following your pointers I got to the following solution, which actually works: `func (v *Vector) AddVec(a *Vector, b *Vector) { v.VecDense.AddVec(&a.VecDense, &b.VecDense) } ` – snufkin Aug 21 '18 at 15:01
  • This is what I have provided in the example to use the function with embedded struct and using pointer type arguments to `AddVec`. Please check my answer and the link. – Himanshu Aug 21 '18 at 15:03
  • Now what you changed is you are passing a pointer to the struct which I have edited that the argument should be of pointer to `Vector`. – Himanshu Aug 21 '18 at 15:09
  • Methods on embedded structs are promoted, unless overwritten on the parent struct. Also note that there is a difference between my `Vector` definition (which is a struct embedding VecDense), and the `Vector` interface, which the gonum `AddVec` expects. – snufkin Aug 21 '18 at 15:09
  • It looks to me like to are trying to fit Go into a class-based like solution. Why are you trying to extend `*VecDense`? The appropriate approach would be to add functions that take the types as parameters. – kortschak Nov 06 '18 at 12:04
  • @kortschak there was no particular concept in doing so, I am learning Golang and perhaps my OO experience has skewed my approach. My goal was to have a linear algebra package, that we can internally use, and that builds on top of gonum. – snufkin Nov 09 '18 at 09:34

2 Answers2

1

In Golang it is described for promoted methods as

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

Given a struct type S and a defined type T, promoted methods are included in the method set of the struct as follows:

  • If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
  • If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

The problem is that you are passing pointer type arguments to AddVec function. But you are using pointer type fields in second case.

func (v *VecDense) AddVec(a, b Vector)

One more thing to notice is that AddVec has value type arguments of Vector struct but you are passing pointer to Vector fields as

x, y := NewVector(3, []float64{1, 2, 3}), NewVector(3, []float64{4, 5, 6}) 

In above code x,y are pointer type returned from NewVector

x.AddVec(x, y)
Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • Thank you for the suggestion, but if the type were incompatible, would the code not even compile? `Vector` embeds the `VecDense` struct, so I can use the methods of the original struct. – snufkin Aug 21 '18 at 14:40
  • @BalazsDianiska try to embed the struct with an example and check if you can do that or not. you can but by calling the name of the parent embedded struct. – Himanshu Aug 21 '18 at 14:43
  • thanks for the tip, unfortunately the issue remains. – snufkin Aug 21 '18 at 14:50
  • @BalazsDianiska I have noticed one more thing which is that `NewVector` returns pointer while the arguments to `AddVec` is of value type. – Himanshu Aug 21 '18 at 14:53
  • @BalazsDianiska yes that's why I have changed the answer and edited that the problem was when passing the arguments to the `AddVec` method not with the receiver. I know the method of embedded struct is promted. – Himanshu Aug 21 '18 at 15:11
0

The problem is that the pointers are compared when doing shadow detection. This is a bug IMO. I just filed https://github.com/gonum/gonum/issues/945

Brendan Tracey
  • 656
  • 1
  • 5
  • 5