예제 #1
0
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)
예제 #2
0
def test_accumulate_op(connected: bool) -> None:
    """Test the Accumulate Op.

    Args:
        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)
    weight = add_random_tensor("weight", _ir.TensorType.Variable, [4], main)
    grad = add_actgrad_tensor("grad", [4], main)
    out = add_actgrad_tensor("updated_weight", [4], main)

    opid = _ir.OperatorIdentifier("ai.graphcore", "Accumulate", 1, num_inputs,
                                  1)
    settings = _ir.Settings(main, "accumulate")

    if connected:
        ins: Dict[int, str] = {0: weight.id, 1: grad.id}
        outs: Dict[int, str] = {0: out.id}
        op = main.createConnectedOp_AccumulateOp(ins,
                                                 outs,
                                                 _ir.AccumulationType.Add,
                                                 _ir.OptimizerValue(0.5),
                                                 settings=settings)
        return
    op = main.createOp_AccumulateOp(_ir.AccumulationType.Add,
                                    _ir.OptimizerValue(0.5),
                                    settings=settings)
    op.connectInTensor(0, weight.id)
    op.connectInTensor(1, grad.id)
    op.connectOutTensor(0, out.id)
    op.setup()
예제 #3
0
def test_tiedgather_op(connected: bool) -> None:
    """Test the Tied Gather Op.

    Args:
        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], main, _ir.DataType.INT32)
    indices = add_random_tensor("indices", _ir.TensorType.Variable, [16, 4],
                                main)
    out0 = add_actgrad_tensor("out0", [8, 4], main)

    settings = _ir.Settings(main, "tiedgather")
    if connected:
        ins: Dict[int, str] = {0: in0.id, 1: indices.id}
        outs: Dict[int, str] = {0: out0.id}
        op = main.createConnectedOp_TiedGatherOp(
            ins,
            outs,
            axis_=0,
            available_memory_proportion_=_ir.OptionalFloat(0.4),
            settings=settings)
        op.setup()
        return
    op = main.createOp_TiedGatherOp(
        axis_=0,
        available_memory_proportion_=_ir.OptionalFloat(0.4),
        settings=settings)
    op.connectInTensor(0, in0.id)
    op.connectInTensor(1, indices.id)
    op.connectOutTensor(0, out0.id)
    op.setup()
예제 #4
0
def test_ipu_copy_op(source: int, destination: int, connected: bool) -> None:
    """Test the ipu copy op

    Args:
        source (int): Source IPU
        destination (int): Destination IPU
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
    """
    _, graphs = create_ir()
    g = graphs[0]
    in0 = add_actgrad_tensor("in0", [1, 2, 3], g)
    opid = _ir.OperatorIdentifier("ai.graphcore", "IpuCopy", 1,
                                  _ir.NumInputs(0, 0), 1)
    settings = _ir.Settings(g, "new_settings")
    if connected:
        op = g.createConnectedOp_IpuCopyOp({0: in0.id}, {0: "outId"}, opid,
                                           source, destination, settings)
        op.setup()
    else:
        op = g.createOp_IpuCopyOp(opid, destination, settings)
        op.connectInTensor(0, in0.id, source)
        op.createAndConnectOutTensor(0, "outId")
        op.setup()

    assert op.inTensor(0) == in0
    assert op.hasInput(0)
    assert op.hasOutput(0)
    assert op.outId(0) == "outId"
    assert op.getDestIpu() == destination
    assert op.getSourceIpu() == source
    assert op.getSourceIpu("in0") == source
    assert op.getMinSourceIpu() == source
    assert op.getMaxSourceIpu() == source
예제 #5
0
def test_accumulate_zero_op(connected: bool) -> None:
    """Test the AccumulatorZeroOp.

    Args:
        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)
    input_ = add_actgrad_tensor("input", [4], main)
    updater = add_actgrad_tensor("updater", [4], main)
    factor = add_actgrad_tensor("factor", [4], main)
    out = add_actgrad_tensor("updated_weight", [4], main)

    opid = _ir.OperatorIdentifier("ai.graphcore", "AccumulatorZeroOp", 1,
                                  num_inputs, 1)
    settings = _ir.Settings(main, "AccumulatorZeroOp")

    if connected:
        ins: Dict[int, str] = {0: input_.id, 1: updater.id, 2: factor.id}
        outs: Dict[int, str] = {0: out.id}
        op = main.createConnectedOp_AccumulatorZeroOp(ins,
                                                      outs,
                                                      settings=settings)
        return
    op = main.createOp_AccumulatorZeroOp(settings=settings)
    op.connectInTensor(0, input_.id)
    op.connectInTensor(1, updater.id)
    op.connectInTensor(2, factor.id)
    op.connectOutTensor(0, out.id)
    op.setup()
예제 #6
0
def test_init_op(init_type: "_ir.InitType", connected: bool):
    """Test the special case of the init op

    Args:
        init_type (_ir.InitType): The initialisation type to use (zero/no init)
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
    """
    _, graphs = create_ir()
    g = graphs[0]
    out0 = add_actgrad_tensor("out0", [1, 2, 3], g)
    opid = _ir.OperatorIdentifier("ai.onnx", "Init", 1, _ir.NumInputs(0, 0), 1)
    settings = _ir.Settings(g, "new_settings")
    if connected:
        op = g.createConnectedOp_InitOp({}, {0: out0.id}, opid, out0.info,
                                        out0.tensorType(), init_type, settings,
                                        0)
    else:
        op = g.createOp_InitOp(opid, out0.info, out0.tensorType(), init_type,
                               settings, 0)
        op.connectOutTensor(0, out0.id)
        op.setup()
    assert not op.hasInput(0)
    assert op.outTensor(0) == out0
    assert op.hasOutput(0)
    assert op.outId(0) == out0.id
예제 #7
0
def test_op_attributes(attribute: str, shorthand: str, input_id: int):
    """Test the various attributes that can be applied to ops.

    Args:
        attribute (str): Name of the attribute 
        shorthand (str): Shorthand of the attribute e.g. VirtualGraphId -> VGraphId
        input_id (int): Long int for the id to use for the attribute. 
    """
    _, 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)
    getter = getattr(op, "get" + attribute)
    setter = getattr(op, "set" + attribute)
    hasser = getattr(op, "has" + attribute)
    get_optional = getattr(op, "getOptional" + shorthand)
    assert not hasser()
    id_ = getattr(_ir, "Optional" + shorthand)
    # Unset optional
    setter(id_())
    assert get_optional() == id_()
    with pytest.raises(popart.popart_exception) as e_info:
        getter()
        assert (e_info.value.args[0] == f"Cannot return {attribute} for Op")

    assert not hasser()
    # Set optional == 0
    setter(id_(input_id))
    assert getter() == input_id
    assert hasser()
    assert get_optional() == id_(input_id)
예제 #8
0
def test_call_op(connected: bool):
    """Test the special case of the call op

    Args:
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
    """
    _, graphs = create_ir(["sub_graph"])  # main graph and 'sub_graph'
    main = graphs[0]
    sub_graph = graphs[1]
    num_inputs = _ir.NumInputs(1, 1)
    in0 = add_actgrad_tensor("in0", [1, 2, 3], main)
    out0 = add_actgrad_tensor("out0", [1, 2, 3], main)

    sub_graph.addInput("inputA", in0.info)

    opid = _ir.OperatorIdentifier("ai.graphcore", "Call", 1, num_inputs, 1)

    settings = _ir.Settings(main, "new_settings")

    if connected:
        ins: Dict[int, str] = {0: in0.id}
        outs: Dict[int, str] = {0: out0.id}
        op = main.createConnectedOp_CallOp(ins, outs, opid, sub_graph,
                                           settings)
    else:
        op = main.createOp_CallOp(opid, sub_graph, settings)
        op.connectInTensor(0, in0.id)
        op.connectOutTensor(0, out0.id)
        op.setup()

    assert op.getCalledGraphs()[0] == sub_graph
    assert op.getCalledGraphIds()[0] == "sub_graph"
예제 #9
0
def test_multi_graph():
    """Test adding ops to multiple graphs.
    """
    ir, graphs = create_ir(["A", "B"])
    g = graphs[0]
    h = graphs[1]
    settings_g = _ir.Settings(g, "settings_g")
    settings_h = _ir.Settings(h, "settings_h")
    num_inputs = _ir.NumInputs(1, 1)

    opid = _ir.OperatorIdentifier("ai.onnx", "Identity", 1, num_inputs, 1)
    op1 = _ir.Op(opid, settings_g)
    op2 = _ir.Op(opid, settings_h)
    assert op1.id == 100  # default Id
    assert op2.id == 101  # default Id + 1
    assert op1.opid == opid == op2.opid
    assert op1.getGraph() == g
    assert op2.getGraph() == h
예제 #10
0
def test_settings():
    """Test creating settings
    """
    ir, graphs = create_ir(["A"])
    g = graphs[0]
    settings = _ir.Settings(g, "new_settings")

    assert settings.name == "new_settings"
    assert settings.getIr() == ir
예제 #11
0
def test_remote_load_op(connected: bool, use_offset: bool,
                        inplace: bool) -> None:
    """Test that the input and output tensors of remote load op are correct.

    Args:
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
        use_offset (bool): Whether or not to specify the optional offset Tensor
        inplace (bool): Whether or not to use the inplace version
    """
    _, graphs = create_ir()
    g = graphs[0]
    t = add_actgrad_tensor("t", [1, 2, 3], g)
    opid = _ir.OperatorIdentifier("ai.onnx", "Init", 1, _ir.NumInputs(0, 0), 1)
    settings = _ir.Settings(g, "new_settings")
    out_id = "out_id"

    offset = add_actgrad_tensor("offset", [1], g)

    opCreator = g.createOp_RemoteLoadOp if not inplace else g.createOp_RemoteLoadInplaceOp
    connectedOpCreator = g.createConnectedOp_RemoteLoadOp if not inplace else g.createConnectedOp_RemoteLoadInplaceOp

    if use_offset:
        if connected:
            op = connectedOpCreator({
                0: t.id,
                1: offset.id
            }, {0: "out_id"}, opid, settings, 1)
        else:
            op = opCreator(opid, settings, 1)
            op.connectInTensor(0, t.id)
            op.connectInTensor(1, offset.id)
            op.createAndConnectOutTensor(0, out_id)
    else:
        if connected:
            op = connectedOpCreator({
                0: t.id,
            }, {0: "out_id"}, opid, settings, 1)
        else:
            op = opCreator(opid, settings, 1)
            op.connectInTensor(0, t.id)
            op.createAndConnectOutTensor(0, out_id)

    op.setup()

    assert op.hasInput(0)
    assert op.inTensor(0) == t
    assert op.inId(0) == t.id
    if use_offset:
        assert op.hasInput(1)
        assert op.inTensor(1) == offset
        assert op.inId(1) == offset.id
    else:
        assert not op.hasInput(1)
    assert op.hasOutput(0)
    assert op.outId(0) == out_id
예제 #12
0
def test_sparse_accumulate_op(connected: bool) -> None:
    """Test the SparseAccumulateOp.

    Args:
        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)
    input_ = add_actgrad_tensor("input", [4], main)
    updater = add_actgrad_tensor("updater", [4], main)
    factor = add_actgrad_tensor("factor", [4], main)
    indices = add_actgrad_tensor("indices", [4], main)
    original_var = add_random_tensor("original_var", _ir.TensorType.Variable,
                                     [4], main)
    out = add_actgrad_tensor("updated_weight", [4], main)

    opid = _ir.OperatorIdentifier("ai.graphcore", "SparseAccumulate", 1,
                                  num_inputs, 1)
    settings = _ir.Settings(main, "SparseAccumulate")

    if connected:
        ins: Dict[int, str] = {
            0: input_.id,
            1: updater.id,
            2: factor.id,
            3: indices.id,
            4: original_var.id
        }
        outs: Dict[int, str] = {0: out.id}
        op = main.createConnectedOp_SparseAccumulateOp(
            ins,
            outs,
            _ir.AccumulationType.Add,
            _ir.OptimizerValue(0.5),
            0,
            settings=settings)
        return
    op = main.createOp_SparseAccumulateOp(_ir.AccumulationType.Add,
                                          _ir.OptimizerValue(0.5),
                                          0,
                                          settings=settings)
    op.connectInTensor(0, input_.id)
    op.connectInTensor(1, updater.id)
    op.connectInTensor(2, factor.id)
    op.connectInTensor(3, indices.id)
    op.connectInTensor(4, original_var.id)
    op.connectOutTensor(0, out.id)
    op.setup()
예제 #13
0
def test_graph_in_outs():
    """Test default behaviour for no inputs or outputs.
    """
    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)
    assert op.hasInput(0) == False
    assert op.hasOutput(0) == False
    assert op.optionalInputs() == set()
    assert op.getInBatchAxis(0) == 0
    assert op.getOutBatchAxis(0) == 0
예제 #14
0
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()
예제 #15
0
def test_op_clone():
    """Op::Clone is pure virtual, this should throw an error. Derived classes should
    be able to call without issue.
    """
    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)
    with pytest.raises(RuntimeError) as e_info:
        op2 = op.clone()
        assert (
            e_info.value.args[0] ==
            "RuntimeError: Tried to call pure virtual function \"Op::clone\"")
예제 #16
0
def test_op_creation():
    """Test simple op creation.
    """
    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)
    assert op.id == 100  # default Id
    assert op.opid == opid
    assert op.opid.domain == "ai.onnx"
    assert op.opid.type == "Identity"
    assert op.opid.version == 1
    assert op.opid.numOutputs == 1
예제 #17
0
def test_remote_store_op(connected: bool, use_offset: bool) -> None:
    """Test that the input and output tensors of remote store op are correct

    Args:
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
        use_offset (bool): Whether or not to specify the optional offset Tensor
    """
    _, graphs = create_ir()
    g = graphs[0]
    t = add_actgrad_tensor("t", [1, 2, 3], g)
    opid = _ir.OperatorIdentifier("ai.onnx", "Init", 1, _ir.NumInputs(0, 0), 1)
    settings = _ir.Settings(g, "new_settings")

    offset = add_actgrad_tensor("offset", [1], g)
    if use_offset:
        if connected:
            op = g.createConnectedOp_RemoteStoreOp({
                0: t.id,
                1: offset.id
            }, {}, opid, settings, 1)
        else:
            op = g.createOp_RemoteStoreOp(opid, settings, 1)
            op.connectInTensor(0, t.id)
            op.connectInTensor(1, offset.id)
    else:
        if connected:
            op = g.createConnectedOp_RemoteStoreOp({
                0: t.id,
            }, {}, opid, settings, 1)
        else:
            op = g.createOp_RemoteStoreOp(opid, settings, 1)
            op.connectInTensor(0, t.id)

    op.setup()

    assert op.hasInput(0)
    assert op.inTensor(0) == t
    assert op.inId(0) == t.id
    if use_offset:
        assert op.hasInput(1)
        assert op.inTensor(1) == offset
        assert op.inId(1) == offset.id
    else:
        assert not op.hasInput(1)
    assert not op.hasOutput(0)
예제 #18
0
def test_matmul_op(serialise_mode: _ir.op.SerialiseSettingsMode,
                   serialise_factor: int,
                   partials_type: _ir.op.MatMulPartialsType, connected: bool):
    """Test the bindings for matmul op, a special case op with extra settings.

    Args:
        serialise_mode (_ir.op.SerialiseSettingsMode): Serialisation mode (see matmul.hpp)
        serialise_factor (int): Factor to serialise by.
        partials_type (_ir.op.MatMulPartialsType): Partials calculation type (FLOAT, HALF)
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
    """
    _, graphs = create_ir()
    g = graphs[0]
    in0 = add_actgrad_tensor("in0", [4, 6], g)
    in1 = add_actgrad_tensor("in1", [6, 12], g)
    out0 = add_actgrad_tensor("out0", [4, 12], g)
    opid = _ir.OperatorIdentifier("ai.onnx", "MatMul", 1, _ir.NumInputs(2, 2),
                                  1)
    serialise_settings = _ir.op.SerialiseSettings()
    serialise_settings.mode = serialise_mode
    serialise_settings.factor = serialise_factor
    optfloat = _ir.OptionalFloat(1.0)
    settings = _ir.Settings(g, "new_settings")
    dtype = _ir.OptionalDataType(_ir.DataType.FLOAT)

    if connected:
        op = g.createConnectedOp_MatMulOp({
            0: in0.id,
            1: in1.id
        }, {0: out0.id}, opid, settings, optfloat, serialise_settings, dtype,
                                          partials_type)
        return

    op = g.createOp_MatMulOp(opid, settings, optfloat, serialise_settings,
                             dtype, partials_type)
    op.connectInTensor(0, in0.id)
    op.connectInTensor(1, in1.id)
    op.connectOutTensor(0, out0.id)
    op.setup()
    assert isinstance(op, _ir.op.MatMulOp)
    assert op.inTensor(0) == in0
    assert op.inTensor(1) == in1
    assert op.outTensor(0) == out0
예제 #19
0
def test_group_norm_op(connected: bool, num_groups: int) -> None:
    """Test the Group Norm Op.

    Args:
        num_groups (int): The number of groups used by the Op
        connected (bool): Whether to use the createConnected<opname> function or
            just create<opname>
    """
    _, graphs = create_ir()
    main = graphs[0]
    num_inputs = _ir.NumInputs(3, 3)
    in0 = add_actgrad_tensor("in0", [8, 4], main)
    weight = add_random_tensor("weight", _ir.TensorType.Variable, [4], main)
    bias = add_random_tensor("bias", _ir.TensorType.Variable, [4], main)
    out = add_actgrad_tensor("out", [8, 4], main)
    mean = add_actgrad_tensor("mean", [8 * num_groups], main)
    invstddev = add_actgrad_tensor("invstddev", [8 * num_groups], main)

    opid = _ir.OperatorIdentifier("ai.graphcore", "GroupNormalization", 1,
                                  num_inputs, 3)
    settings = _ir.Settings(main, "nll")

    if connected:
        ins: Dict[int, str] = {0: in0.id, 1: weight.id, 2: bias.id}
        outs: Dict[int, str] = {0: out.id, 1: mean.id, 2: invstddev.id}
        op = main.createConnectedOp_GroupNormOp(ins,
                                                outs,
                                                opid=opid,
                                                num_groups_=num_groups,
                                                epsilon_=1e-5,
                                                settings=settings)
        return
    op = main.createOp_GroupNormOp(opid=opid,
                                   num_groups_=num_groups,
                                   epsilon_=1e-5,
                                   settings=settings)
    op.connectInTensor(0, in0.id)
    op.connectInTensor(1, weight.id)
    op.connectInTensor(2, bias.id)
    op.connectOutTensor(0, out.id)
    op.connectOutTensor(1, mean.id)
    op.connectOutTensor(2, invstddev.id)
    op.setup()
예제 #20
0
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"
예제 #21
0
def create_dummy_op(op_domain: str, op_type: str, op_version: int,
                    num_inputs: int, num_outputs: int) -> _ir.Op:
    """Create an op with the provided properties.

    Args:
        op_domain (str): Op domain
        op_type (str): Op type name
        op_version (int): Op version
        num_inputs (int): Max = min number of outputs
        num_outputs (int): Number of outputs

    Returns:
        _ir.Op: The op in question.
    """
    ir, graphs = create_ir(["graph_123"])
    graph = graphs[0]
    settings = _ir.Settings(graph, "new_settings")
    num_inputs_obj = _ir.NumInputs(num_inputs, num_inputs)
    opid = _ir.OperatorIdentifier(op_domain, op_type, op_version,
                                  num_inputs_obj, num_outputs)
    return _ir.Op(opid, settings), ir, graph
예제 #22
0
    def _get_op_settings(self, name: str) -> _ir.Settings:
        """Return an internal_ir Settings object using any values specified by a context.
            For example: virtual_graph"""
        pb_g = self.graph._pb_graph
        settings = _ir.Settings(pb_g, "/".join((*self.name_scopes, name)))

        vgid = self.virtual_graph_id
        if vgid is not None:
            settings.vgraphId = _ir.OptionalVGraphId(vgid)

        pstage = self.pipeline_stage
        if pstage is not None:
            settings.pipelineStage = _ir.OptionalPipelineStage(pstage)

        if self.io_tile_set:
            settings.tileSet = _ir.TileSet.IO

        if self._debug_info is not None:
            settings.debugInfoId = self._debug_info.getId()

        return settings
예제 #23
0
def create_new_op(inputs: Dict[int, "_ir.Tensor"],
                  outputs: Dict[int, "_ir.Tensor"],
                  op_name: str,
                  g: "_ir.Graph",
                  inplace: bool = False,
                  connected: bool = False,
                  **kwargs):
    num_inputs = _ir.NumInputs(len(inputs), len(inputs))
    opid = _ir.OperatorIdentifier("ai.onnx", op_name, 1, num_inputs,
                                  len(outputs))
    settings = _ir.Settings(g, "new_settings")
    if connected:
        create_new_op_fn = getattr(g, f"createConnectedOp_{op_name}")
        inp: Dict[int, str] = {}
        outp: Dict[int, str] = {}
        for i, t in inputs.items():
            inp[i] = t.id
        for i, t in outputs.items():
            outp[i] = t.id
        # For some reason inplace ops don't need opid
        if inplace:
            op = create_new_op_fn(inp, outp, settings=settings, **kwargs)
        else:
            op = create_new_op_fn(inp,
                                  outp,
                                  opid=opid,
                                  settings=settings,
                                  **kwargs)
    else:
        create_new_op_fn = getattr(g, f"createOp_{op_name}")
        if inplace:
            op = create_new_op_fn(settings=settings, **kwargs)
        else:
            op = create_new_op_fn(opid=opid, settings=settings, **kwargs)
        for i, t in inputs.items():
            op.connectInTensor(i, t.id)
        for i, t in outputs.items():
            op.connectOutTensor(i, t.id)
        op.setup()
    return op
예제 #24
0
def test_scatter_op(connected: bool) -> None:
    """Test the scatter Op.

    Args:
        connected (bool): Whether to use the createConnected<opname> function or
            just create<opname>
    """
    _, graphs = create_ir()
    main = graphs[0]
    num_inputs = _ir.NumInputs(3, 3)
    in0 = add_actgrad_tensor("in0", [3, 3], main, _ir.DataType.INT32)
    indices = add_random_tensor("indices", _ir.TensorType.Variable, [2, 3],
                                main)
    updates = add_actgrad_tensor("updates", [2, 3], main, _ir.DataType.INT32)
    out0 = add_actgrad_tensor("out0", [3, 3], main)

    opid = _ir.OperatorIdentifier("ai.onnx", "Scatter", 11, num_inputs, 1)
    settings = _ir.Settings(main, "scatter")
    if connected:
        ins: Dict[int, str] = {0: in0.id, 1: indices.id, 2: updates.id}
        outs: Dict[int, str] = {0: out0.id}
        op = main.createConnectedOp_ScatterOp(
            ins,
            outs,
            axis_=0,
            opid=opid,
            available_memory_proportion_=_ir.OptionalFloat(0.4),
            settings=settings)
        op.setup()
        return
    op = main.createOp_ScatterOp(
        axis_=0,
        opid=opid,
        available_memory_proportion_=_ir.OptionalFloat(0.4),
        settings=settings)
    op.connectInTensor(0, in0.id)
    op.connectInTensor(1, indices.id)
    op.connectInTensor(2, updates.id)
    op.connectOutTensor(0, out0.id)
    op.setup()
예제 #25
0
def test_bools():
    """Test default behaviour of bool returns.
    """
    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)
    assert op.isInplaceViewChange() == False
    assert op.isOutplaceViewChange() == False
    assert op.isLossOp() == False
    assert op.isIpuCopyOp() == False
    assert op.isOptimizerOp() == False
    assert op.requiresRandomSeed() == False
    assert op.isOutlineable()
    assert op.hasSideEffect() == False
    assert op.isNorm() == False
    assert op.canBeReplacedByIdentity() == False
    assert op.copiesOptimizerTensors() == False
    assert op.inputsUnmodifiable() == False
    assert op.isElementWiseUnary() == False
예제 #26
0
def test_adam_updater_op(connected: bool) -> None:
    """Test the AdamUpdater Op.

    Args:
        connected (bool): Whether to use the createConnected<opname> function or
            just create<opname>
    """
    _, graphs = create_ir()
    g = graphs[0]
    w = add_random_tensor("w", _ir.TensorType.Variable, [0, 1], g)
    mode = _ir.AdamMode.Adam
    wd = _ir.OptimizerValue(0.02)
    b1 = _ir.OptimizerValue(0.9)
    b2 = _ir.OptimizerValue(0.99)
    eps = _ir.OptimizerValue(0.4)
    m = add_random_tensor("m", _ir.TensorType.Variable, [4, 1], g)
    v = add_random_tensor("v", _ir.TensorType.Variable, [2, 1], g)
    t = add_random_tensor("t", _ir.TensorType.Variable, [1], g)
    out = add_actgrad_tensor("out", [1, 4], g)

    ins = {0: w.id, 1: m.id, 2: v.id, 3: t.id}
    outs: Dict[int, str] = {0: out.id}
    settings = _ir.Settings(g, "new_settings")

    if connected:
        op = g.createConnectedOp_AdamUpdaterOp(ins, outs, mode, wd, b1, b2,
                                               eps, settings)
        op.setup()
        return
    op = g.createOp_AdamUpdaterOp(mode, wd, b1, b2, eps, settings)
    op.connectInTensor(0, w.id)
    op.connectInTensor(1, m.id)
    op.connectInTensor(2, v.id)
    op.connectInTensor(3, t.id)

    op.connectOutTensor(0, out.id)
    op.setup()
예제 #27
0
def test_string_methods(op_name: str, domain: str, op_type: str, op_num: int,
                        op_version: int):
    """Test various string methods (name, id etc)

    Args:
        op_name (str): Name for the op
        domain (str): Domain e.g. ai.onnx
        op_type (str): Op type 
        op_num (int): Op number to test against (default 100)
        op_version (int): Op version
    """
    ir, graphs = create_ir(["A"])
    g = graphs[0]
    settings = _ir.Settings(g, "new_settings")
    num_inputs = _ir.NumInputs(1, 1)

    opid = _ir.OperatorIdentifier(domain, op_type, op_version, num_inputs, 1)
    op = _ir.Op(opid, settings)
    op.setName(op_name)
    assert op.getName() == op_name
    assert op.name() == op_name
    assert op.str() == f"{op_num} ({domain}.{op_type}:{op_version})"
    assert op.debugName(
    ) == f"Op({op_name} ({domain}.{op_type}:{op_version}), inputs=[], outputs=[])"
예제 #28
0
def test_host_store_op(connected: bool) -> None:
    """Test the host store op

    Args:
        connected (bool): Whether to use the createConnected<opname> function or 
            just create<opname>
    """
    _, graphs = create_ir()
    g = graphs[0]
    in0 = add_actgrad_tensor("out0", [1, 2, 3], g)
    opid = _ir.OperatorIdentifier("ai.onnx", "Init", 1, _ir.NumInputs(0, 0), 1)
    settings = _ir.Settings(g, "new_settings")
    if connected:
        op = g.createConnectedOp_HostStoreOp({0: in0.id}, {}, opid, settings,
                                             "streamTensor")
    else:
        op = g.createOp_HostStoreOp(opid, settings, "streamTensor")
        op.connectInTensor(0, in0.id)
    op.setup()

    assert op.inTensor(0) == in0
    assert op.hasInput(0)
    assert not op.hasOutput(0)
    assert op.inId(0) == in0.id
예제 #29
0
def make_sub_graph(ir: _ir.Ir, ins: Dict[int, _ir.TensorInfo]) -> _ir.Graph:
    """
    Makes the following subgraph, with len(ins) inputs. 

    input0  input1  input2  ...  input n
    │       │       │            │
    │       │       │            │
    │       │       │            │
    └─►add ◄┘       │            │
        │           │            │
        └──────►add◄┘            │
                │                │
                │                │
                │                │
                └────►add ...    ▼


                               add
                                │
                                ▼
                             softmax
                                │
                                ▼
                               out

    Args:
        ir (_ir.Ir): The ir to add the subgraph to
        ins (Dict[int, _ir.TensorInfo]): The map of in indices to tensorinfos.

    Returns:
        _ir.Graph: The subgraph in question.
    """
    g = ir.createGraph(_ir.GraphId("fwd"))

    for i, tinfo in ins.items():
        g.addInput(_ir.addScope(g, f"in{i}"), tinfo)

    inputs = g.getInputIds()

    t = g.getTensor(inputs[0])
    for i in range(1, len(ins)):
        settings = _ir.Settings(g, f"add{i}")
        opid = _ir.OperatorIdentifier("ai.onnx", f"Add{i}", 1,
                                      _ir.NumInputs(2, 2), 1)
        add = g.createConnectedOp_AddOp({
            0: t.id,
            1: inputs[i]
        }, {0: _ir.addScope(g, f"add{i}")}, opid, settings)
        t = add.outTensor(0)

    settings = _ir.Settings(g, "softmax0")
    opid = _ir.OperatorIdentifier("ai.onnx", "SoftMax", 1, _ir.NumInputs(1, 1),
                                  1)
    sm = g.createConnectedOp_SoftmaxOp({0: t.id}, {0: _ir.addScope(g, "sm0")},
                                       opid=opid,
                                       axis_=0,
                                       settings=settings)

    g.markAsOutput(sm.outTensor(0).id)

    return g
예제 #30
0
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_