using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using static System.Math;
using static System.Console;
class Simple
{
    int N, M;
    int[] L, R, D;

    List<Edge>[] adj;
    bool[] isVisited;
    long[] dis2next;

    void Solve() {
        //input
        N = io.Int;
        M = io.Int;
        L = new int[M];
        R = new int[M];
        D = new int[M];
        for(int i = 0; i < M; ++i) {
            L[i] = io.Int;
            R[i] = io.Int;
            D[i] = io.Int;
        }
        //cal
        adj = new List<Edge>[N + 1];
        for(int i = 0; i < adj.Count(); ++i) {
            adj[i] = new List<Edge>();
        }
        for(int i = 0; i < M; ++i) {
            adj[L[i]].Add(new Edge(R[i],  D[i]));
            adj[R[i]].Add(new Edge(L[i], -D[i]));
        }
        dis2next  = new long[N + 1];
        isVisited = new bool[N + 1];

        for(int i = 1; i <= N; ++i) {
            if (!isVisited[i]) {
                isVisited[i] = true;
                dfs(i, 0);
            }
        }

        //ret
        WriteLine("Yes");
    }

    void dfs(int cur,int pre) {
        foreach(var e in adj[cur]) {
            var next = e.To;
            var cost = e.Cost;
            if (isVisited[next]) {
                var pred = dis2next[next] - cost;
                if(dis2next[cur]!= pred) {
                    WriteLine("No");
                    io.writeFlush();
                    Environment.Exit(0);
                }
            } else {
                isVisited[next] = true;
                dis2next[next] = dis2next[cur] + cost;
                dfs(next, cur);
            }
        }
    }

    SimpleIO io = new SimpleIO();
    public static void Main(string[] args) => new Simple().Stream(); 
    void Stream() { Solve(); io.writeFlush(); }
}
public struct Edge : IComparable<Edge>
{
    public int To;
    public long Cost;
    public Edge(int to, long cost) {
        this.To = to;
        this.Cost = cost;
    }
    public int CompareTo(Edge e) => Cost.CompareTo(e.Cost);
}
class ShortestPath
{
    protected int I; //辺は1~N
    protected int V; //頂点数
    protected int E; //辺の数
    protected bool isNonDir = true; //有向?
    public long INF = long.MaxValue / 2; //初期化      
    public long[] Cost; //重み  
    public List<Edge>[] Adj; //隣接リスト
    public void Init(int n) {
        I = n + 1;
        V = n;
        Cost = new long[I];
        Adj = new List<Edge>[I];
        for (int i = 0; i < I; ++i) Adj[i] = new List<Edge>();
    }
    public void AddPath(int f, int t, long c = 1) {
        E++;
        Adj[f].Add(new Edge(t, c));
        if (isNonDir) Adj[t].Add(new Edge(f, c));
    }
    public void AddPath(int[] from, int[] to, long[] cost) {
        for (int i = 0; i < from.Length; ++i) AddPath(from[i], to[i], cost[i]);
    }
    public void AddPath(int[] from, int[] to) {
        for (int i = 0; i < from.Length; ++i) AddPath(from[i], to[i]);
    }
    public bool PathExist(int t) => Cost[t] != INF;
}
class SimpleIO
{
    string[] nextBuffer;
    int BufferCnt;
    char[] cs = new char[] { ' ' };
    StreamWriter sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
    public SimpleIO() {
        nextBuffer = new string[0];
        BufferCnt = 0;
        Console.SetOut(sw);
    }
    public string Next() {
        if (BufferCnt < nextBuffer.Length)
            return nextBuffer[BufferCnt++];
        string st = Console.ReadLine();
        while (st == "")
            st = Console.ReadLine();
        nextBuffer = st.Split(cs, StringSplitOptions.RemoveEmptyEntries);
        BufferCnt = 0;
        return nextBuffer[BufferCnt++];
    }
    public string String => Next();
    public char Char => char.Parse(String);
    public int Int => int.Parse(String);
    public long Long => long.Parse(String);
    public double Double => double.Parse(String);
    public void writeFlush() { Console.Out.Flush(); }
}


Measured memory_bytes: 49932