import algorithm, future, hashes, macros, math, sequtils, sets, strutils, tables, unicode

template readString: string = stdin.readLine
template readStrings: seq[string] = stdin.readLine.split
template readInt: int = stdin.readLine.parseInt
template readInts: seq[int] = readLine(stdin).split(" ").map(parseInt)
template readFloat: float = stdin.readLine.parseFloat
template readFloats: seq[float] = stdin.readLine.split.map(parseFloat)
template times(n: int, body: untyped): untyped = (for _ in 0..<n: body)
proc newSeq2[T](n1, n2: Natural): seq[seq[T]] = newSeqWith(n1, newSeq[T](n2))
proc newSeq3[T](n1, n2, n3: Natural): seq[seq[seq[T]]] = newSeqWith(n1, newSeqWith(n2, newSeq[T](n3)))

macro unpack*(rhs: seq, cnt: static[int]): auto =
  let t = genSym(); result = quote do:(let `t` = `rhs`;())
  for i in 0..<cnt: result[0][1].add(quote do:`t`[`i`])

# -------------------------------------------------- #

type
  Deque[T] = object
    data: seq[T]
    mask, head, tail, count: int

proc initDeque[T](initialSize: int = 4): Deque[T] =
  result.data = newSeq[T](initialSize.nextPowerOfTwo)
  result.mask = initialSize.nextPowerOfTwo - 1
  result.head = 0
  result.tail = 0
  result.count = 0
  
proc len[T](deq: Deque[T]): int =
  result = deq.count

template emptyCheck(deq) =
  when compileOption("boundChecks"):
    if unlikely(deq.count < 1):
      raise newException(IndexError, "Empty deque.")

proc expandIfNeeded[T](deq: var Deque[T]) =
  var cap = deq.mask + 1
  if unlikely(deq.count >= cap):
    var tmpData = newSeq[T](cap * 2)
    for i in 0..<deq.count:
      shallowCopy(tmpData[i], deq.data[(deq.head + 1) and deq.mask])
    shallowCopy(deq.data, tmpData)
    deq.mask = cap * 2 - 1
    deq.head = 0
    deq.tail = deq.count

proc addFirst[T](deq: var Deque[T], item: T) =
  expandIfNeeded(deq)
  inc deq.count
  deq.head = (deq.head - 1) and deq.mask
  deq.data[deq.head] = item

proc addLast[T](deq: var Deque[T], item: T) =
  expandIfNeeded(deq)
  inc deq.count
  deq.data[deq.tail] = item
  deq.tail = (deq.tail + 1) and deq.mask

proc peekFirst[T](deq: Deque[T]): T =
  emptyCheck(deq)
  result = deq.data[deq.head]

proc peekLast[T](deq: Deque[T]): T =
  emptyCheck(deq)
  result = deq.data[(deq.tail - 1) and deq.mask]

proc popFirst[T](deq: var Deque[T]): T {.discardable.} =
  emptyCheck(deq)
  dec deq.count
  result = deq.data[deq.head]
  deq.head = (deq.head + 1) and deq.mask

proc popLast[T](deq: var Deque[T]): T {.discardable.} =
  emptyCheck(deq)
  dec deq.count
  deq.tail = (deq.tail - 1) and deq.mask
  result = deq.data[deq.tail]

proc clear[T](deq: var Deque[T]) =
  deq.count = 0
  deq.tail = deq.head

# -------------------------------------------------- #

type Edge = tuple[t: int, c: int]

var (N, M) = readInts.unpack(2)
var edge = newseq2[Edge](N + 1, 0)
var indegree = newseq[int](N + 1)
var indegree0 = newseq[int]()
for i in 1..M:
  var (L, R, D) = readInts.unpack(3)
  edge[L].add((R, D))
  indegree[R].inc
for i in 1..N:
  if indegree[i] == 0:
    indegree0.add(i)

var dag = @[0]

proc topologicalSort =
  var deq = initDeque[int](N + 1)
  for i in 1..N:
    if indegree[i] == 0:
      deq.addLast(i)
  while deq.len > 0:
    var v = deq.popLast()
    dag.add(v)
    for eg in edge[v]:
      var u = eg.t
      indegree[u].dec
      if indegree[u] == 0:
        deq.addLast(u)

proc solve =
  topologicalSort()
  if dag.len != N + 1:
    echo "No"
    quit()
  for i in indegree0:
    var d = newseq[int](N + 1)
    var v = dag[i]
    for eg in edge[v]:
      var u = eg.t
      var cost = eg.c
      if d[v] == 0:
        d[u] = cost
      else:
        if d[u] > 0 and d[u] != d[v] + cost:
          echo "No"
          quit()
  echo "Yes"

solve()