Struct Graph

Source
pub struct Graph { /* private fields */ }
Expand description

A structure that stores a pointer to a computation graph, where every node corresponds to an operation.

§Rust crates

Clone trait duplicates the pointer, not the underlying graph.

PartialEq trait compares pointers, not the related graphs.

§Example

let c = create_context().unwrap();
let g1 = c.create_graph().unwrap();
let g2 = c.create_graph().unwrap();
assert_ne!(g1, g2);
let g3 = g1.clone();
assert_eq!(g1, g3);

Implementations§

Source§

impl Graph

Public methods which supposed to be imported in Python.

Source

pub fn set_as_main(&self) -> Result<Graph>

Applies Context::set_main_graph to the parent context and this graph. Returns the clone of this.

§Returns

This graph

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], INT32);
let n = g.input(t).unwrap();
n.set_as_output().unwrap();
g.finalize().unwrap();
g.set_as_main().unwrap();
Source

pub fn set_name(&self, name: &str) -> Result<Graph>

Applies Context::set_graph_name to the parent context and this graph. Returns the clone of this.

§Arguments

name - name of the graph

§Returns

This graph

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
g.set_name("relu").unwrap();
Source

pub fn get_name(&self) -> Result<String>

Applies Context::get_graph_name to the parent context and this graph.

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
g.set_name("relu").unwrap();
assert_eq!(g.get_name().unwrap(), "relu".to_owned());
Source

pub fn retrieve_node(&self, name: &str) -> Result<Node>

Applies Context::retrieve_node to the parent context and this graph.

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let n = g.input(scalar_type(BIT)).unwrap();
n.set_name("input_node").unwrap();
assert!(n == g.retrieve_node("input_node").unwrap());
Source

pub fn input(&self, input_type: Type) -> Result<Node>

Adds an input node to the graph and returns it.

During evaluation, input nodes require values to be supplied.

§Arguments

input_type - type of a new input node

§Returns

New input node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = scalar_type(BIT);
let n = g.input(t).unwrap();
Source

pub fn zeros(&self, t: Type) -> Result<Node>

Adds an node with zeros of given type.

Compared to constant this node does produce a big value array in serialized graph.

§Arguments

t - node type

§Returns

New node with zeros of given type.

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let z = g.zeros(array_type(vec![10, 20], UINT8)).unwrap();
Source

pub fn ones(&self, t: Type) -> Result<Node>

Adds an node with ones of given type.

Compared to constant this node does produce a big value array in serialized graph.

§Arguments

t - node type

§Returns

New node with ones of given type.

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let z = g.ones(array_type(vec![10, 20], UINT8)).unwrap();
Source

pub fn add(&self, a: Node, b: Node) -> Result<Node>

Adds a node that sums two arrays or scalars of the same scalar type elementwise.

If input shapes are different, the broadcasting rules are applied (see the NumPy broadcasting rules). For example, adding two arrays of shapes [10,1,7] and [8,1] results in an array of shape [10,8,7].

§Arguments
  • a - node containing the first term (array or scalar)
  • b - node containing the second term (array or scalar)
§Returns

New addition node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = scalar_type(BIT);
let n1 = g.input(t.clone()).unwrap();
let n2 = g.input(t).unwrap();
let n3 = g.add(n1, n2).unwrap();
Source

pub fn subtract(&self, a: Node, b: Node) -> Result<Node>

Adds a node that subtracts two arrays or scalars of the same scalar type elementwise.

If input shapes are different, the broadcasting rules are applied (see the NumPy broadcasting rules). For example, subtracting two arrays of shapes [10,1,7] and [8,1] results in an array of shape [10,8,7].

§Arguments
  • a - node containing the minuend (array or scalar)
  • b - node containing the subtrahend (array or scalar)
§Returns

New subtraction node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = scalar_type(BIT);
let n1 = g.input(t.clone()).unwrap();
let n2 = g.input(t).unwrap();
let n3 = g.subtract(n1, n2).unwrap();
Source

pub fn multiply(&self, a: Node, b: Node) -> Result<Node>

Adds a node that multiplies two arrays or scalars of the same scalar type elementwise.

If input shapes are different, the broadcasting rules are applied (see the NumPy broadcasting rules). For example, multiplication of two arrays of shapes [10,1,7] and [8,1] results in an array of shape [10,8,7].

§Arguments
  • a - node containing the first factor (array or scalar)
  • b - node containing the second factor (array or scalar)
§Returns

New multiplication node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = scalar_type(BIT);
let n1 = g.input(t.clone()).unwrap();
let n2 = g.input(t).unwrap();
let n3 = g.multiply(n1, n2).unwrap();
Source

pub fn mixed_multiply(&self, a: Node, b: Node) -> Result<Node>

Adds a node that multiplies an integer array or scalar by a binary array or scalar elementwise. For each integer element, this operation returns this element or zero depending on the corresponding bit element.

If input shapes are different, the broadcasting rules are applied (see the NumPy broadcasting rules). For example, multiplication of two arrays of shapes [10,1,7] and [8,1] results in an array of shape [10,8,7].

§Arguments
  • a - node containing an integer array or scalar
  • b - node containing a binary array or scalar
§Returns

New mixed multiplication node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = scalar_type(INT32);
let t2 = scalar_type(BIT);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.mixed_multiply(n1, n2).unwrap();
Source

pub fn dot(&self, a: Node, b: Node) -> Result<Node>

Adds a node that computes the dot product according to the NumPy rules:

  • if both factors are 1-dimensional arrays, return their inner product;
  • if both factors are 2-dimensional arrays, return their matrix product;
  • if one of the factors is scalar, return the result of multiply;
  • if the first factor is n-dimensional and the second one is 1-dimensional, compute the elementwise multiplication and return the sum over the last axis.
  • if both factors are n-dimensional (n>2), return the sum product over the last axis of the first factor and the second-to-last axis of the second factor, i.e.

dot(A, B)[i,j,k,m] = sum(A[i,j,:] * B[k,:,m]) (in the NumPy notation).

§Arguments
  • a - node containing the first factor (array or scalar)
  • b - node containing the second factor (array or scalar)
§Returns

New dot product node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![10], INT32);
let n1 = g.input(t.clone()).unwrap();
let n2 = g.input(t).unwrap();
let n3 = g.dot(n1, n2).unwrap();
Source

pub fn matmul(&self, a: Node, b: Node) -> Result<Node>

Adds a node that computes the matrix product of two arrays according to the NumPy rules.

Each array is represented as an array of 2-dimensional matrix elements and this node returns the elementwise product of such matrix arrays.

§Arguments
  • a - node containing the first array
  • b - node containing the second array
§Returns

New matrix product node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![2, 3], INT32);
let t2 = array_type(vec![3, 2], INT32);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.matmul(n1, n2).unwrap();
Source

pub fn join( &self, a: Node, b: Node, t: JoinType, headers: HashMap<String, String>, ) -> Result<Node>

Adds a node that computes a join of a given type on two named tuples along given key headers.

Each tuple should consist of arrays having the same number of rows, i.e. the first dimensions of these arrays should be equal.

In addition, each named tuple should have a binary array named with NULL_HEADER that contains zeros in rows void of content; otherwise, it contains ones. This column is called the null column.

Let row key be the bitstring obtained by concatenating data elements for given key headers. WARNING: Rows must have unique row keys, except for rows where NULL_HEADER is zero: those rows are ignored.

This operation returns:

  • Inner join: a named tuple containing rows where input tuples have matching row keys.
  • Left join: a named tuple containing all the rows of the first input tuple merged with the rows of the second input tuple having same row keys.
  • Union join: a named tuple containing rows of the first input tuple that are not in the inner join and all the rows of the second tuple. In contrast to the SQL union, this operation does not require that input datasets have respective columns of the same type. This means that columns of both datasets are included and filled with zeros where no data can be retrieved. Namely, the rows of the second set in the union join will contain zeros in non-key columns of the first set and vice versa.
  • Full join: a named tuple containing all the rows of input tuples. If the row key of a row of the first set match with the row key of a row of the second set, they are merged into one. The order of rows goes as follows:
  1. the rows of the first set that don’t belong to the inner join.
  2. all the rows of the second set including those merged with the rows of the first set as in inner join. In this form, full join is computed as union_join(a, left_join(b, a)).
§Arguments
  • a - node containing the first named tuple
  • b - node containing the second named tuple
  • t - join type (Inner/Left/Union/Full)
  • headers - headers of columns along which the join is performed
§Returns

New join node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1n = array_type(vec![100], BIT);
let t11 = array_type(vec![100], INT32);
let t12 = array_type(vec![100, 128], BIT);
let t13 = array_type(vec![100], INT64);
let t2n = array_type(vec![50], BIT);
let t21 = array_type(vec![50], INT32);
let t22 = array_type(vec![50, 128], BIT);
let t23 = array_type(vec![50], UINT8);
let t1 = named_tuple_type(vec![
    (NULL_HEADER.to_owned(), t1n),
    ("ID".to_owned(), t11),
    ("Occupation".to_owned(), t12),
    ("Revenue".to_owned(), t13),
]);
let t2 = named_tuple_type(vec![
    (NULL_HEADER.to_owned(), t2n),
    ("ID".to_owned(), t21),
    ("Job".to_owned(), t22),
    ("Age".to_owned(), t23),
]);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.join(n1, n2, JoinType::Inner, HashMap::from([
    ("ID".to_owned(), "ID".to_owned()),
    ("Occupation".to_owned(), "Job".to_owned()),
])).unwrap();
Source

pub fn join_with_column_masks( &self, a: Node, b: Node, t: JoinType, headers: HashMap<String, String>, ) -> Result<Node>

Adds a node that computes a join of a given type on two named tuples along given key headers.

Each tuple should consist of pairs of arrays having the same number of rows, i.e. the first dimensions of these arrays should be equal. Each pair has a binary array that contains zeros in rows where the data array has no content and an array with data.

In addition, each named tuple should have a binary array named with NULL_HEADER that contains zeros in rows void of content; otherwise, it contains ones. This column is called the null column.

Let row key be the bitstring obtained by concatenating data elements for given key headers where all the corresponding mask elements are set to one. WARNING: Rows must have unique row keys, except for rows where NULL_HEADER is zero or at least one mask element in given key headers is zero. Rows with zero NULL_HEADER are ignored. We assume that rows with zero mask elements don’t match with other rows. Thus, they can’t show up in inner and left joins, but can be copied over to the result of union or full joins.

This operation returns:

  • Inner join: a named tuple containing rows where input tuples have matching row keys.
  • Left join: a named tuple containing all the rows of the first input tuple merged with the rows of the second input tuple having same row keys.
  • Union join: a named tuple containing rows of the first input tuple that are not in the inner join and all the rows of the second tuple. In contrast to the SQL union, this operation does not require that input datasets have respective columns of the same type. This means that columns of both datasets are included and filled with zeros where no data can be retrieved. Namely, the rows of the second set in the union join will contain zeros in non-key columns of the first set and vice versa.
  • Full join: a named tuple containing all the rows of input tuples. If the row key of a row of the first set match with the row key of a row of the second set, they are merged into one. The order of rows goes as follows:
  1. the rows of the first set that don’t belong to the inner join.
  2. all the rows of the second set including those merged with the rows of the first set as in inner join. In this form, full join is computed as union_join(a, left_join(b, a)).
§Arguments
  • a - node containing the first named tuple
  • b - node containing the second named tuple
  • t - join type (Inner/Left/Union/Full)
  • headers - headers of columns along which the join is performed
§Returns

New join node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1n = array_type(vec![100], BIT);
let t11 = tuple_type(vec![array_type(vec![100], BIT), array_type(vec![100], INT32)]);
let t12 = tuple_type(vec![array_type(vec![100], BIT), array_type(vec![100, 128], BIT)]);
let t13 = tuple_type(vec![array_type(vec![100], BIT), array_type(vec![100], INT64)]);
let t2n = array_type(vec![50], BIT);
let t21 = tuple_type(vec![array_type(vec![50], BIT), array_type(vec![50], INT32)]);
let t22 = tuple_type(vec![array_type(vec![50], BIT), array_type(vec![50, 128], BIT)]);
let t23 = tuple_type(vec![array_type(vec![50], BIT), array_type(vec![50], UINT8)]);
let t1 = named_tuple_type(vec![
    (NULL_HEADER.to_owned(), t1n),
    ("ID".to_owned(), t11),
    ("Occupation".to_owned(), t12),
    ("Revenue".to_owned(), t13),
]);
let t2 = named_tuple_type(vec![
    (NULL_HEADER.to_owned(), t2n),
    ("ID".to_owned(), t21),
    ("Job".to_owned(), t22),
    ("Age".to_owned(), t23),
]);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.join_with_column_masks(n1, n2, JoinType::Inner, HashMap::from([
    ("ID".to_owned(), "ID".to_owned()),
    ("Occupation".to_owned(), "Job".to_owned()),
])).unwrap();
Source

pub fn sort(&self, a: Node, key: String) -> Result<Node>

Adds a node that sorts a table given as named tuple according to the column given by the key argument. The key column must be a 2-d BIT array of shape [n, b], interpreted as bitstrings of length b. Other columns in the named tuple must be arrays of arbitrary type and shape, as long as they share the first dimension: [n, …]. Bitstrings are sorted lexicographically, and the sorting algorithm is stable: preserving relative order of entries in other arrays where the corresponding key entries match.

§Arguments
  • a - node containing a named tuple – arrays to sort.
  • key - name of the field to sort on it, this array must be 2-d of type BIT.
§Returns

New sorted node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let v1 = g.input(array_type(vec![20], INT32)).unwrap();
let v2 = g.input(array_type(vec![20, 10, 2], UINT64)).unwrap();
let k = g.input(array_type(vec![20, 32], BIT)).unwrap();
let a = g.create_named_tuple(vec![("key".to_string(), k), ("value1".to_string(), v1), ("value2".to_string(), v2)]).unwrap();
let a = g.sort(a, "key".to_string()).unwrap();
Source

pub fn truncate(&self, a: Node, scale: u128) -> Result<Node>

Adds a node that divides a scalar or each entry of an array by a positive constant integer scale.

§Arguments
  • a - node containing a scalar or an array
  • scale - positive integer
§Returns

New truncate node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![2, 3], INT32);
let n1 = g.input(t).unwrap();
let n2 = g.truncate(n1, 4).unwrap();
Source

pub fn sum(&self, a: Node, axes: ArrayShape) -> Result<Node>

Adds a node that computes the sum of entries of an array along given axes (see numpy.sum).

For example, summing the array [[1000, 200], [30, 4]] along the first or the second axes results in the arrays [1030,204] or [1200,34], respectively. Summing along both axes yields 1234.

§Arguments
  • a - node containing an array
  • axes - indices of the axes of a
§Returns

New sum node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let axes = vec![1, 0];
let n1 = g.input(t).unwrap();
let n2 = g.sum(n1, axes).unwrap();
Source

pub fn cum_sum(&self, a: Node, axis: u64) -> Result<Node>

Adds a node that computes the cumulative sum of elements along a given axis. (see numpy.cumsum).

For example, summing the array [[1000, 200], [30, 4]] along the first or the second axes results in the arrays [[1000, 200], [1030, 204]] or [[1000, 1200], [30, 34]], respectively.

§Arguments
  • a - node containing an array
  • axis - axis along which the cumulative sum is computed
§Returns

New cumulative sum node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], INT32);
let n1 = g.input(t).unwrap();
let n2 = g.cum_sum(n1, 1).unwrap();
Source

pub fn permute_axes(&self, a: Node, axes: ArrayShape) -> Result<Node>

Adds a node that permutes an array along given axes (see numpy.transpose). This function generalizes matrix transposition.

For example, permutation of an array of shape [a,b,c] with permutation [2,0,1] results in an array of shape [c,a,b].

§Arguments
  • a - node containing an array
  • axes - indices of the axes of a
§Returns

New node with permuted axes

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let axes = vec![1, 0, 2];
let n1 = g.input(t).unwrap();
let n2 = g.permute_axes(n1, axes).unwrap();
Source

pub fn get(&self, a: Node, index: ArrayShape) -> Result<Node>

Adds a node that extracts a sub-array with a given index. This is a special case of get_slice and corresponds to single element indexing as in NumPy.

For example, given an array A of shape [a,b,c,d], its subarray B of shape [c,d] with index [i,j] can be extracted as follows

B = A[i,j,:,:] (in the NumPy notation)

§Arguments
  • a - node containing an array
  • index - index of a sub-array
§Returns

New node containing an extracted sub-array

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let index = vec![2];
let n1 = g.input(t).unwrap();
let n2 = g.get(n1, index).unwrap();
Source

pub fn get_slice(&self, a: Node, slice: Slice) -> Result<Node>

Adds a node that extracts a sub-array corresponding to a given slice.

Our slicing conventions follow the NumPy rules.

For example, given an array A of shape [a,b], its subarray B containing only the last 3 rows of A can be extracted as follows

get_slice(A, [-3::])[i,j] = A[a-3+i,j].

Slices are defined as vectors of SliceElements that have 3 possible types:

  • SingleIndex(i64) is used to extract all the elements with a given index in a respective dimension,
  • SubArray(Option<i64>, Option<i64>, Option<i64>) describes the range of indices that should be extracted over a certain dimension (similar to the a:b:c notation in NumPy)
  • Ellipsis describes several consecutive dimensions that must be extracted in full, e.g. the slice [i,...,j] can be used to extract all the elements with the index i in the first dimension and the index j in the last one, while the indices of all the other dimensions have no constraints. See the NumPy slicing for more details.
§Arguments
  • a - node containing an array
  • slice - array slice
§Returns

New node containing an extracted sub-array

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let slice = vec![SliceElement::Ellipsis, SliceElement::SubArray(None, None, Some(-2))];
let n1 = g.input(t).unwrap();
let n2 = g.get_slice(n1, slice).unwrap();
Source

pub fn reshape(&self, a: Node, new_type: Type) -> Result<Node>

Adds a node that reshapes a value to a given compatible type (similar to numpy.reshape, but more general). Specifically,

  • if the input value is an array, it can be reshaped to any array with the same number of elements;
  • if the input value in the flattened form contains n arrays or scalars, it can be reshaped to any type with the same number of arrays and scalars. Each array can be reshaped as in the above rule.

For example, an array of shape [3,10,5] can be reshaped to [2,75]. A tuple with arrays of shapes [3,4], [12], [2,6] can be reshaped to a vector with 3 array elements of shape [2,2,3].

§Arguments
  • a - node containing a value
  • new_type - type
§Returns

New node with a reshaped value

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let old_t = array_type(vec![3, 2, 3], INT32);
let new_t = array_type(vec![3,6], INT32);
let n1 = g.input(old_t).unwrap();
let n2 = g.reshape(n1, new_t).unwrap();
Source

pub fn stack(&self, nodes: Vec<Node>, outer_shape: ArrayShape) -> Result<Node>

Adds a node that joins a sequence of arrays governed by a given shape.

The input arrays should have the same shape or be able to be broadcast to the same shape.

For example, stacking 2 arrays of shapes [2,2] and [2,1] with the outer shape [2] works as follows

stack(arrays=[[[1,2],[3,4]], [5,6]], shape=[2]) = [[[1,2],[3,4]], [[5,5], [6,6]]]

§Arguments
  • nodes - vector of nodes containing arrays
  • outer_shape - shape defining how the input arrays are arranged in the resulting array
§Returns

New stack node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![3, 2, 3], INT32);
let t2 = array_type(vec![2, 3], INT32);
let shape = vec![2];
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.stack(vec![n1,n2], shape).unwrap();
Source

pub fn concatenate(&self, nodes: Vec<Node>, axis: u64) -> Result<Node>

Adds a node that joins a sequence of arrays along a given axis. This operation is similar to the NumPy concatenate.

The input arrays should have the same shape except in the given axis.

§Arguments
  • nodes - vector of nodes containing arrays
  • axis - axis along which the above arrays are joined
§Returns

New Concatenate node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![3, 2, 3], INT32);
let t2 = array_type(vec![3, 2, 10], INT32);
let shape = vec![2];
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.concatenate(vec![n1,n2], 2).unwrap();
Source

pub fn constant(&self, output_type: Type, value: Value) -> Result<Node>

Adds a node creating a constant of a given type and value.

§Arguments
  • output_type - type of a constant
  • value - value of a constant
§Returns

New constant node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = scalar_type(BIT);
let v = Value::from_scalar(0, BIT).unwrap();
let n = g.constant(t, v).unwrap();
Source

pub fn a2b(&self, a: Node) -> Result<Node>

Adds a node converting an integer array or scalar to the binary form.

Given an array of shape [a,b,c] and scalar type st, this node returns an array of shape [a,b,c,s] where s is the bit size of st. For example, an array of shape [1,2,3] with INT32 entries will be converted to a binary array of shape [1,2,3,32].

§Arguments

a - node containing an array or scalar

§Returns

New node converting an array/scalar to the binary form

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], INT32);
let n1 = g.input(t).unwrap();
let n2 = g.a2b(n1).unwrap();
Source

pub fn b2a(&self, a: Node, scalar_type: ScalarType) -> Result<Node>

Adds a node converting a binary array to an array of a given scalar type.

Given a binary array of shape [a,b,c] and a scalar type st of bit size c, this node returns an array of shape [a,b] with st entries. For example, a binary array of shape [2,3,32] can be converted to an array of shape [2,3] with INT32 entries.

§Arguments
  • a - node containing an array or scalar
  • scalar_type - scalar type
§Returns

New node converting an array from the binary form

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 32], BIT);
let n1 = g.input(t).unwrap();
let n2 = g.b2a(n1, INT32).unwrap();
Source

pub fn create_tuple(&self, elements: Vec<Node>) -> Result<Node>

Adds a node that creates a tuple from several (possibly, zero) elements.

§Arguments

elements - vector of nodes

§Returns

New node with a tuple

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![3, 2, 3], INT32);
let t2 = array_type(vec![2, 3], INT32);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.create_tuple(vec![n1,n2]).unwrap();
Source

pub fn create_vector( &self, element_type: Type, elements: Vec<Node>, ) -> Result<Node>

Adds a node that creates a vector from several (possibly, zero) elements of the same type.

§Arguments

elements - vector of nodes

§Returns

New node with a created vector

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let n1 = g.input(t.clone()).unwrap();
let n2 = g.input(t.clone()).unwrap();
let n3 = g.create_vector(t, vec![n1,n2]).unwrap();
Source

pub fn create_named_tuple(&self, elements: Vec<(String, Node)>) -> Result<Node>

Adds a node that creates a named tuple from several (possibly, zero) elements.

§Arguments

elements - vector of pairs (node name, node)

§Returns

New node creating a named tuple

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![3, 2, 3], INT32);
let t2 = array_type(vec![2, 3], INT32);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.create_named_tuple(vec![("node1".to_owned(), n1), ("node2".to_owned(), n2)]).unwrap();
Source

pub fn tuple_get(&self, tuple: Node, index: u64) -> Result<Node>

Adds a node that extracts an element of a tuple.

§Arguments
  • tuple - node containing a tuple
  • index - index of a tuple element between 0 and tuple length minus 1
§Returns

New node with an extracted element

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![3, 2, 3], INT32);
let t2 = array_type(vec![2, 3], INT32);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.create_tuple(vec![n1, n2]).unwrap();
let n4 = g.tuple_get(n3, 1).unwrap();
Source

pub fn named_tuple_get(&self, tuple: Node, key: String) -> Result<Node>

Adds a node that extracts an element of a named tuple.

§Arguments
  • tuple - node containing a named tuple
  • key - key of a tuple element
§Returns

New node extracting a tuple element

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t1 = array_type(vec![3, 2, 3], INT32);
let t2 = array_type(vec![2, 3], INT32);
let n1 = g.input(t1).unwrap();
let n2 = g.input(t2).unwrap();
let n3 = g.create_named_tuple(vec![("node1".to_owned(), n1), ("node2".to_owned(), n2)]).unwrap();
let n4 = g.named_tuple_get(n3, "node2".to_owned()).unwrap();
Source

pub fn vector_get(&self, vec: Node, index: Node) -> Result<Node>

Adds a node that extracts an element of a vector.

§Arguments
  • vec - node containing a vector
  • index - node containing the index of a tuple element
§Returns

New node extracting a vector element

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let n1 = g.input(t.clone()).unwrap();
let n2 = g.input(t.clone()).unwrap();
let n3 = g.create_vector(t, vec![n1,n2]).unwrap();
let index = g.constant(scalar_type(UINT32), Value::from_scalar(0, UINT32).unwrap()).unwrap();
let n4 = g.vector_get(n3, index).unwrap();
Source

pub fn zip(&self, nodes: Vec<Node>) -> Result<Node>

Adds a node that takes vectors V1(n, t1), V2(n, t2), …, Vk(n, tk) of the same length and returns a vector V(n, tuple(t1, …, tk)) (similar to zip).

§Arguments

nodes - vector of nodes containing input vectors

§Returns

New zip node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let vec_t = vector_type(3, t);
let n1 = g.input(vec_t.clone()).unwrap();
let n2 = g.input(vec_t.clone()).unwrap();
let n3 = g.zip(vec![n1,n2]).unwrap();
Source

pub fn repeat(&self, a: Node, n: u64) -> Result<Node>

Adds a node that creates a vector with n copies of a value of a given node.

§Arguments
  • a - node containing a value
  • n - number of copies
§Returns

New repeat node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let n1 = g.input(t).unwrap();
let n2 = g.repeat(n1, 10).unwrap();
Source

pub fn call(&self, graph: Graph, arguments: Vec<Node>) -> Result<Node>

Adds a node that calls another graph with inputs contained in given nodes.

The input graph must be finalized and have as many inputs as the number of provided arguments.

For example, let G be a graph implementing the function max(x,0), then call(G, [17]) = max(17, 0).

§Arguments
  • graph - graph with n input nodes
  • arguments - vector of n nodes
§Returns

New call node

§Example
let c = create_context().unwrap();

let g1 = c.create_graph().unwrap();
let t = array_type(vec![3, 2, 3], INT32);
let n1 = g1.input(t.clone()).unwrap();
let n2 = g1.repeat(n1, 10).unwrap();
let n3 = g1.vector_to_array(n2).unwrap();
n3.set_as_output().unwrap();
g1.finalize().unwrap();

let g2 = c.create_graph().unwrap();
let n4 = g2.input(t).unwrap();
let n5 = g2.add(n4.clone(), n4).unwrap();
let n6 = g2.call(g1, vec![n5]).unwrap();
Source

pub fn iterate(&self, graph: Graph, state: Node, input: Node) -> Result<Node>

Adds a node that iteratively computes a given finalized graph on the elements of a given vector and updates the state value accordingly.

This node calls another graph with 2 input nodes old_state and input and an output node that returns a tuple (new_state, output). This graph is used to map the elements of a given vector V to another vector W as follows:

graph(state_0, V[0]) -> (state1, W[0]),
graph(state_1, V[1]) -> (state2, W[1]),
...
graph(state_k, V[k]) -> (final_state, W[k]).

The output is a tuple (final_state, W). The initial state state_0 should be provided as an argument.

This node generalize map and reduce procedures (see MapReduce for more details).

For example, let G be a graph implementing the function max(x,0) and incrementing state if its output is negative, then iterate(G, 0, [-1,2,0,3,2]) = (1, [0,2,0,3,2]). The final state is equal to the number of negative values in the input vector.

§Arguments
  • graph - graph with 2 input nodes of types Ts and Ti and returning a tuple of type (Ts, To)
  • state - node containing an initial state of type Ts
  • input - node containing a vector with elements of type Ti
§Returns

New iterate node

§Example
let c = create_context().unwrap();

let t_s = scalar_type(BIT);
let t = scalar_type(INT32);
let vec_t = vector_type(10, t.clone());

// Graph that outputs 0 at even indices or input value at odd indices.
let g1 = c.create_graph().unwrap();
{
    let old_state = g1.input(t_s.clone()).unwrap();
    let input = g1.input(t.clone()).unwrap();
    let result = g1.mixed_multiply(input, old_state.clone()).unwrap();
    let new_state = g1.add(old_state, constant_scalar(&g1, 1, BIT).unwrap()).unwrap();
    let out_tuple = g1.create_tuple(vec![new_state, result]).unwrap();
    out_tuple.set_as_output().unwrap();
    g1.finalize().unwrap();
}

let g2 = c.create_graph().unwrap();
let initial_state = constant_scalar(&g2, 0, BIT).unwrap();
let input_vector = g2.input(vec_t).unwrap();
g2.iterate(g1, initial_state, input_vector).unwrap();
Source

pub fn array_to_vector(&self, a: Node) -> Result<Node>

Adds a node converting an array to a vector.

Given an array of shape [a,b,c], this node returns a vector of a arrays of shape [b,c].

§Arguments

a - node containing an array

§Returns

New node converting an array to a vector

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![4, 3, 2], INT32);
let n1 = g.input(t).unwrap();
let n2 = g.array_to_vector(n1).unwrap();
let index = g.constant(scalar_type(UINT32), Value::from_scalar(0, UINT32).unwrap()).unwrap();
let n3 = g.vector_get(n2.clone(), index).unwrap();

assert!(n2.get_type().unwrap().is_vector());
assert_eq!(n3.get_type().unwrap().get_shape(), vec![3,2]);
Source

pub fn vector_to_array(&self, a: Node) -> Result<Node>

Adds a node converting a vector to an array.

Given a vector of a arrays of shape [b,c], this node returns an array of shape [a,b,c].

§Arguments

a - node containing a vector

§Returns

New node converting a vector to an array

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], INT32);
let vec_t = vector_type(4, t);
let n1 = g.input(vec_t).unwrap();
let n2 = g.vector_to_array(n1).unwrap();

assert!(n2.get_type().unwrap().is_array());
assert_eq!(n2.get_type().unwrap().get_shape(), vec![4, 3, 2]);
Source

pub fn finalize(&self) -> Result<Graph>

Checks that the graph has an output node and finalizes the graph.

After finalization the graph can’t be changed.

§Returns

Finalized graph

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], INT32);
let vec_t = vector_type(4, t);
let n1 = g.input(vec_t).unwrap();
let n2 = g.vector_to_array(n1).unwrap();
n2.set_as_output().unwrap();
g.finalize().unwrap();
Source

pub fn get_nodes(&self) -> Vec<Node>

Returns the vector of nodes contained in the graph in order of construction.

§Returns

Vector of nodes of the graph

Source

pub fn set_output_node(&self, output_node: Node) -> Result<()>

Promotes a given node to the output node of the parent graph.

§Arguments

output_node - node to be set as output

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], INT32);
let vec_t = vector_type(4, t);
let n1 = g.input(vec_t).unwrap();
let n2 = g.vector_to_array(n1).unwrap();
g.set_output_node(n2).unwrap();
g.finalize().unwrap();
Source

pub fn get_output_node(&self) -> Result<Node>

Returns the output node of the graph.

§Returns

Output node of the graph

Source

pub fn get_id(&self) -> u64

Returns the ID of the graph.

A graph ID is a serial number of a graph between 0 and n-1 where n is the number of graphs in the parent context.

§Returns

Graph ID

Source

pub fn get_num_nodes(&self) -> u64

Returns the number of the graph nodes.

§Returns

Number of the graph nodes

Source

pub fn get_node_by_id(&self, id: u64) -> Result<Node>

Returns the node corresponding to a given ID.

§Arguments

id - node ID

§Returns

Node with a given ID

Source

pub fn get_context(&self) -> Context

Returns the context of the graph nodes.

§Returns

Context of the graph

Source

pub fn custom_op( &self, op: CustomOperation, arguments: Vec<Node>, ) -> Result<Node>

Adds a node computing a given custom operation.

Custom operations can be created by the user as public structs implementing the CustomOperationBody.

§Arguments
  • op - custom operation
  • arguments - vector of nodes used as input for the custom operation
§Returns

New custom operation node

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], BIT);
let n1 = g.input(t).unwrap();
let n2 = g.custom_op(CustomOperation::new(Not {}), vec![n1]).unwrap();
Source

pub fn print(&self, message: String, input: Node) -> Result<Node>

Adds a node which logs its input at runtime, and returns the input. This is intended to be used for debugging.

§Arguments
  • message - Informational message to be printed
  • input - Node to be printed
§Returns

The value of the node.

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = array_type(vec![3, 2], BIT);
let n1 = g.input(t).unwrap();
let n2 = g.print("n1:".into(), n1).unwrap();
Source

pub fn assert( &self, message: String, condition: Node, input: Node, ) -> Result<Node>

Adds a node which fails the execution at runtime if condition is false, and returns the input otherwise. This is intended to be used for debugging.

§Arguments
  • message - message to be returned for the failed assertion.
  • condition - BIT to be checked in the assertion.
  • input - Node to be returned for pass-through.
§Returns

The value of the node.

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let cond = g.input(scalar_type(BIT)).unwrap();
let t = array_type(vec![3, 2], BIT);
let n1 = g.input(t).unwrap();
let n2 = g.assert("Condition".into(), cond, n1).unwrap();
Source§

impl Graph

Methods which aren’t supposed to be imported in Python.

Source

pub fn add_node( &self, node_dependencies: Vec<Node>, graph_dependencies: Vec<Graph>, operation: Operation, ) -> Result<Node>

Adds an operation node to the graph and returns it.

§Arguments
  • node_dependencies - vector of nodes necessary to perform the given operation
  • graph_dependencies - vector of graphs necessary to perform the given operation
  • operation - operation performed by the node
§Returns

New operation node that gets added

Source

pub fn get_annotations(&self) -> Result<Vec<GraphAnnotation>>

Source

pub fn prepare_input_values<T: Clone>( &self, values: HashMap<&str, T>, ) -> Result<Vec<T>>

Rearrange given input values according to the names and the order of the related input nodes.

For example, given a graph with the first input node named ‘A’ and the second one named ‘B’ and input values {'B': v, 'A': w}, this function returns a vector [w, v].

§Arguments

values - hashmap of values keyed by node names

§Returns

Vector of values arranged by node names

§Example
let c = create_context().unwrap();
let g = c.create_graph().unwrap();
let t = scalar_type(BIT);
let n1 = g.input(t.clone()).unwrap();
n1.set_name("input1").unwrap();
let n2 = g.input(t.clone()).unwrap();
n2.set_name("input2").unwrap();

let mut input_map = HashMap::new();
input_map.insert("input2", 2);
input_map.insert("input1", 1);
let ordered_input = g.prepare_input_values(input_map).unwrap();

assert_eq!(vec![1,2], ordered_input);

Trait Implementations§

Source§

impl Clone for Graph

Source§

fn clone(&self) -> Self

Returns a new Graph value with a copy of the pointer to a computation graph.

1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Graph

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Graph

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for Graph

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Hashes the graph pointer.

§Arguments

state - state of a hash function that is changed after hashing the graph

1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Graph

Source§

fn eq(&self, other: &Self) -> bool

Tests whether self and other graphs are equal via comparison of their respective pointers.

§Arguments

other - another Graph value

§Returns

true if self and other are equal, false otherwise

1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Graph

Auto Trait Implementations§

§

impl Freeze for Graph

§

impl !RefUnwindSafe for Graph

§

impl Send for Graph

§

impl Sync for Graph

§

impl Unpin for Graph

§

impl !UnwindSafe for Graph

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V