Re: How to instantiate `ptr object`

2020-05-19 Thread snej
> You can use closures or pass the data parameter as an input to the function.

In some sense closures and objects are isomorphic, as are functional and OOP. 
There’s a famous old _koan_ about this:

> The venerable master Qc Na was walking with his student, Anton. Hoping to 
> prompt the master into a discussion, Anton said “Master, I have heard that 
> objects are a very good thing — is this true?” Qc Na looked pityingly at his 
> student and replied, “Foolish pupil — objects are merely a poor man’s 
> closures.” 
> [(continues...)](https://citizen428.net/blog/of-closures-and-objects/)

—Jens


Re: How to instantiate `ptr object`

2020-05-19 Thread Stefan_Salewski

y: 1.2E3


Run

Thanks for that hint. Of course the book is in an very early state still, I 
wrote most of the two first chapters in April. But I hope and think that it can 
be already useful for beginners with nearly no CS knowledge. I have just 
yesterday applied a lot fixes provided by Jim Wilcoxson, mostly concerning 
English grammar. Current book content is the result of a fast flow directly 
from my head to the keyboard, so examples are untested and book content is not 
compared to the Nim manual in detail still. For reporting small issues you may 
use github at 
[https://github.com/StefanSalewski/NimProgrammingBook](https://github.com/StefanSalewski/NimProgrammingBook),
 or for a general discussion 
[https://forum.nim-lang.org/t/6170#38482](https://forum.nim-lang.org/t/6170#38482).


Re: How to instantiate `ptr object`

2020-05-18 Thread Araq
> For your example ARC makes a gigantic difference.

Yeah, known problem. Method dispatch got super slow for ARC. Not sure yet how 
to fix it. I can do it the old way without problems, but where is the fun in 
that. ;-) 


Re: How to instantiate `ptr object`

2020-05-18 Thread DIzer
2 Stefan_Salewski - is your book published or it is "in process" state? I saw 
some referred (in the text) mistypings like: 


var
  mean = 3.0 / 7.9
  x: float = 12
  y: 1.2E3


Run


Re: How to instantiate `ptr object`

2020-05-16 Thread mratsim
For machine learning in particular deep learning this is how I do it:

1\. I use inheritance, but only to be able to store the objects in the same 
container, **never for dispatch**. Example on neural network layer, called 
`Gate`, re-using the terminology from Andrej Karpathy's [Hacker's Guide to 
Neural Network](http://karpathy.github.io/neuralnets/)

[https://github.com/mratsim/Arraymancer/blob/5b24877b/src/autograd/autograd_common.nim#L72-L85](https://github.com/mratsim/Arraymancer/blob/5b24877b/src/autograd/autograd_common.nim#L72-L85)


type
  Gate*[TT] = ref object of RootObj # {.acyclic.}
## Base operator or layer. You can describe your custom operations or 
layers
## by inheriting from Gate and add a forward and optionally a backward 
method.
## Each operations should set the number of gradients produced during 
backpropagation.
## Additional fields specific to the operations like weights or inputs 
cache should be added too.
  
  PayloadKind* = enum
pkVar, pkSeq
  Payload*[TT] = object
case kind*: PayloadKind
of pkVar: variable*: Variable[TT]
of pkSeq: sequence*: seq[Variable[TT]]
  
  Backward*[TT] = proc(self: Gate[TT], payload: Payload[TT]): 
SmallDiffs[TT] {.nimcall.}


Run

2\. For dispatching I associate a compile-time proc for each kind of `Gate` I 
have. The `Gate` carry the state I need to pass. The compile-time proc just 
unwrap it and pass it to the 
_[real](https://forum.nim-lang.org/postActivity.xml#real) which has the proper 
signature.

For example for a MaxPool layer, the backward proc should have for signature. 
[https://github.com/mratsim/Arraymancer/blob/5b24877b/src/nn_primitives/nnp_maxpooling.nim#L70-L74](https://github.com/mratsim/Arraymancer/blob/5b24877b/src/nn_primitives/nnp_maxpooling.nim#L70-L74)
 So how to map it to `proc(self: Gate[TT], payload: Payload[TT]): 
SmallDiffs[TT] {.nimcall.}`?

The MaxPoolGate and backward shim does the trick 
[https://github.com/mratsim/Arraymancer/blob/5b24877b/src/nn/layers/maxpool2D.nim#L20-L46](https://github.com/mratsim/Arraymancer/blob/5b24877b/src/nn/layers/maxpool2D.nim#L20-L46),
 `maxpool2D_backward_ag` stands for autograd version


type MaxPool2DGate*[TT] {.final.} = ref object of Gate[TT]
  cached_input_shape: MetadataArray
  cached_max_indices: Tensor[int]
  kernel, padding, stride: Size2D

proc maxpool2D_backward_ag[TT](self: MaxPool2DGate[TT], payload: 
Payload[TT]): SmallDiffs[TT] =
  let gradient = payload.variable.grad
  result = newDiffs[TT](1)
  result[0] = maxpool2d_backward(
self.cached_input_shape,
self.cached_max_indices,
gradient
  )


Run

So I get compile-time dispatch on arbitrary state with a fixed interface. And 
even though the wrapper proc does not much, 2 static function calls are always 
faster than a closure or a method.

Other example, convolution, this may seem more complex as the backward 
operation has a lot of inputs: 
[https://github.com/mratsim/Arraymancer/blob/5b24877b/src/nn_primitives/nnp_convolution.nim#L65-L70](https://github.com/mratsim/Arraymancer/blob/5b24877b/src/nn_primitives/nnp_convolution.nim#L65-L70)


proc conv2d_backward*[T](input, weight, bias: Tensor[T],
 padding: Size2D,
 stride: Size2D,
 grad_output: Tensor[T],
 grad_input, grad_weight, grad_bias: var Tensor[T],
 algorithm = Conv2DAlgorithm.Im2ColGEMM)


Run

But it's actually straightforward as well


type Conv2DGate*[TT]{.final.} = ref object of Gate[TT]
  cached_input: Variable[TT]
  weight, bias: Variable[TT]
  padding, stride: Size2D
  # TODO: store the algorithm (NNPACK / im2col)

proc conv2d_backward_ag[TT](self: Conv2DGate[TT], payload: Payload[TT]): 
SmallDiffs[TT] =
  let gradient = payload.variable.grad
  if self.bias.isNil:
result = newDiffs[TT](2)
  else:
result = newDiffs[TT](3)
  conv2d_backward(
self.cached_input.value,
self.weight.value, self.bias.value,
self.padding, self.stride,
gradient,
result[0], result[1], result[2]
  )


Run


Re: How to instantiate `ptr object`

2020-05-15 Thread dataPulverizer
@mratsim Thank you for your detailed and informative response, also I didn't 
know about the do statement in Nim that's something else new for me. My main 
takeaway is that I don't always have to use runtime resolution, especially 
because the kind of code I write is mostly statistics/machine learning - type 
algorithms where performance is important and virtual method calls (dynamic 
dispatch) has an adverse impact.

To be clear the example you gave using AbstractKernel[T] is resolved at compile 
time rather than at run time, AbstractKernel[T] is essentially a function 
pointer whose type (along with the matrix) is known at compile time. I guess 
OOP-based polymorphism is one way to provide a common interface to function 
dispatch and parametric polymorphism is another - which has no performance 
penalties but can not be used in instances where type resolution at compile 
time is not available, but in instances where this is not the case it pays to 
use parametric polymorphism.


Re: How to instantiate `ptr object`

2020-05-14 Thread Stefan_Salewski
For your example ARC makes a gigantic difference. (I put your global code in a 
main proc)


import times

type FooBase = ref object {.inheritable.}
  dummy: int

type Foo{.final.} = ref object of FooBase
  value : float32

proc inplace_add_proc(x: var Foo, a: float32) =
  x.value += a

proc inplace_add_closure(x: var float32, a: float32) =
  proc add_closure(v: var float32) = v += a
  add_closure(x)

method inplace_add_method(x: FooBase, a: float32) {.base.} =
  discard

method inplace_add_method(x: Foo, a: float32) =
  x.value += a

proc main =
  
  var bar : Foo
  new bar
  var start = cpuTime()
  for i in 0..<1:
inplace_add_proc(bar, 1.0f)
  echo " Proc with ref object ", cpuTime() - start
  
  var x : float32
  start = cpuTime()
  for i in 0..<1:
inplace_add_closure(x, 1.0f)
  echo " Closures ", cpuTime() - start
  
  var baz : Foo
  new baz
  start = cpuTime()
  for i in 0..<1:
inplace_add_method(baz, 1.0f)
  echo " Methods ", cpuTime() - start

main()


Run


$ nim c -r -d:danger t3.nim
 Proc with ref object 0.117909274
 Closures 1.719076293
 Methods 0.264790297999


Run


$ nim c -r -d:danger --gc:arc t3.nim
 Proc with ref object 0.118106775
 Closures 0.941439717001
 Methods 3.822302076


Run


Re: How to instantiate `ptr object`

2020-05-14 Thread mratsim
You can use closures or pass the data parameter as an input to the function.

Here is an example with closures


type
  AbstractKernel[T] = proc(dst: var T, src: Matrix[T], i, j: int) 
{.closure.}
  Matrix[T] = object
dim: array[2, int]
data: seq[T]

func newMatrix(rows, cols: int, T: typedesc): Matrix[T] =
  result.dim[0] = rows
  result.dim[1] = cols
  result.data.newSeq(rows * cols)

func ncols(m: Matrix): int {.inline.} =
  m.dim[1]

func nrows(m: Matrix): int {.inline.} =
  m.dim[0]

template `[]`(m: Matrix, i, j: int): auto =
  m.data[i * m.dim[1] + j]

template `[]=`[T](m: var Matrix[T], i, j: int, val: T) =
  m.data[i * m.dim[1] + j] = val

proc calculateKernelMatrix*[T](m: Matrix[T], kernel: AbstractKernel[T]): 
Matrix[T] =
  result = newMatrix(m.nrows, m.ncols, T);
  for i in 0 ..< m.nrows:
for j in 0 ..< m.ncols:
  kernel(result[i, j], m, i, j);

let M = newMatrix(3, 3, int)
echo M # (dim: [3, 3], data: @[0, 0, 0, 0, 0, 0, 0, 0, 0])

let N = M.calculateKernelMatrix do (dst: var int, src: Matrix[int], i, j: 
int):
let state = 100
dst = state * i + j
echo N # (dim: [3, 3], data: @[0, 1, 2, 100, 101, 102, 200, 201, 202])

let K = N.calculateKernelMatrix do (dst: var int, src: Matrix[int], i, j: 
int):
let state = -1
dst = src[i, j] * state
echo K # (dim: [3, 3], data: @[0, -1, -2, -100, -101, -102, -200, -201, 
-202])


Run

`state` can be anything beyond a simple int.


Re: How to instantiate `ptr object`

2020-05-14 Thread dataPulverizer
> No you don't, you can have AbstractKernel be a procNo you don't, you can have 
> AbstractKernel be a proc. 
> 
> 
> type AbstractKernel[F] = proc(loc: var F) {.nimcall.}
> 
> 
> Run

I don't understand and have a couple of questions. Firstly, what would be the 
syntax for other kernel functions that inherit? Secondly, the DotKernel 
function is the only function whose type doesn't carry data, the vast majority 
of the other kernel functions do, for example:


Gaussian*[T] = ref object of AbstractKernel
gamma: T


Run

Other kernels have different number (and names) of data members so how would 
you represent that with your function types?

Thanks


Re: How to instantiate `ptr object`

2020-05-14 Thread mratsim
> At compile time yes you could just use procs but you need polymorphism for 
> runtime dispatch.

No you don't, you can have AbstractKernel be a proc


type AbstractKernel[F] = proc(loc: var F) {.nimcall.}


Run


Re: How to instantiate `ptr object`

2020-05-13 Thread dataPulverizer
One of the things that intrigued me about Nim was it's object system, you could 
create value, reference and pointer types with/without inheritance. As I 
understand it, an important distinction between ptr and ref classes is that ref 
classes are managed by the garbage collector while ptr classes have to be 
manually allocated and deallocated.

In this case for me that _is_ the distinction between both of them, I see 
inheritance from a ptr class as the same as inheritance from a ref class and 
polymorphism in the same way:


type:
  Animal = ptr object of RootObj
  Cat = ptr object of Animal

var x: Animal = create(Cat)


Run

But I also want to be able to dispatch against Cat (x) at runtime, so send it 
as a parameter in a function marked with Animal but within that function call 
the cat method and allow the right method to be dispatched. In exactly the same 
way as in my kernel function:


proc calculateKernelMatrix*(K: AbstractKernel, data: Matrix[F]): Matrix[F] =
  let n = int64(ncol(data));
  var mat = Matrix[F](data: newSeq[F](n*n), dim: @[n, n]);
  for j in 0..

Re: How to instantiate `ptr object`

2020-05-13 Thread mratsim
The allure of undocumented behaviors ;)

@dataPulverizer, if you already use pointers you can use generic procs, why do 
you need inheritance for dispatch?


Re: How to instantiate `ptr object`

2020-05-13 Thread Araq
> you can't mix inheritance with un-managed raw pointers.

I can do it. In fact, I did in Nim's allocator.


Re: How to instantiate `ptr object`

2020-05-13 Thread Araq
> you can't mix inheritance with un-managed raw pointers.

I can do it. In fact, I did in Nim's allocator.


Re: How to instantiate `ptr object`

2020-05-13 Thread dataPulverizer
Isn't the point of inheritance method dispatch? Why isn't that a separate issue 
from objects that are dissociated from the garbage collector?


Re: How to instantiate `ptr object`

2020-05-13 Thread Stefan_Salewski
> you can't mix inheritance with un-managed raw pointers

That is an interesting point, I was wondering about it this morning already 
when I wrote my first reply.

I am still not sure. I could generally imagine that inheritance can work with 
with un-managed raw pointers. But maybe not in Nim?

@dataPulverizer note that create() may be easier to use, as with create() no 
cast is necessary.

@snej The book is in a very early stage still, I wrote the two chapters in 
April. And it is mostly for people with very less programming experience.


Re: How to instantiate `ptr object`

2020-05-13 Thread mratsim
`ptr object of RootObj` seems very strange, you can't mix inheritance with 
un-managed raw pointers.


Re: How to instantiate `ptr object`

2020-05-13 Thread snej
I wasn’t aware of that book, though I’ve been guzzling from the 
Nim-documentation firehose for a week now. You should get it linked from the 
Nim-lang “learning” page!


Re: How to instantiate `ptr object`

2020-05-13 Thread dataPulverizer
I just wasn't sure whether when allocating for objects you have to account for 
any "gubbings", just cautious I guess:


type
  MyTypeA = ptr object of RootObj
x: int64
  MyTypeB = ptr object of RootObj
x: array[0..4, int64]


var x0 = alloc(sizeof(int64));
var x1 = cast[MyTypeA](x0);
x1.x = 42;

echo "x: ", x1.x;
dealloc(x1); # or dealloc(x0)

var y0 = alloc(sizeof(int64)*5);
var y1 = cast[MyTypeB](y0);

y1.x = [1'i64, 2, 3, 4, 5];
echo "y1: ", y1.x;
dealloc(y1); # or dealloc(y0)


Run

I've now added your book to my Nim reading list. Thanks


Re: How to instantiate `ptr object`

2020-05-12 Thread Stefan_Salewski
That is also covered in my book in detail. Have you starting reading?

[http://ssalewski.de/nimprogramming.html#_allocating_objects](http://ssalewski.de/nimprogramming.html#_allocating_objects)

alloc(), create(), dealloc().