import std.algorithm;
import std.array;
import std.conv;
import std.math;
import std.range;
import std.stdio;
import std.string;
import std.typecons;

int readint() { return readln.chomp.to!int; }
int[] readints() { return readln.split.to!(int[]); }

alias Info = Tuple!(int, "l", int, "r", int, "d");

bool calc(int n, Info[] infos) {
    auto uf = new UnionFind(n);
    foreach (info; infos) {
        uf.unite(info.l, info.r);
    }

    Info[][int] db;
    foreach (info; infos) {
        db[info.l] ~= info;
        db[info.r] ~= info;
    }

    bool[int] used;
    for (int i = 1; i <= n; i++) {
        if (i in used) continue;
        // used[i] = true;

        int[int] map;
        auto q = [i];
        while (q.length > 0) {
            int node = q[0];
            q = q[1..$];

            if (node in used) continue;
            used[node] = true;

            if (node in db) {
                foreach (info; db[node]) {
                    int l = info.l;
                    int r = info.r;
                    if (l !in used) q ~= l;
                    if (r !in used) q ~= r;

                    if (l in map && r in map) {
                        int x = map[l];
                        int y = map[r];
                        if (y - x != info.d) return false;
                    }
                    else if (l in map) {
                        int x = map[l];
                        map[r] = x + info.d;
                    }
                    else if (r in map) {
                        int y = map[r];
                        map[l] = y - info.d;
                    }
                    else {
                        map[l] = 0;
                        map[r] = info.d;
                    }
                }
            }
        }
    }

    return true;
}

void main() {
    auto nm = readints;
    int n = nm[0], m = nm[1];

    Info[] infos;
    for (int i = 0; i < m; i++) {
        auto lrd = readints;
        infos ~= Info(lrd[0], lrd[1], lrd[2]);
    }

    writeln(calc(n, infos) ? "Yes" : "No");
}

class UnionFind {
    private int[] _data;
    private int[] _nexts; // 次の要素。なければ -1
    private int[] _tails; // 末尾の要素

    this(int n) {
        _data  = new int[](n + 1);
        _nexts = new int[](n + 1);
        _tails = new int[](n + 1);

        _data[] = -1;
        _nexts[] = -1;
        for (int i = 0; i < _tails.length; i++)
            _tails[i] = i;
    }

    int root(int a) {
        if (_data[a] < 0) return a;
        return _data[a] = root(_data[a]);
    }

    bool unite(int a, int b) {
        int ra = root(a);
        int rb = root(b);
        if (ra == rb) return false;
        // ra に rb を繋げる
        _data[ra] += _data[rb];
        _data[rb] = ra;

        // ra の末尾の後ろに rb を繋げる
        _nexts[_tails[ra]] = rb;
        // ra の末尾が rb の末尾になる
        _tails[ra] = _tails[rb];
        return true;
    }

    bool isSame(int a, int b) {
        return root(a) == root(b);
    }

    /// a が属する集合のサイズ
    int groupSize(int a) {
        return -_data[root(a)];
    }

    /// a が属する集合の要素全て
    void doEach(int a, void delegate(int) fn) {
        auto node = root(a);
        while (node != -1) {
            fn(node);
            node = _nexts[node];
        }
    }
}