Domains¶
A domain is a first-class representation of an index set. Domains are used to specify iteration spaces, to define the size and shape of arrays (Arrays), and to specify aggregate operations like slicing. A domain can specify a single- or multi-dimensional rectangular iteration space or represent a set of indices of a given type. Domains can also represent a subset of another domain’s index set, using either a dense or sparse representation. A domain’s indices may potentially be distributed across multiple locales as described in Distributions, thus supporting global-view data structures.
In the next subsection, we introduce the key characteristics of domains. In Base Domain Types and Values, we discuss the types and values that can be associated with a base domain. In Simple Subdomain Types and Values, we discuss the types and values of simple subdomains that can be created from those base domains. In Sparse Subdomain Types and Values, we discuss the types and values of sparse subdomains. The remaining sections describe the important manipulations that can be performed with domains, as well as the predefined operators and functions defined for domains.
Domain Overview¶
There are three kinds of domain, distinguished by their subset dependencies: base domains, subdomains and sparse subdomains. A base domain describes an index set spanning one or more dimensions. A subdomain creates an index set that is a subset of the indices in a base domain or another subdomain. Sparse subdomains are subdomains which can represent sparse index subsets efficiently. Simple subdomains are subdomains that are not sparse. These relationships can be represented as follows:
domain-type:
base-domain-type
simple-subdomain-type
sparse-subdomain-type
Domains can be further classified according to whether they are regular or irregular. A regular domain represents a rectangular iteration space and can have a compact representation whose size is independent of the number of indices. Rectangular domains, with the exception of sparse subdomains, are regular.
An irregular domain can store an arbitrary set of indices of an arbitrary but homogeneous index type. Irregular domains typically require space proportional to the number of indices being represented. All associative domain types and their subdomains (including sparse subdomains) are irregular. Sparse subdomains of regular domains are also irregular.
An index set can be either ordered or unordered depending on whether its members have a well-defined order relationship. All regular domains are ordered. All associative domains are unordered.
The type of a domain describes how a domain is represented and the operations that can be performed upon it, while its value is the set of indices it represents. In addition to storing a value, each domain variable has an identity that distinguishes it from other domains that may have the same type and value. This identity is used to define the domain’s relationship with subdomains, index types (Domain Index Types), and arrays (Association of Arrays to Domains).
The runtime representation of a domain is controlled by its distribution, see Distributions.
Parallel Safety with respect to Domains (and Arrays)¶
Users must take care when applying operations to arrays and domains concurrently from distinct tasks. For instance, if one task is modifying the index set of a domain while another task is operating on either the domain itself or an array declared over that domain, this represents a race and could have arbitrary consequences including incorrect results and program crashes. While making domains and arrays safe with respect to such concurrent operations would be appealing, Chapel’s current position is that such safety guarantees would be prohibitively expensive.
Chapel arrays do support concurrent reads, writes, iterations, and operations as long as their domains are not being modified simultaneously. Such operations are subject to Chapel’s memory consistency model like any other memory accesses. Similarly, tasks may make concurrent queries and iterations on a domain as long as another task is not simultaneously modifying the domain’s index set.
An associative domain may permit multiple tasks to modify its index
set concurrently in a parallel-safe manner if its type is declared with
parSafe=true
. The following example demonstrates how to create a
parallel-safe associative domain of strings:
var D: domain(string, parSafe=true);
Note that declaring a domain with parSafe=true
adds some amount of overhead
to many domain operations. This is because such a domain uses locking on the
underlying data structure each time the domain is modified. This overhead is
unnecessary, for example, when the domain is operated upon only by a single
task, in which case it can be avoided by declaring the domain’s type with
parSafe=false
or without an explicit parSafe
setting.
Note that the parSafe=true
mode is currently unstable for associative
domains. Its availability and behavior may change in the future.
As with any other domain type, it is not safe to access an
associative array while its domain is changing, regardless of
whether parSafe
is set to true
or false
.
Base Domain Types and Values¶
Base domain types can be classified as regular or irregular. Dense and strided rectangular domains are regular domains. Irregular base domain types include all of the associative domain types.
base-domain-type:
rectangular-domain-type
associative-domain-type
These base domain types are discussed in turn in the following subsections.
The keyword domain
, when not followed by parentheses, refers to
a generic type that can be instantiated with any domain type.
This type may also be written as domain(?)
.
Rectangular Domains¶
Rectangular domains describe multidimensional rectangular index sets. They are characterized by a tensor product of ranges and represent indices that are tuples of an integral type. Because their index sets can be represented using ranges, regular domain values typically require only \(O(1)\) space.
Rectangular Domain Types¶
Rectangular domain types are parameterized by three things:
rank
a positiveint
value indicating the number of dimensions that the domain represents;idxType
a type member representing the index type for each dimension; andstrides
a parameter of the typestrideKind
defining what strides are allowed in each dimension.
If rank
is \(1\), the index type represented by a rectangular
domain is idxType
. Otherwise, the index type is the homogeneous
tuple type rank*idxType
. If unspecified, idxType
defaults to
int
and strides
defaults to strideKind.one
.
Open issue.
We may represent a rectangular domain’s index type as rank*idxType even if rank is 1. This would eliminate a lot of code currently used to support the special (rank == 1) case.
The syntax of a rectangular domain type is summarized as follows:
rectangular-domain-type:
'domain' ( named-expression-list )
where named-expression-list
allows specifying the values of rank
,
idxType
, and strides
.
Example (typeFunctionDomain.chpl).
The following declarations both create an uninitialized rectangular domain with three dimensions, with
int
indices:var D1 : domain(rank=3, idxType=int, strides=strideKind.one); var D2 : domain(3);
Rectangular Domain Values¶
Each dimension of a rectangular domain d
is a range of type
range(d.idxType, boundKind.both, d.strides)
. The index set
for a rank 1 domain is the set of indices described by its singleton
range. The index set for a rank \(n\) domain is the set of all
n*idxType
tuples described by the tensor product of its ranges. When
expanded (as by an iterator), rectangular domain indices are ordered
according to the lexicographic order of their values. That is, the index
with the highest rank is listed first and changes most slowly.
This is also known as row-major ordering.
Note
Future
Domains defined using unbounded ranges may be supported.
Literal rectangular domain values are represented by a comma-separated
list of range expressions of matching idxType
enclosed in curly
braces:
rectangular-domain-literal:
{ range-expression-list }
range-expression-list:
range-expression
range-expression, range-expression-list
The type of a rectangular domain literal is defined as follows:
rank
= the number of range expressions in the literal;idxType
= the type of the range expressions;strides
= the most narrowstrideKind
that can represent allstrides
parameters of the range expressions.
If the index types in the ranges differ and all of them can be promoted
to the same type, then that type is used as the idxType
. Otherwise,
the domain literal is invalid.
Example.
The expression
{1..5, 1..5}
defines a rectangular domain with typedomain(rank=2,
idxType=int,
strides=strideKind.one)
. It is a \(5 \times 5\) domain with the indices:\[(1, 1), (1, 2), \ldots, (1, 5), (2, 1), \ldots (5, 5).\]
A domain expression may contain bounds which are evaluated at runtime.
Example.
In the code
var D: domain(2) = {1..n, 1..n};
D
is defined as a two-dimensional rectangular domain with an index type of2*int
and is initialized to contain the set of indices \((i,j)\) for all \(i\) and \(j\) such that \(i \in {1, 2, \ldots, n}\) and \(j \in {1, 2, \ldots, n}\).
The default value of a domain type is the rank
default range values
for type:
range(idxType, boundKind.both, strides)
Example (rectangularDomain.chpl).
The following creates a two-dimensional rectangular domain and then uses this to declare an array. The array indices are iterated over using the domain’s
dim()
method, and each element is filled with some value. Then the array is printed out.Thus, the code
var D : domain(2) = {1..2, 1..7}; var A : [D] int; for i in D.dim(0) do for j in D.dim(1) do A[i,j] = 7 * i**2 + j; writeln(A);produces
8 9 10 11 12 13 14 29 30 31 32 33 34 35
Associative Domains¶
Associative domains represent an arbitrary set of indices of a given
type and can be used to describe sets or to create dictionary-style
arrays (hash tables). The type of indices of an associative domain, or
its idxType
, can be any primitive type except void
or any class
type.
Associative Domain Types¶
An associative domain type is parameterized by idxType
, the type of
the indices that it stores. The syntax is as follows:
associative-domain-type:
'domain' ( associative-index-type )
associative-index-type:
type-expression
The associative-index-type
determines the idxType
of the
associative domain type.
When an associative domain is used as the index set of an array, the relation between the indices and the array elements can be thought of as a map between the values of the index set and the elements stored in the array.
Associative Domain Values¶
An associative domain’s value is simply the set of all index values that the domain describes. The iteration order over the indices of an associative domain is undefined.
Specification of an associative domain literal value follows a similar syntax as rectangular domain literal values. What differentiates the two are the types of expressions specified in the comma separated list. Use of values of a type other than ranges will result in the construction of an associative domain.
associative-domain-literal:
{ associative-expression-list }
associative-expression-list:
non-range-expression
non-range-expression, associative-expression-list
non-range-expression:
expression
It is required that the types of the values used in constructing an associative domain literal value be of the same type. If the types of the indices does not match a compiler error will be issued.
Note
Future
Due to implementation of
==
over arrays it is currently not possible to use arrays as indices within an associative domain.
Example (associativeDomain.chpl).
The following example illustrates construction of an associative domain containing string indices “bar” and “foo”. Note that due to internal hashing of indices the order in which the values of the associative domain are iterated is not the same as their specification order.
This code
const D : domain(string) = {"bar", "foo"}; writeln(D);produces the output
{foo, bar}
If uninitialized, the default value of an associative domain is the empty index set.
Indices can be added to or removed from an associative domain as described in Adding and Removing Domain Indices.
Simple Subdomain Types and Values¶
A subdomain is a domain whose indices are guaranteed to be a subset of those described by another domain known as its parent domain. A subdomain has the same type as its parent domain, and by default it inherits the distribution of its parent domain. All domain types support subdomains.
Simple subdomains are subdomains that are not sparse. Sparse subdomains are discussed in the following section (Sparse Subdomain Types and Values). A simple subdomain inherits its representation (regular or irregular) from its base domain (or base subdomain). A sparse subdomain is always irregular, even if its base domain is regular.
In all other respects, the two kinds of subdomain behave identically. In this specification, “subdomain” refers to both simple and sparse subdomains, unless it is specifically distinguished as one or the other.
Rationale.
Subdomains are provided in Chapel for a number of reasons: to facilitate the ability of the compiler or a reader to reason about the inter-relationship of distinct domain variables; to support the author’s ability to omit redundant distribution specifications; to support the compiler’s ability to reason about the relative alignment of multiple domains; and to improve the compiler’s ability to prove away bounds checks for array accesses.
Simple Subdomain Types¶
A simple subdomain type is specified using the following syntax:
simple-subdomain-type:
'subdomain' ( domain-expression )
This declares that domain-expression
is the parent domain of this
subdomain type. A simple subdomain specifies a subdomain with the same
underlying representation as its base domain.
Open issue.
An open semantic issue for subdomains is when a subdomain’s subset property should be re-verified once its parent domain is reassigned and whether this should be done aggressively or lazily.
Simple Subdomain Values¶
The value of a simple subdomain is the set of all index values that the subdomain describes.
The default value of a simple subdomain type is the same as the default value of its parent’s type (Rectangular Domain Values, Associative Domain Values).
A simple subdomain can be initialized or otherwise operated on in the same way as its parent domain. It is an error to attempt to add an index to a subdomain that is not also a member of the parent domain.
Sparse Subdomain Types and Values¶
Warning
Sparse domains and arrays are currently unstable. Their functionality is likely to change in the future.
sparse-subdomain-type:
'sparse' 'subdomain'[OPT] ( domain-expression )
This declaration creates a sparse subdomain. Sparse subdomains are irregular domains that describe an arbitrary subset of a domain, even if the parent domain is a regular domain. Sparse subdomains are useful in Chapel for defining sparse arrays in which a single element value (usually “zero”) occurs frequently enough that it is worthwhile to avoid storing it redundantly. The set difference between a sparse subdomain’s index set and that of parent domain is the set of indices for which the sparse array will store this replicated value. See Sparse Arrays for details about sparse arrays.
Sparse Subdomain Types¶
Each root domain type has a unique corresponding sparse subdomain type. Sparse subdomains whose parent domains are also sparse subdomains share the same type.
Sparse Subdomain Values¶
A sparse subdomain’s value is simply the set of all index values that the domain describes. If the parent domain defines an iteration order over its indices, the sparse subdomain inherits that order.
There is no literal syntax for a sparse subdomain. However, a variable
of a sparse subdomain type can be initialized or assigned to
with a tuple containing the desired index values.
Each index value must be of the parent’s rank*idxType
, or,
for a one-dimensional domain, of the parent’s idxType
.
Indices can also be added to or removed from a sparse subdomain
as described in Adding and Removing Domain Indices.
The default value for a sparse subdomain value is the empty set.
Example.
The following code declares a two-dimensional dense domain
D
, followed by a two dimensional sparse subdomain ofD
namedSpsD
. SinceSpsD
is uninitialized, it will initially describe an empty set of indices fromD
.const D: domain(2) = {1..n, 1..n}; var SpsD: sparse subdomain(D);
Domain Index Types¶
Each domain value has a corresponding compiler-provided index type which can be used to represent values belonging to that domain’s index set. Index types are described using the following syntax:
index-type:
'index' ( domain-expression )
A variable with a given index type is constrained to take on only values available within the domain on which it is defined. This restriction allows the compiler to prove away the bound checking that code safety considerations might otherwise require. Due to the subset relationship between a base domain and its subdomains, a variable of an index type defined with respect to a subdomain is also necessarily a valid index into the base domain.
Since an index types are known to be legal for a given domain, it may also afford the opportunity to represent that index using an optimized format that doesn’t simply store the index variable’s value. This fact could be used to support accelerated access to arrays declared over that domain. For example, iteration over an index type could be implemented using memory pointers and strides, rather than explicitly calculating the offset of each index within the domain.
These potential optimizations may make it less expensive to index into arrays using index type variables of their domains or subdomains.
In addition, since an index type is associated with a specific domain or
subdomain, it carries more semantic weight than a generic index. For
example, one could iterate over a rectangular domain with integer bounds
using an int(n)
as the index variable. However, it would be more
precise to use a variable of the domain’s index type.
Open issue.
An open issue for index types is what the semantics should be for an index type value that is live across a modification to its domain’s index set—particularly one that shrinks the index set. Our hypothesis is that most stored indices will either have short lifespans or belong to constant or monotonically growing domains. But these semantics need to be defined nevertheless.
Iteration Over Domains¶
All domains support iteration via standard for
, forall
, and
coforall
loops. These loops iterate over all of the indices that the
domain describes. If the domain defines an iteration order of its
indices, then the indices are visited in that order.
The type of the iterator variable for an iteration over a domain named
D
is that domain’s index type, index(D)
.
Domains as Arguments¶
This section describes the semantics of passing domains as arguments to functions.
Formal Arguments of Domain Type¶
When a domain value is passed to a formal argument of compatible domain type by default intent, it is passed by reference in order to preserve the domain’s identity.
Domain Promotion of Scalar Functions¶
Domain values may be passed to a scalar function argument whose type matches the domain’s index type. This results in a promotion of the scalar function as defined in Promotion.
Example.
Given a function
foo()
that accepts real floating point values and an associative domainD
of typedomain(real)
,foo
can be called withD
as its actual argument which will result in the function being invoked for each value in the index set ofD
.
Example.
Given an array
A
with element typeint
declared over a one-dimensional domainD
withidxType
int
, the array elements can be assigned their corresponding index values by writing:A = D;This is equivalent to:
forall (a,i) in zip(A,D) do a = i;
Domain Operations¶
Chapel supplies predefined operators and functions that can be used to manipulate domains. Unless otherwise noted, these operations are applicable to a domain of any type, whether a base domain or a subdomain.
domain-expression:
domain-literal
domain-name
domain-assignment-expression
domain-striding-expression
domain-alignment-expression
domain-slice-expression
domain-literal:
rectangular-domain-literal
associative-domain-literal
domain-assignment-expression:
domain-name = domain-expression
domain-name:
identifier
Domain Assignment¶
All domain types support domain assignment.
Domain assignment is by value and causes the target domain variable to take on the index set of the right-hand side expression. Note: the distribution of the left-hand side domain is unaffected by domain assignment, as discussed here. In practice, the right-hand side expression is often another domain value; a tuple of ranges (for regular domains); or a tuple of indices or a loop that enumerates indices (for irregular domains). If the domain variable being assigned was used to declare arrays, these arrays are reallocated as discussed in Association of Arrays to Domains.
Assignment between two rectangular domains performs dimension-wise range assignment. The two domains must have the same rank and assignment between the ranges in each dimension must be legal.
Example.
The following three assignments show ways of assigning indices to a sparse domain,
SpsD
. The first assigns the domain two index values,(1,1)
and(n,n)
. The second assigns the domain all of the indices along the diagonal from(1,1)
\(\ldots\)(n,n)
. The third invokes an iterator that is written toyield
indices read from a file named “inds.dat”. Each of these assignments has the effect of replacing the previous index set with a completely new set of values.SpsD = ((1,1), (n,n)); SpsD = [i in 1..n] (i,i); SpsD = readIndicesFromFile("inds.dat");
Domain Comparison¶
Equality operators are defined to test if two domains are equivalent or not:
dom1 == dom2 dom1 != dom2
Domain Striding¶
The by
operator can be applied to a rectangular domain value in
order to create a strided rectangular domain value. The right-hand
operand to the by
operator is the stride value,
which can be either an integral value or an
integral tuple whose size matches the domain’s rank.
domain-striding-expression:
domain-expression 'by' expression
The type of the resulting domain is the same as the original domain,
with the strides
parameter adjusted to the most narrow
strideKind
that can represent all strides
parameters
of the resulting domain’s ranges.
The resulting domain’s range in each dimension is obtained
by applying the by
operator to the corresponding dimension
of the operand domain and the stride value if it is an integer,
or the corresponding component of the stride value if it is a tuple.
Domain Alignment¶
The align
operator can be applied to a rectangular domain value in
order to create a domain with different alignment(s).
The right-hand operand to the align
operator is the alignment value,
which can be either an integral
value or an integral tuple whose size matches the domain’s rank.
domain-alignment-expression:
domain-expression 'align' expression
The type of the resulting domain is the same as the original domain.
The resulting domain’s range in each dimension is obtained
by applying the align
operator to the corresponding dimension
of the operand domain and the alignment value if it is an integer,
or the corresponding component of the alignment value if it is a tuple.
Domain Slicing¶
Slicing is the application of an index set to a domain. It can be written using either parentheses or square brackets. The index set can be defined with either a domain or a list of ranges.
domain-slice-expression:
domain-expression [ slicing-index-set ]
domain-expression ( slicing-index-set )
slicing-index-set:
domain-expression
range-expression-list
The result of slicing, or a slice, is a new domain value that represents the intersection of the index set of the domain being sliced and the index set being applied. The type and distribution of the slice match the domain being sliced.
Slicing can also be performed on an array, resulting in aliasing a subset of the array’s elements (Array Slicing).
Domain-based Slicing¶
If the brackets or parentheses contain a domain value, its index set is applied for slicing.
Open issue.
Can we say that it is an alias in the case of sparse/associative?
Range-based Slicing¶
When slicing rectangular domains or arrays, the brackets or parentheses
can contain a list of rank
ranges. These ranges can either be
bounded or unbounded. When unbounded, they inherit their bounds from the
domain or array being sliced. The Cartesian product of the ranges’ index
sets is applied for slicing.
Example.
The following code declares a two dimensional rectangular domain
D
, and then a number of subdomains ofD
by slicing intoD
using bounded and unbounded ranges. TheInnerD
domain describes the inner indices of D,Col2OfD
describes the 2nd column ofD
, andAllButLastRow
describes all ofD
except for the last row.const D: domain(2) = {1..n, 1..n}, InnerD = D[2..n-1, 2..n-1], Col2OfD = D[.., 2..2], AllButLastRow = D[..n-1, ..];
Rank-Change Slicing¶
For multidimensional rectangular domains and arrays, substituting integral values for one or more of the ranges in a range-based slice will result in a domain or array of lower rank.
The result of a rank-change slice on an array is an alias to a subset of the array’s elements as described in Rectangular Array Slicing.
The result of rank-change slice on a domain is a subdomain of the domain
being sliced. The resulting subdomain’s type will be the same as the
original domain, but with a rank
equal to the number of dimensions
that were sliced by ranges rather than integers.
Count Operator¶
The #
operator can be applied to dense rectangular domains with a
tuple argument whose size matches the rank of the domain (or optionally
an integer in the case of a 1D domain). The operator produces a new domain
obtained by applying the #
operator to each of the component ranges
of the argument domain, with the same distribution as the argument.
Adding and Removing Domain Indices¶
All irregular domain types support the ability to incrementally add and
remove indices from their index sets. This can either be done using
add(i:idxType)
and remove(i:idxType)
methods on a domain
variable or by using the +=
and -=
assignment operators. It is
legal to add the same index to an irregular domain’s index set twice,
but illegal to remove an index that does not belong to the domain’s
index set.
Open issue.
These remove semantics seem dangerous in a parallel context; maybe add flags to both the method versions of the call that say whether they should balk or not? Or add exceptions…
As with normal domain assignments, arrays declared in terms of a domain being modified in this way will be reallocated as discussed in Association of Arrays to Domains.
Set Operations on Associative Domains¶
Associative domains (and arrays) support a number of operators for set manipulations. The supported set operators are:
+ , |
Union
&
Intersection
-
Difference
^
Symmetric Difference
Predefined Routines on Domains¶
- config const defaultHashTableResizeThreshold = 0.5¶
Fractional value that specifies how full this domain can be before requesting additional memory. The default value of 0.5 means that the map will not resize until the map is more than 50% full. The acceptable values for this argument are between 0 and 1, exclusive, meaning (0,1). A lower defaultHashTableResizeThreshold value can potentially improve indexing performance, since the table will likely have fewer collisions, while a higher value can help save memory. Note that this value also impacts all of Chapel’s hash-based data structures, such as set and map.
- type domain : writeSerializable, readDeserializable¶
The domain type.
- proc distribution¶
Returns the domain map that implements this domain.
- proc rank param¶
Returns the number of dimensions in this domain.
- proc idxType type¶
Returns the type used to represent the indices of this domain. For a multidimensional domain, this represents the per-dimension index type.
- proc fullIdxType type¶
Returns the full type used to represent the indices of this domain. For a 1D or associative domain, this is the same as
idxType
above. For a multidimensional domain, it isrank
*idxType
.
- proc strides param where this.isRectangular()¶
Returns the ‘strides’ parameter of the domain.
- proc stride¶
Returns the stride of the indices in this domain.
- proc alignment¶
Returns the alignment of the indices in this domain.
- proc targetLocales() const ref¶
Returns an array of locales over which this domain has been distributed.
- proc isEmpty() : bool¶
Returns true if the domain has no indices.
- proc size : int¶
Returns the number of indices in this domain as an
int
.
- proc sizeAs(type t: integral) : t¶
Returns the number of indices in this domain as the specified type.
- proc dims()¶
Returns a tuple of ranges describing the bounds of a rectangular domain. For a sparse domain, returns the bounds of the parent domain.
- proc dim(d: int)¶
Returns a range representing the boundary of this domain in a particular dimension.
- proc shape : rank*int where this.isRectangular() || this.isSparse()¶
Returns a tuple of
int
values representing the size of each dimension.For a sparse domain, this returns the shape of the parent domain.
- proc low¶
Returns the lowest index represented by a rectangular domain.
- proc high¶
Returns the highest index represented by a rectangular domain.
- proc lowBound¶
Returns the domain’s ‘pure’ low bound. For example, given the domain
{1..10 by -2}
,.lowBound
would return 1, whereas.low
would return 2 since it’s the lowest index represented by the domain. This routine is only supported on rectangular domains.
- proc highBound¶
Returns the domain’s ‘pure’ high bound. For example, given the domain
{1..10 by 2}
,.highBound
would return 10, whereas.high
would return 9 since it’s the highest index represented by the domain. This routine is only supported on rectangular domains.
- proc first¶
Returns the first index in this domain.
- proc last¶
Returns the last index in this domain.
- proc tryCreateArray(type eltType) throws¶
Warning
tryCreateArray() is subject to change in the future.
Invoking this method will attempt to create and return an array declared over the domain instance. If there is not enough memory to satisfy the allocation, an error will be thrown, allowing the program to continue if handled, as opposed to halting and thus stopping program execution.
This method will be most reliable in configurations that use a fixed heap (e.g., when using
CHPL_GASNET_SEGMENT=large
), but can be called in all configurations. In the case of a dynamic heap, it is possible that overcommit will cause the array allocation to succeed, even if there is not enough physical memory to satisfy the allocation, which will then fail with a bus error when attempting to access the array.This method can be called on all domains that implement a ‘doiTryCreateArray’ method.
Throws an ArrayOomError when out of memory allocating elements.
- proc tryCreateArray(type eltType, initExpr: ?t) throws where isSubtype(t, _iteratorRecord) || isCoercible(t, eltType)
Warning
tryCreateArray() is subject to change in the future.
- proc tryCreateArray(type eltType, initExpr: [?dom] ?arrayEltType) throws where this.rank == dom.rank && isCoercible(arrayEltType, eltType)
Warning
tryCreateArray() is subject to change in the future.
- type unsafeAssignManager : contextManager¶
An instance of this type is a context manager that can be used in manage statements to resize arrays of non-default-initializable element types after resizing their underlying domain.
Using an instance of this type in a manage statement will cause a domain assignment to occur before executing the statement body. The left-hand-side of the assignment is the receiver domain that had
unsafeAssign()
called on it, while the right-hand-side is the dom formal of the same call.If the assignment adds new indices to the assigned domain, then corresponding elements are added to arrays declared over it. If an array’s element type is non-default-initializable, then any newly added elements remain uninitialized.
The
initialize()
method can be used within the manage statement body to initialize new elements of non-default-initializable arrays declared over the assigned domain.The new elements of default-initializable arrays over the assigned domain will be default-initialized. They can be set to desired values as usual, for example using an assignment operator.
- proc checks param¶
Returns
true
if this manager has runtime safety checks enabled.- proc isElementInitialized(arr: [?d], idx)¶
Returns
true
if the value at a given index in an array has been initialized.- proc initialize(ref arr: [?d], idx, in value: arr.eltType)¶
Initializes a newly added array element at an index with a new value.
If checks is
true
and the array element at idx has already been initialized, this method will halt. If checks isfalse
, then calling this method on an already initialized element will result in undefined behavior.It is an error if idx is not a valid index in arr.
- iter newIndices()¶
Iterates over any new indices that will be added to this domain as a result of unsafe assignment.
- proc ref unsafeAssign(const ref dom: domain, param checks: bool = false)¶
Returns an instance of
unsafeAssignManager
.The returned context manager can be used in a manage statement to assign the indices of dom into the receiver domain. Within the body of the manage statement, the manager can initialize elements of non-default-initializable arrays declared over the receiver domain.
If checks is
true
, this method will guarantee that:Newly added elements of any non-default-initializable arrays declared over the receiver domain have been initialized by the end of the manage statement
Newly added elements are only initialized once
These guarantees hold only for initialization done through calls to the
initialize()
method on the context manager. Performing any other operation on a newly added array element causes undefined behavior until afterinitialize()
has been called.For example:
var D = {0..0}; var A: [D] shared C = [new shared C(0)]; manage D.unsafeAssign({0..1}, checks=true) as mgr { // 'D' has a new index '1', so 'A' has a new element at '1', // which we need to initialize: mgr.initialize(A, 1, new shared C(1)); }
Note
Checks are not currently supported for arrays of non-default-initializable element types other than arrays of non-nilable classes.
- Arguments:
dom – The domain to assign to the receiver
checks – If this manager should provide runtime safety checks
- Returns:
A
unsafeAssignManager
for use in manage statements
- proc ref clear()¶
Removes all indices from this domain, leaving it empty.
- proc ref remove(idx)¶
Removes index
idx
from this domain.
- proc ref add(in idx)¶
Adds index
idx
to this domain. This method is also available as the+=
operator. Returns the number of indices that were added.The domain must be irregular.
- proc createIndexBuffer(size: int)¶
Warning
createIndexBuffer() is subject to change in the future.
Creates an index buffer which can be used for faster index addition.
For example, instead of:
var spsDom: sparse subdomain(parentDom); for i in someIndexIterator() do spsDom += i;
You can use SparseIndexBuffer for better performance:
var spsDom: sparse subdomain(parentDom); var idxBuf = spsDom.createIndexBuffer(size=N); for i in someIndexIterator() do idxBuf.add(i); idxBuf.commit();
The above snippet will create a buffer of size N indices, and will automatically commit indices to the sparse domain as the buffer fills up. Indices are also committed when the buffer goes out of scope.
- Arguments:
size :
int
– Size of the buffer in number of indices.
- proc ref bulkAdd(inds: [] (_value.rank*_value.idxType), dataSorted = false, isUnique = false, addOn = nilLocale) where this.isSparse() && _value.rank > 1¶
Warning
bulkAdd() is subject to change in the future.
Adds indices in
inds
to this domain in bulk.For sparse domains, an operation equivalent to this method is available with the
+=
operator, where the right-hand-side is an array. However, in that case, default values will be used for the flagsdataSorted
andisUnique
. This method is available because in some cases, expensive operations can be avoided by setting those flags. To do so,bulkAdd
must be called explicitly (instead of+=
).Note
Right now, this method and the corresponding
+=
operator are only available for sparse domains. In the future, we expect that these methods will be available for all irregular domains.Note
nilLocale
is a sentinel value to denote that the locale where this addition should occur is unknown. We expect this to change in the future.Note
This method may make a copy of
inds
if the data is not sorted to preserve the indices used. If the data is already sorted, it is possible to avoid this extra copy by usingbulkAddNoPreserveInds
, which does not copy the indices and may modifyinds
in place.- Arguments:
inds – Indices to be added.
inds
must be an array ofrank*idxType
, except for 1-D domains, where it must be an array ofidxType
.dataSorted :
bool
–true
if data ininds
is sorted.isUnique :
bool
–true
if data ininds
has no duplicates.addOn :
locale
– The locale where the indices should be added. Default value isnil
which indicates that locale is unknown or there are more than one.
- Returns:
Number of indices added to the domain
- Return type:
int
- proc ref bulkAddNoPreserveInds(ref inds: [] (_value.rank*_value.idxType), dataSorted = false, isUnique = false, addOn = nilLocale) where this.isSparse() && _value.rank > 1¶
Warning
bulkAddNoPreserveInds() is subject to change in the future.
Adds indices in
inds
to this domain in bulk.This is nearly identical to
bulkAdd
.bulkAdd
may make a copy ofinds
if the data is unsorted, whereas this method will modifyinds
in place.Note
Right now, this method is only available for sparse domains. In the future, we expect that this method will be available for all irregular domains.
Note
nilLocale
is a sentinel value to denote that the locale where this addition should occur is unknown. We expect this to change in the future.- Arguments:
inds – Indices to be added.
inds
must be an array ofrank*idxType
, except for 1-D domains, where it must be an array ofidxType
.dataSorted :
bool
–true
if data ininds
is sorted.isUnique :
bool
–true
if data ininds
has no duplicates.addOn :
locale
– The locale where the indices should be added. Default value isnil
which indicates that locale is unknown or there are more than one.
- Returns:
Number of indices added to the domain
- Return type:
int
- proc ref requestCapacity(capacity)¶
Requests space for a particular number of values in an domain.
Currently only applies to associative domains.
- proc contains(const idx: _value.idxType ...rank)¶
Returns true if this domain contains
idx
. Otherwise returns false. For sparse domains, only indices with a value are considered to be contained in the domain.
- proc contains(other: domain)
Returns true if this domain contains all the indices in the domain
other
.
- proc orderToIndex(order: int) where this.isRectangular() && isNumericType(this.idxType)¶
Warning
domain.orderToIndex() is unstable and its behavior may change in the future
Returns the ith index in the domain counting from 0. For example,
{2..10 by 2}.orderToIndex(2)
would return6
.The order of a multidimensional domain follows its serial iterator. For example,
{1..3, 1..2}.orderToIndex(3)
would return(2, 2)
.Note
Right now, this method supports only dense rectangular domains with numeric indices
- Arguments:
order – Order for which the corresponding index in the domain has to be found.
- Returns:
Domain index for a given order in the domain.
- proc expand(off: rank*integral)¶
Warning
domain.expand() is unstable and its behavior may change in the future
Returns a new domain that is the current domain expanded by
off(d)
in dimensiond
ifoff(d)
is positive or contracted byoff(d)
in dimensiond
ifoff(d)
is negative.See
ChapelRange.range.expand
for further information about what it means to expand a range.
- proc expand(off: integral) where rank > 1
Warning
domain.expand() is unstable and its behavior may change in the future
Returns a new domain that is the current domain expanded by
off
in all dimensions ifoff
is positive or contracted byoff
in all dimensions ifoff
is negative.See
ChapelRange.range.expand
for further information about what it means to expand a range.
- proc exterior(off: rank*integral)¶
Warning
domain.exterior() is unstable and its behavior may change in the future
Returns a new domain that is the exterior portion of the current domain with
off(d)
indices for each dimensiond
. Ifoff(d)
is negative, compute the exterior from the low bound of the dimension; if positive, compute the exterior from the high bound.See
ChapelRange.range.exterior
for further information about what it means to compute the exterior of a range.
- proc exterior(off: integral) where rank != 1
Warning
domain.exterior() is unstable and its behavior may change in the future
Returns a new domain that is the exterior portion of the current domain with
off
indices for each dimension. Ifoff
is negative, compute the exterior from the low bound of the dimension; if positive, compute the exterior from the high bound.See
ChapelRange.range.exterior
for further information about what it means to compute the exterior of a range.
- proc interior(off: rank*integral)¶
Warning
domain.interior() is unstable and its behavior may change in the future
Returns a new domain that is the interior portion of the current domain with
off(d)
indices for each dimensiond
. Ifoff(d)
is negative, compute the interior from the low bound of the dimension; if positive, compute the interior from the high bound.See
ChapelRange.range.interior
for further information about what it means to compute the exterior of a range.
- proc interior(off: integral) where rank != 1
Warning
domain.interior() is unstable and its behavior may change in the future
Returns a new domain that is the interior portion of the current domain with
off
indices for each dimension. Ifoff
is negative, compute the interior from the low bound of the dimension; if positive, compute the interior from the high bound.See
ChapelRange.range.interior
for further information about what it means to compute the exterior of a range.
- proc translate(off: rank*integral)¶
Warning
domain.translate() is unstable and its behavior may change in the future
Returns a new domain that is the current domain translated by
off(d)
in each dimensiond
.See
ChapelRange.range.translate
for further information about what it means to translate a range.
- proc translate(off: integral) where rank != 1
Warning
domain.translate() is unstable and its behavior may change in the future
Returns a new domain that is the current domain translated by
off
in each dimension.See
ChapelRange.range.translate()
for further information about what it means to translate a range.
- proc localSlice(r ...rank)¶
Returns a local view of the sub-domain (slice) defined by the provided range(s), halting if the slice contains elements that are not local.
- proc localSlice(d: domain)
Returns a local view of the sub-domain (slice) defined by the provided domain, halting if the slice contains elements that are not local.
- iter sorted(comparator: ?t = chpl_defaultComparator())¶
Yields the domain indices in sorted order. This method is only supported on associative domains.
Warning
It is recommended to use
Sort.sorted
instead of this method.
- proc hasSingleLocalSubdomain() param¶
Warning
‘hasSingleLocalSubdomain’ on domains is unstable and may change in the future
Returns true if the local subdomain can be represented as a single domain. Otherwise returns false.
- proc localSubdomain(loc: locale = here)¶
Returns the subdomain that is local to loc.
- Arguments:
loc :
locale
– indicates the locale for which the query should take place (defaults to here)
- iter localSubdomains(loc: locale = here)¶
Warning
‘localSubdomains’ on domains is unstable and may change in the future
Yields the subdomains that are local to loc.
- Arguments:
loc :
locale
– indicates the locale for which the query should take place (defaults to here)
- proc tryCast(type t: domain)¶
Casts a rectangular domain to a new rectangular domain type. Throws an IllegalArgumentError when the original bounds and/or stride(s) do not fit in the new idxType or when the original stride(s) are not legal for the new strides parameter.
- proc isRectangular() param¶
Returns true if this domain is a rectangular. Otherwise returns false.
- proc isIrregular() param¶
Returns true if
d
is an irregular domain; e.g. is not rectangular. Otherwise returns false.
- proc isAssociative() param¶
Returns true if
d
is an associative domain. Otherwise returns false.
- proc isSparse() param¶
Returns true if
d
is a sparse domain. Otherwise returns false.
- proc makeRectangularDomain(low: ?t1, high: ?t2, param inclusive: bool = true) where chpl_isValidRangeIdxType(t1) && chpl_isValidRangeIdxType(t2)¶
Warning
makeRectangularDomain() is subject to change in the future.
Creates a rectangular domain with bounds defined by the scalar values low and high. If inclusive is true, the domain includes the high value. Otherwise, the domain excludes the high value.
- proc makeRectangularDomain(low: ?t1, high: ?t2, param inclusive: bool = true) where isTuple(low) && isTuple(high) && isHomogeneousTuple(low) && isHomogeneousTuple(high) && low.size == high.size && (isCoercible(low(0).type, high(0).type) || isCoercible(high(0).type, low(0).type))
Warning
makeRectangularDomain() is subject to change in the future.
Creates a multidimensional rectangular domain with bounds defined by the pairwise elements of low and high. If inclusive is true, the domain includes the high values. Otherwise, the domain excludes the high values. For example, makeRectangularDomain((1, 2), (10,11)) is equivalent to {1..10, 2..11}.
- proc makeRectangularDomain(low: ?t1, high: ?t2, param inclusive: bool = true) where isTuple(low) != isTuple(high)
Warning
makeRectangularDomain() is subject to change in the future.
Creates a rectangular domain with bounds defined by one tuple and one scalar value. The scalar argument is used in each dimension of the domain, while the ‘n’-th tuple element is used to define the ‘n’-th dimension of the domain. If inclusive is true, the domain includes the high value. Otherwise, the domain excludes the high value. For example, makeRectangularDomain((1, 2), 10) is equivalent to {1..10, 2..10} and makeRectangularDomain(1, (10, 11), inclusive=false) is equivalent to {1..<10, 1..<11}.