def test_shapes(shape1: List[int], shape2: List[int], expected: List[int], dtype: str): """Test the shapes and np broadcasting. Don't really need to test the broadcasting as that is tested at C++ level. But try a few cases to be sure binding works correctly. Args: shape1 (List[int]): First tensor shape shape2 (List[int]): Second Tensor Shape expected (List[int]): Expected shape dtype (Str): Popart data type to use """ ir, graphs = create_ir(["A"]) g = graphs[0] settings = _ir.Settings(g, "new_settings") num_inputs = _ir.NumInputs(1, 1) opid = _ir.OperatorIdentifier("ai.onnx", "Identity", 1, num_inputs, 1) op = _ir.Op(opid, settings) shape = op.prettyNpOut(shape1, shape2) assert shape == list(expected) t1 = _ir.TensorInfo(dtype, shape1) t2 = _ir.TensorInfo(dtype, shape2) shape = op.prettyNpOut(t1, t2) assert shape == _ir.TensorInfo(dtype, expected)
def test_tensor_info_data_operator_eq_neq(dType1, shape1, metaShape1, dType2, shape2, metaShape2): """ Test the operator==() and operator!=() methods of a popart._internal.ir.TensorInfo object. """ tensorInfo1 = _ir.TensorInfo(dType1, shape1, metaShape1) tensorInfo2 = _ir.TensorInfo(dType2, shape2, metaShape2) areEqual = all( [dType1 == dType2, shape1 == shape2, metaShape1 == metaShape2]) assert (tensorInfo1 == tensorInfo2) == areEqual
def test_tensor_info_construction(): """ Test that we can construct a popart._internal.ir.TensorInfo object. """ dType = _ir.DataType.FLOAT16 dTypeStr = "FLOAT16" shape = [5, 5] shapeStr = "(5,5)" metaShape = [25, 1] _ir.TensorInfo(dType, shape) _ir.TensorInfo(dType, shape, metaShape) _ir.TensorInfo(dTypeStr, shapeStr) _ir.TensorInfo(dTypeStr, shape)
def _setup_call_and_repeat( pb_ir: _ir.Ir, pb_top_graph: _ir.Graph, pb_bottom_graph: _ir.Graph ) -> Tuple[_ir.Graph, _ir.op.CallOp, _ir.op.LoopOp]: """Setup the call and repeat ops, as well as the middle graph that the loop op will loop. Args: pb_ir (_ir.Ir): The _ir level Ir pb_top_graph (_ir.Graph): The _ir top level graph that will contain the loop op. pb_bottom_graph (_ir.Graph): The _ir user defined subgraph that will be called. Returns: Tuple[_ir.Graph, _ir.op.CallOp, _ir.op.LoopOp]: The created _ir-level middle graph, call op and loop op. """ # This is the graph we will repeat. pb_middle_graph = pb_ir.createGraph( _ir.GraphId( pb_ir.createUniqueSubgraphId( f"{pb_bottom_graph.id.str()}__loop_wrapper"))) opid = _ir.OperatorIdentifier("ai.graphcore", "Call", 1, _ir.NumInputs(), 0) op_name = pb_middle_graph.id.str() + '__call__' + pb_bottom_graph.id.str() ctx = get_current_context() # Call the bottom_graph pb_callop = pb_middle_graph.createOp_CallOp(opid, pb_bottom_graph, ctx._get_op_settings(op_name)) opid = _ir.OperatorIdentifier("ai.onnx", "Loop", 11, _ir.NumInputs(), 0) op_name = pb_top_graph.id.str() + '__loop__' + pb_middle_graph.id.str() # Loop the middle_graph pb_loop_op = pb_top_graph.createOp_LoopOp(opid, ctx._get_op_settings(op_name), pb_middle_graph) # Add mandatory loop iterator tensor to subgraph (is not an output) repeatIterId = _ir.addScope(pb_middle_graph, "Iterator___") pb_middle_graph.addInput(repeatIterId, _ir.TensorInfo(_ir.DataType.INT32, ())) # Add mandatory loop condition tensor to subgraph (is also an output) repeatCondId = _ir.addScope(pb_middle_graph, "LoopCond___") pb_middle_graph.addInput(repeatCondId, _ir.TensorInfo(_ir.DataType.BOOL, ())) pb_middle_graph.markAsOutput(repeatCondId) return pb_middle_graph, pb_callop, pb_loop_op
def add_tensor(graph: _ir.Graph, tid: str = "t", shape: List[int] = [1], t_type: _ir.TensorType = _ir.TensorType.ActGrad, d_type: _ir.DataType = _ir.DataType.FLOAT) -> _ir.Tensor: """Add a tensor to the given graph, with given properties. Args: graph (_ir.Graph): Graph to use tid (str, optional): Id for the tensor. Defaults to "t". shape (List[int], optional): Shape for the tensor. Defaults to [1]. t_type (_ir.TensorType, optional): Tensor type. Defaults to _ir.TensorType.ActGrad. d_type (_ir.DataType, optional): DataType for the tensor. Defaults to _ir.DataType.FLOAT. Returns: _ir.Tensor: the tensor that was just added. """ ts = _ir.Tensors(graph) tinfo = _ir.TensorInfo(d_type, shape) if t_type == _ir.TensorType.ActGrad: ts.addActGrad(tid) elif t_type == _ir.TensorType.Variable: data = np.random.rand(*shape).astype(np.float32) ts.addVarInit(tid, tinfo, data) elif t_type == _ir.TensorType.Const: data = np.random.rand(*shape).astype(np.float32) ts.addConstInit(tid, tinfo, data) return ts.get(tid)
def test_tensor_info_set0(): """ Test the set() method of a popart._internal.ir.TensorInfo object. """ dTypeOld = _ir.DataType.FLOAT dTypeNew = _ir.DataType.INT8 tensorInfo = _ir.TensorInfo(dTypeOld, [4, 2]) tensorInfo.set(dTypeNew) assert tensorInfo.dataType() == dTypeNew
def init(shape: Iterable[int], dtype: dtypes.dtype, name: Optional[str] = None) -> Tensor: """ Init Op: create a tensor with zero values. The returned tensor is not considered a variable. Args: dtype (dtypes.dtype): Data type for the output Tensor shape (Tuple[int]): Shape of the output tensor. name (str): Name to use for the poplar stream. Returns: Tensor: The output tensor streamed from host. """ ctx = get_current_context() g = ctx.graph pb_g = g._pb_graph info = _ir.TensorInfo(dtype._pb_dtype, list(shape)) opid_init = _ir.OperatorIdentifier("ai.graphcore", "Init", 1, _ir.NumInputs(0), 1) op = pb_g.createConnectedOp_InitOp( {}, {0: g._create_tensor_id(name)}, opid_init, info, _ir.TensorType.ActGrad, _ir.InitType.Zero, ctx._get_op_settings('init'), -1, ) return Tensor._from_pb_tensor(op.outTensor(0))
def prepare_remote_buffer(t: Tensor, remote_buffer_handle: Optional[RemoteBufferHandle], g: Graph) -> RemoteBufferHandle: """Prepare the remote buffer. Args: t (Tensor): Input tensor to the op. remote_buffer_handle (Optional[RemoteBufferHandle]): If set: The remote buffer handle to use in the preparation g (Graph): The graph to set the remote buffer info to Raises: ValueError: If there is a shape or type mismatch between `t` and `remote_buffer_handle` Returns: RemoteBufferHandle: The remote buffer handle used in the preparation. """ if remote_buffer_handle is None: shape = t._pb_tensor.info.shape() d_type = dtype.as_dtype(t._pb_tensor.info.data_type_lcase()) # Check for existing buffer handles existing_buffers = RemoteBufferHandle._buffers for _, rbh in existing_buffers.items(): if rbh.tensor_shape == shape and rbh.tensor_dtype == d_type: remote_buffer_handle = rbh break if remote_buffer_handle is None: # Create handle if not found remote_buffer_handle = RemoteBufferHandle(remote_buffer_id=None, tensor_shape=shape, tensor_dtype=d_type, repeats=1) # The remote buffer handle may be set, and may have empty shape and dtype if remote_buffer_handle.tensor_shape is None: remote_buffer_handle.tensor_shape = t._pb_tensor.info.shape() if remote_buffer_handle.tensor_dtype is None: remote_buffer_handle.tensor_dtype = dtype.as_dtype( t._pb_tensor.info.data_type_lcase()) info = _ir.TensorInfo(remote_buffer_handle.tensor_dtype._pb_dtype, remote_buffer_handle.tensor_shape) if t._pb_tensor.info.dataType() != info.dataType(): raise ValueError( f"DataType of {t.id} ({t._pb_tensor.info.dataType()}) " f"does not match that of the RemoteBufferHandle ({info.dataType()})" ) if t._pb_tensor.info.shape() != info.shape(): raise ValueError( f"DataType of {t.id} ({t._pb_tensor.info.shape()}) " f"does not match that of the RemoteBufferHandle ({info.shape()})") g._ir._pb_ir.setRemoteBufferInfo( remote_buffer_handle.remote_buffer_id, _ir.RemoteBufferInfo(info, remote_buffer_handle.repeats)) return remote_buffer_handle
def test_tensor_info_set2(dTypeOld, dTypeNew, shapeOld, shapeNew, metaShapeOld, metaShapeNew): """ Test the set() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(dTypeOld, shapeOld, metaShapeOld) tensorInfo.set(dTypeNew, shapeNew, metaShapeNew) assert tensorInfo.shape() == shapeNew assert tensorInfo.metaShape() == metaShapeNew assert tensorInfo.dataType() == dTypeNew
def test_tensor_data_reset_data(): """ Test the resetData() method of a popart._internal.ir.TensorData object. """ a = np.random.rand(2, 3, 4) b = np.random.rand(2, 3, 4) tInfo = _ir.TensorInfo(_ir.DataType.FLOAT, a.shape) tData = _ir.TensorData(tInfo, a) tData.resetData(tInfo, b)
def add_actgrad_tensor(id_: str, shape: List[int], g: "_ir.Graph", dtype: "_ir.DataType" = _ir.DataType.FLOAT): t_info = _ir.TensorInfo(dtype, shape) g.addActGrad(id_) t = g.getTensor(id_) t.info = t_info return t
def test_tensor_has_tensor_data(): """ Test the hasTensorData() method of a popart._internal.ir.Tensor object. """ ir = _ir.Ir() g = ir.createGraph("g") t = _ir.Tensor("t", _ir.TensorType.ActGrad, g) assert t.hasTensorData() == False buffer = np.random.rand(2, 3, 4) tInfo = _ir.TensorInfo(_ir.DataType.FLOAT, buffer.shape) t.setTensorData(tInfo, buffer) assert t.hasTensorData() == True
def test_loop_op(): """Test setting up the loop op. """ _, graphs = create_ir(["sub_graph"]) # main graph and 'sub_graph' main = graphs[0] sub_graph = graphs[1] num_inputs = _ir.NumInputs(2, 2) main.addConstInit("loopcount", _ir.TensorInfo(_ir.DataType.INT64, []), np.array(10)) main.addConstInit("keepgoing", _ir.TensorInfo(_ir.DataType.BOOL, []), np.array(True)) a = add_actgrad_tensor("a", [1, 2, 3], main, _ir.DataType.FLOAT) b = add_actgrad_tensor("b", [1, 2, 3], main, _ir.DataType.FLOAT) sub_graph.addInput("loopcount", _ir.TensorInfo(_ir.DataType.INT64, [])) sub_graph.addInput("keepgoing", _ir.TensorInfo(_ir.DataType.BOOL, [])) sub_graph.addInput("a", a.info) sub_graph.addInput("b", b.info) opid = _ir.OperatorIdentifier("ai.graphcore", "Loop", 1, num_inputs, 1) settings = _ir.Settings(main, "new_settings") add = sub_graph.createConnectedOp_AddOp( { 0: a.id, 1: b.id }, {0: "out"}, _ir.OperatorIdentifier("ai.onnx", "Add", 1, num_inputs, 1), settings, ) sub_graph.markAsOutput(add.outTensor(0).id) sub_graph.markAsOutput("keepgoing") ins: Dict[int, str] = {0: "loopcount", 1: "keepgoing", 2: a.id} outs: Dict[int, str] = {0: add.outTensor(0).id} op = main.createConnectedOp_LoopOp(ins, outs, opid, settings, sub_graph) assert op.getCalledGraphs()[0] == sub_graph assert op.getCalledGraphIds()[0] == "sub_graph"
def test_graph_graph_outputs(): """ Test we can add/remove graph outputs. """ ir = _ir.Ir() g1Id = _ir.GraphId("g1") g1 = ir.createGraph(g1Id) # We add inputs as a way of adding tensors to the graph that we can mark as # outputs. g1.addInput("t0", _ir.TensorInfo(_ir.DataType.FLOAT16, [5, 5])) g1.addInput("t1", _ir.TensorInfo(_ir.DataType.FLOAT16, [5, 5])) g1.addInput("t2", _ir.TensorInfo(_ir.DataType.FLOAT16, [5, 5])) # Check markAsInput. check_graph_outputs(g1, []) g1.markAsOutput("t0") check_graph_outputs(g1, ["t0"]) g1.markAsOutput(0, "t1", False) check_graph_outputs(g1, ["t1", "t0"]) g1.markAsOutput(0, "t2", True) check_graph_outputs(g1, ["t2", "t0"]) # Check getOutputId. assert g1.getOutputId(0) == "t2" assert g1.getOutputId(1) == "t0" # Check getOutputIndex assert g1.getOutputIndex("t2") == 0 assert g1.getOutputIndex("t0") == 1 with pytest.raises(popart.popart_exception) as excinfo: g1.getOutputIndex("nonExistingTensor") # Check hasOutputId. assert g1.hasOutputId("t0") assert g1.hasOutputId("t2") assert not g1.hasOutputId("t1") # Check removeInput. g1.removeOutput(1) check_graph_outputs(g1, ["t2"]) g1.removeOutput("t2") check_graph_outputs(g1, [])
def test_graph_graph_inputs(): """ Test we can add/remove graph inputs. """ ir = _ir.Ir() g1Id = _ir.GraphId("g1") g1 = ir.createGraph(g1Id) # Check initially graph inputs are empty. check_graph_inputs(g1, []) # Check addInput. g1.addInput("inputA", _ir.TensorInfo(_ir.DataType.FLOAT16, [5, 5])) check_graph_inputs(g1, ["inputA"]) g1.addInput("inputB", _ir.TensorInfo(_ir.DataType.FLOAT, [65, 5])) check_graph_inputs(g1, ["inputA", "inputB"]) g1.addInput(1, "input1", _ir.TensorInfo(_ir.DataType.FLOAT, [65, 5]), False) check_graph_inputs(g1, ["inputA", "input1", "inputB"]) g1.addInput(1, "input2", _ir.TensorInfo(_ir.DataType.FLOAT, [65, 5]), True) check_graph_inputs(g1, ["inputA", "input2", "inputB"]) # Check getInputId. assert g1.getInputId(0) == "inputA" assert g1.getInputId(1) == "input2" assert g1.getInputId(2) == "inputB" # Check getInputIndex assert g1.getInputIndex("inputA") == 0 assert g1.getInputIndex("input2") == 1 assert g1.getInputIndex("inputB") == 2 with pytest.raises(popart.popart_exception) as excinfo: g1.getInputIndex("nonExistingTensor") # Check hasInputId. assert g1.hasInputId("inputA") assert not g1.hasInputId("input1") # Check removeInput. g1.removeInput(1) check_graph_inputs(g1, ["inputA", "inputB"]) g1.removeInput("inputA") check_graph_inputs(g1, ["inputB"])
def test_nll_op(reduction: _ir.ReductionType, ignoreIndex: int, connected: bool) -> None: """Test the Nll Op, special case with unusual arguments. Args: reduction (_ir.ReductionType): The reduction type to use ignoreIndex (int): The index to ignore. Note this has to be converted to an _ir.OptionalInt connected (bool): Whether to use the createConnected<opname> function or just create<opname> """ _, graphs = create_ir() main = graphs[0] num_inputs = _ir.NumInputs(2, 2) in0 = add_actgrad_tensor("in0", [8, 2, 3], main) t_info = _ir.TensorInfo(_ir.DataType.INT32, [8, 2]) main.addActGrad("label") l = main.getTensor("label") l.info = t_info out0 = add_actgrad_tensor("out0", [1, 2, 3], main) opid = _ir.OperatorIdentifier("ai.graphcore", "nll", 1, num_inputs, 1) settings = _ir.Settings(main, "nll") if connected: ins: Dict[int, str] = {0: in0.id, 1: l.id} outs: Dict[int, str] = {0: out0.id} op = main.createConnectedOp_NllOp( ins, outs, ignoreIndex=_ir.OptionalInt(ignoreIndex), # <- Note this reduction=_ir.ReductionType.Mean, inputIsLogProbability=True, opid=opid, settings=settings) return op = main.createOp_NllOp( opid=opid, ignoreIndex=_ir.OptionalInt(ignoreIndex), # <- Note this reduction=_ir.ReductionType.Mean, inputIsLogProbability=True, settings=settings) op.connectInTensor(0, in0.id) op.connectInTensor(1, l.id) op.connectOutTensor(0, out0.id) op.setup()
def subgraph_input(shape: Iterable[int], dtype: dtypes.dtype, name: Optional[str] = None, by_ref: bool = False) -> Tensor: """Create a new input tensor to the current graph. You can use this function when defining a subgraph to create a new input tensor. When you call that subgraph, you will have to pass a tensor to the subgraph for this input. Example: ``` import popart.ir as pir def add_w(x): w = pir.subgraph_input(x.shape, x.dtype, "w") return w + x ir = pir.Ir() with ir.main_graph(): w = pir.variable(1) x = pir.variable(3) add_w_graph = ir.create_graph(add_w, x, w) y = ops.call(add_w_graph, x, w) ``` Args: shape (Tuple[int, ...]) The shape of the Tensor dtype (dtype): The data type of the tensor name (Optional[str]): The name of the tensor. """ g = gcg() pb_g = g._pb_graph pb_id = g._create_tensor_id(name) pb_info = _ir.TensorInfo(dtype._pb_dtype, list(shape)) pb_g.addInput(pb_id, pb_info) t = Tensor._from_pb_tensor(pb_g.getTensor(pb_id)) if by_ref: g._by_ref_inputs.add(t) return t
def add_random_tensor(id_: str, t_type: "_ir.TensorType", shape: List[int], g: "_ir.Graph") -> "_ir.Tensor": data = np.random.random(shape).astype(np.float32) t_info = _ir.TensorInfo(_ir.DataType.FLOAT, shape) if t_type == _ir.TensorType.ActGrad: raise TypeError("Use add_actgrad_tensor for adding actgrad tensors. " "Actgrad tensors have no initial value.") elif t_type == _ir.TensorType.Variable: g.addVarInit(id_, t_info, data, "") return g.getTensor(id_) elif t_type == _ir.TensorType.Const: g.addConstInit(id_, t_info, data, "") return g.getTensor(id_) else: raise TypeError("Incorrect tensor type")
def constant( data: Union[np.ndarray, Iterable[Any], int, float], dtype: Optional[dtypes.dtype] = None, name: Optional[str] = None, downcast: bool = True, ) -> Constant: """A constant tensor that is initialised with data during graph creation. This tensor cannot change during the runtime of a model. The intended use of this class is when doing operations between `popart.ir.Tensor` instances and other types, such as `numpy.ndarray` objects, numbers, or list or tuples of numbers. Example: ``` import popart.ir as pir ir = pir.Ir() with ir.main_graph(): a = pir.constant(0) # The `1` will be implicitly converted to a `Constant`. b = a + 1 ``` Args: data (np.array, or a value numpy can use to construct an np.ndarray): The data used to initialise the tensor. dtype (Optional[dtype]): The data type of the tensor to be created, if not specified Numpy will infer the data type and be downcasted to 32 bits if necessary. name (Optional[str]): The name of the tensor. Defaults to `None`. """ g = gcg() pb_g = g._pb_graph np_dtype = dtype.as_numpy() if dtype is not None else None np_data: np.ndarray = np.array(data, dtype=np_dtype) if np_data.dtype in downcast_np_dtypes and downcast and dtype is None: np_data = np_data.astype(downcast_np_dtypes[np_data.dtype]) pir_dt = dtypes.dtype.as_dtype(np_data) info = _ir.TensorInfo(pir_dt._pb_dtype, np_data.shape) pb_id = g._create_tensor_id(name) pb_g.addConstInit(pb_id, info, np_data) return Constant._from_pb_tensor(pb_g.getTensor(pb_id))
def variable( data: Union[np.ndarray, Iterable[Any], int, float, bool], dtype: Optional[dtypes.dtype] = None, name: Optional[str] = None, downcast: bool = True, ) -> Variable: """ A variable tensor that is initialised with data during graph creation. This tensor can be used to represent a model weight or any other parameter that can change while running a model. Must be created in the main graph scope. Example: ``` import popart.ir as pir with pir.Ir().main_graph(): a = pir.variable(0) ``` Args: data (np.ndarray, or a value numpy can use to construct an np.ndarray): The data used to initialise the tensor. dtype (Optional[dtype]): The data type of the tensor to be created, if not specified Numpy will infer the data type and be downcasted to 32 bits if necessary. name (Optional[str]): The name of the tensor. Defaults to `None`. downcast (bool): If no dtype is provided 64 bit float/ints will be downcasted to 32 bit variants. Default to True. """ g = gcg() pb_g = g._pb_graph np_dtype = dtype.as_numpy() if dtype is not None else None np_data: np.ndarray = np.array(data, dtype=np_dtype) if np_data.dtype in downcast_np_dtypes and downcast and dtype is None: np_data = np_data.astype(downcast_np_dtypes[np_data.dtype]) pir_dt = dtypes.dtype.as_dtype(np_data) info = _ir.TensorInfo(pir_dt._pb_dtype, np_data.shape) pb_id = g._create_tensor_id(name) pb_g.addVarInit(pb_id, info, np_data) return Variable._from_pb_tensor(pb_g.getTensor(pb_id))
def h2d_stream(shape: Iterable[int], dtype: dtype, name: Optional[str] = None) -> HostToDeviceStream: g = gcg() mg = g.ir().main_graph() if g.name != mg.name: raise ValueError( "popart.ir: Can only call `h2d_stream` in context of main graph. You are in context of graph:", g.name) pb_mg = mg._pb_graph if name is None: name = "h2d_stream" name = mg._create_tensor_id(name) pb_mg.addStream(name, _ir.TensorInfo(dtype._pb_dtype, list(shape)), name) return HostToDeviceStream._from_tensor( Tensor._from_pb_tensor(pb_mg.getTensor(name)))
def test_tensor_tensor_data(): """ Test the tensorData() and setTensorData() methods of a popart._internal.ir.Tensor object. """ ir = _ir.Ir() g = ir.createGraph("g") t = _ir.Tensor("t", _ir.TensorType.ActGrad, g) with pytest.raises(popart.popart_exception) as e_info: t.tensorData() assert e_info.value.args[0] == "Data not set for t" with pytest.raises(popart.popart_exception) as e_info: t.tensorData_const() assert e_info.value.args[0] == "Data not set for t" buffer = np.random.rand(2, 3, 4) tInfo = _ir.TensorInfo(_ir.DataType.FLOAT, buffer.shape) t.setTensorData(tInfo, buffer) # TODO(T42205): Test that the returned tensor data matches the one that was # set. t.tensorData() t.tensorData_const()
def test_tensor_info_meta_shape(metaShape): """ Test the metaShape() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(_ir.DataType.FLOAT, [6], metaShape) assert tensorInfo.metaShape() == metaShape
def test_tensor_info_nelms(dType, shape, nBytesInDType): """ Test the nelms() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(dType, shape) assert tensorInfo.nbytes() == np.prod(shape) * nBytesInDType
assert op.inTensor(i) == t assert op.hasInput(i) assert op.inId(i) == t.id for i, t in outs.items(): assert op.outTensor(i) == t assert op.hasOutput(i) assert op.outId(i) == t.id assert op.getCalledGraphs() == [] assert op.getCalledGraphIds() == [] @pytest.mark.parametrize("connected", [True, False]) # yapf: disable, pylint: disable-all @pytest.mark.parametrize("op_name,inplace,kwargs", [ ("DynamicUpdateOp", False, {"axes_":[0], "sizes_":[1], "noOverlap_":False, "updateInInfo_": _ir.TensorInfo()}), ("DynamicUpdateInplaceOp", False, {"axes_":[0], "sizes_":[1], "noOverlap_":False, "updateInInfo_": _ir.TensorInfo()}), ]) # yapf: enable, pylint: enable-all def test_ternary_ops(connected: bool, inplace: bool, op_name: str, kwargs: Dict[str, Any]) -> None: """Test unary (3 in, 1 out) ops Args: connected (bool): Whether to use the createConnected<opname> function or just create<opname> inplace (bool): Whether this op is inplace op_name (str): Name of the op e.g. AddOp kwargs (Dict[str, Any]): Additional kwargs to pass to the ops """ _, graphs = create_ir()
def test_tensor_info_dim(shape): """ Test the dim() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(_ir.DataType.FLOAT, shape) for i, dim in enumerate(shape): assert tensorInfo.dim(i) == dim
def test_tensor_info_data_type(dType): """ Test the dataType() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(dType, [6]) assert tensorInfo.dataType() == dType
def test_tensor_info_data_type_lcase(dType, dTypeLowerCase): """ Test the data_type_lcase() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(dType, [5, 5]) assert tensorInfo.data_type_lcase() == dTypeLowerCase
def test_tensor_info_shape(dType, shape, expectedShape): """ Test the shape() method of a popart._internal.ir.TensorInfo object. """ tensorInfo = _ir.TensorInfo(dType, shape) assert tensorInfo.shape() == expectedShape
def make_main_graph( num_inputs: int = 2 ) -> Tuple[_ir.Ir, List[_ir.Tensor], List[_ir.Tensor]]: """ Creates the following graph, with num_inputs inputs, alternating data inputs and variable inputs. Init (act) Init (var) Init (act) Init (var) │ │ │ │ ▼ ▼ ▼ ▼ Hostload Hostload Hostload Hostload etc.. │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ┌────────────────────────────────────┐ │ Call │ │ │ └──────────────────┬─────────────────┘ │ │ ▼ HostStore Args: num_inputs (int, optional): Number of main graph inputs. Defaults to 2. Returns: Tuple[_ir.Ir, List[_ir.Tensor], List[_ir.Tensor]]: The ir, The subgraph output tensors, and the variable tensors """ ir, _ = create_ir() main = ir.getMainGraph() t_info = _ir.TensorInfo(_ir.DataType.FLOAT, [1, 2, 3]) inits: Dict[int, _ir.Op] = dict() hostloads: Dict[int, _ir.Op] = dict() inputs = {i: t_info for i in range(num_inputs)} for i in range(len(inputs)): # init i opid = _ir.OperatorIdentifier("ai.onnx", f"Init{i}", 1, _ir.NumInputs(0, 0), 1) actgrads = [] vars_ = [] settings = _ir.Settings(main, f"input{i}") if i % 2 == 0: ttype = _ir.TensorType.ActGrad inits[i] = main.createConnectedOp_InitOp({}, {0: f"init{i}"}, opid, t_info, ttype, _ir.InitType.Zero, settings, 0) actgrads.append(inits[i]) else: ttype = _ir.TensorType.Variable inits[i] = main.createConnectedOp_InitOp({}, {0: f"init{i}"}, opid, t_info, ttype, _ir.InitType.Zero, settings, 0) vars_.append(inits[i].outTensor(0)) # hostload i opid = _ir.OperatorIdentifier("ai.onnx", f"HostLoad{i}", 1, _ir.NumInputs(1, 1), 1) hostloads[i] = main.createConnectedOp_HostLoadOp( {0: inits[i].outTensor(0).id}, {0: f"hostload{i}"}, opid, settings, f"hl{i}") settings = _ir.Settings(main, "call") fwd = make_sub_graph(ir, inputs) fwd_outs = [fwd.getTensor(tid) for tid in fwd.getOutputIds()] opid = _ir.OperatorIdentifier("ai.graphcore", "Call", 1, _ir.NumInputs(num_inputs, num_inputs), 1) call = main.createConnectedOp_CallOp( {i: hostloads[i].outTensor(0).id for i in range(len(hostloads))}, {0: "call0"}, opid, fwd, settings) # host store opid = _ir.OperatorIdentifier("ai.onnx", "HostStore", 1, _ir.NumInputs(1, 1), 1) settings = _ir.Settings(main, "host_store") _ = main.createConnectedOp_HostStoreOp({0: call.outTensor(0).id}, {}, opid, settings, "hs1") deviceInfo = popart.DeviceManager().createIpuModelDevice({}) ir.setDeviceInfo(deviceInfo) ir.setIsPrepared() ir.logIr() print(fwd_outs, vars_) return ir, fwd_outs, vars_