Resizing a domain by an array of records

I have a hypergraph data structure with two arrays, one for edges and one for vertices (similar to a bipartite graph). I have a problem resizing arrays, so I tried a simplified example:

ar dom = {0..0}; var arr: [dom] int; writeln(arr); dom = {0..#2}; writeln(arr); dom = {0..#1}; writeln(arr); record Vertex {} record Edge {} record Wrapper { type nodeType; type idType; var id: idType; } record NodeData { type nodeIdType; var ndom = {0..-1}; var neighborList: [ndom] nodeIdType; proc numNeighbors() return ndom.numIndices; var lock$: sync bool = true; // This method is not parallel-safe proc addNodes(vals) { lock$; // acquire lock neighborList.push_back(vals); lock$ = true; // release the lock } proc readWriteThis(f) { f <~> new ioLiteral("{ ndom = ") <~> ndom <~> new ioLiteral(", neighborlist = ") <~> neighborList <~> new ioLiteral(", lock$ = ") <~> lock$.readFF() <~> new ioLiteral(" }"); } } type vType = Wrapper(Vertex, int); type eType = Wrapper(Edge, int); var dom1 = {0..0}; var arr1: [dom1] NodeData(eType); writeln(arr1); dom1 = {0..#2}; writeln(arr1); dom1 = {0..#1}; writeln(arr1); 

When I try to run this code, it hangs with the following output:

 $ ./resize -nl 1 salloc: Granted job allocation 15015 0 0 0 0 { ndom = {0..-1}, neighborlist = , lock$ = true } 

Thus, resizing an array of integers works fine, but I can't resize an array of records. What am I doing wrong?

As a side note, when I try to resize domains in my full code, I see that the domains are changing, but my arrays that use domains do not change at all. At least the code does not freeze.

EDIT

I tried another example that really better illuminates my original problem:

 class Test { var dom; var ints: [dom] int; proc resize(size) { dom = {dom.low..size}; } } var test = new Test(dom = {0..-1}); writeln(test); test.resize(1); writeln(test); 

Here is the result that I see:

 $ ./resize -nl 1 salloc: Granted job allocation 15038 {dom = {0..-1}, ints = } {dom = {0..1}, ints = } salloc: Relinquishing job allocation 15038 

So my problem is that the resize method is useless. It changes the domain, but it does not change the array of elements.

+5
source share
2 answers

In response to the problem you see in the EDIT example:

I am afraid that you will find yourself in the dark corner of the compiler from version 1.17, and I am sorry that it exists, although I think we can get you out of it.

Starting from some background and important context: from the very beginning, Chapel supported class and record constructors (e.g. proc C(...) for class C ), but they were naive in design, in particular wrt common classes and records. Over the past few releases, we have moved from constructors to initializers (such as proc init(..) for class C ) to remove these limitations.

To date, version 1.17, initializers are in pretty good shape (for example, now I use them for all the new code that I write, and rarely swear at them), but if you did not provide either an initializer or a constructor (as in your examples ), the compiler will create a default constructor (rather than a default initializer) and thus may encounter some of these long-standing problems. For version 1.18, the goal is for the compiler to create default initializers and completely condemn constructors.

So, here are some ways to get around the problem for your smaller test program in EDIT, all of which seem to give the correct result for me in version 1.17 of Chapel:

1) Make the class less general. Here I gave the dom field an initial value, so that the compiler can determine its type, and this seems to help it with the default constructor, sufficient to generate the expected result:

 class Test { var dom = {0..-1}; var ints: [dom] int; proc resize(size) { dom = {dom.low..size}; } } var test = new Test(dom = {0..-1}); writeln(test); test.resize(1); writeln(test); 

2) Write an explicit initializer. Here I leave dom generic, but create an initializer that assigns it to match the signature of your new call:

 class Test { var dom; var ints: [dom] int; proc init(dom: domain(1)) { this.dom = dom; } proc resize(size) { dom = {dom.low..size}; } } var test = new Test(dom = {0..-1}); writeln(test); test.resize(1); writeln(test); 

3) (last resort) Request the compiler to create a default initializer (and not a standard constructor) for you. This approach is really not intended for end users, it will not work for all cases and will go away in the future, but it may be convenient to know about it. Here I am attaching a pragma to the class to tell the compiler to create a default initializer, and not a default constructor. Although the default compiler does not create default initializers, for many classes and records, it can, if you ask, and this is one of them:

 pragma "use default init" class Test { var dom; var ints: [dom] int; proc resize(size) { dom = {dom.low..size}; } } var test = new Test(dom = {0..-1}); writeln(test); test.resize(1); writeln(test); 

In the interests of space, I considered only your shorter example, and not your longer one, but I hope that these methods will also help with it (and I will gladly spend more time on a longer one, if necessary).

+4
source

The following is the answer related to the problem you reported viewing in your original example (as opposed to the problem in your EDIT example, separately):

The reason your source code fails is due to the default assignment that Chapel creates for the records. In particular, given the record R :

 record R { var x: t; var y: t2; var z: t3; } 

if you did not create an assignment between R s, the compiler will create one of the general forms:

 proc =(ref lhs: R, rhs: R) { lhs.x = rhs.x; lhs.y = rhs.y; lhs.z = rhs.z; } 

In your example, this causes problems, because currently (like Chapel 1.17.0) when re-distributing an array, its elements are first built / initialized by default and then assigned. Thus, the synchronized field lock$ gets initialized by default to true , and then when the elements of the array are copied, the assignment operator tries to reassign this field, but it is already filled.

One solution that apparently allowed your code to compile and run for me is to implement its own assignment function so that it does not touch the lock$ field:

 proc =(ref lhs: NodeData(?t), rhs: NodeData(t)) { lhs.ndom = rhs.ndom; lhs.neighborList = rhs.neighborList; // let not touch lhs.lock$ } 

But you should obviously consider whether this will be the right blocking discipline for your use case. If you think that you need to change something here wrt semantics of initialization / assignment wrt array size, please write Chapel GitHub problem against it .

+3
source

Source: https://habr.com/ru/post/1276275/


All Articles