#include <iostream>
#include <map>
#include <set>
#include <algorithm>
#include <vector>
#include <sstream>
#include <string>
#include <functional>
#include <queue>
#include <deque>
#include <stack>
#include <limits>
#include <unordered_map>
#include <unordered_set>
#include <math.h>
#include <fstream>
#include <iterator>
#include <random>
#include <chrono>
 
 
#define forr(i,start,count) for (int i = (start); i < (start)+(count); ++i)
#define set_map_includes(set, elt) (set.find((elt)) != set.end())
#define readint(i) int i; cin >> i
#define readll(i) ll i; cin >> i
#define readdouble(i) double i; cin >> i
#define readstring(s) string s; cin >> s
 
typedef long long ll;
 
using namespace std;

ll modd = 1000*1000*1000+7;

template<class T>
class depth_first_search_iterator {
  public:
    stack<pair<T,T>> todo;
    function<bool(T)> notvisited;
    function<void(T)> mark_visited;
    function<set<T>(T)> neighbors;

    depth_first_search_iterator() {}

    depth_first_search_iterator(T start, function<bool(T)> notv, function<void(T)> mark_v, function<set<T>(T)> neigh) :
      notvisited(notv), mark_visited(mark_v), neighbors(neigh), end_(false), current(start), prev(start) {
          todo.push(make_pair(start, start)); operator++();
    }

    void operator++() {
        end_ = true;
        while (!todo.empty()) {
            current = todo.top().first; prev = todo.top().second; todo.pop();
            if (notvisited(current)) {
                mark_visited(current);
                for (const T x : neighbors(current)) {  todo.push(make_pair(x, current));   }
                end_ = false;
                break;
            }
        }
    }

    T operator*() {     return current;    }

    T previous() {  return prev;  }

    bool end() {  return end_;  }

  private:
    T current; // contains current node
    T prev;    // contains previous node
    bool end_;
};

template<class T>
class breadth_first_search_iterator {
  public:
    queue<pair<T,T>> todo;
    function<bool(T)> notvisited;
    function<void(T)> mark_visited;
    function<set<T>(T)> neighbors;

    breadth_first_search_iterator() {}

    breadth_first_search_iterator(T start, function<bool(T)> notv, function<void(T)> mark_v, function<set<T>(T)> neigh) :
      notvisited(notv), mark_visited(mark_v), neighbors(neigh), end_(false), current(start), prev(start) {
          todo.push(make_pair(start, start)); operator++();
    }

    void operator++() {
        end_ = true;
        while (!todo.empty()) {
            current = todo.front().first; prev = todo.front().second; todo.pop();
            if (notvisited(current)) {
                mark_visited(current);
                for (const T x : neighbors(current)) {  todo.push(make_pair(x, current));   }
                end_ = false;
                break;
            }
        }
    }

    T operator*() {     return current;    }

    T previous() {  return prev;  }

    bool end() {  return end_;  }

  private:
    T current;   // current node
    T prev;      // previous node
    bool end_;
};



template<class T>
class DirectedGraph {
    // allows only single connection between two vertices
    public:
      vector<T> vertices;
      vector<set<int>> neighbors;
      depth_first_search_iterator<int> dfs_iterator;
      breadth_first_search_iterator<int> bfs_iterator;
      vector<bool> visited;

      DirectedGraph() {}

      DirectedGraph(int n) {  for (int i = 0; i < n; ++i) {  AddVertex(0);  }   }

      bool EdgeExists(int i_from, int j_to) {
          return (neighbors[i_from].find(j_to) != neighbors[i_from].end());
      }

      void AddVertex(T val) {
          vertices.push_back(val);
          neighbors.push_back(set<int>());      }

      void AddEdge(int i_from, int j_to) {
          if (!EdgeExists(i_from, j_to)) {     neighbors[i_from].insert(j_to);      }
      }

      T& operator[](int i) {      return vertices[i];      }

      void RemoveEdge(int i_from, int j_to) {
          if (EdgeExists(i_from, j_to)) {
          neighbors[i_from].erase(find(neighbors[i_from].begin(), neighbors[i_from].end(), j_to));      } }

      DirectedGraph<T> Transpose() {
          DirectedGraph<T> ret;
          for (auto x : vertices) {  ret.AddVertex(x);   }
          for (int i_from = 0; i_from < vertices.size(); ++i_from) {
              for (int j_to : neighbors[i_from]) {
                  ret.AddEdge(j_to, i_from);
              }
          }
          return ret;      }

      int vertices_count() {   return vertices.size();  }

      void dfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          dfs_iterator = depth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void bfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          bfs_iterator = breadth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void print_() {
          forr(i, 0, vertices.size()) {
              for (auto x : neighbors[i]) {
                  cout << i << "->" << x << endl;
              }
          }
      }
};

struct Trie2Data {
    char c;                // char at this node
    int no_of_strings;     // no_of_strings with this prefix (ie not necessarily identical to the prefix)
    int prev_node;         // index of previous node, -1 if root
    int pref_length;       // prefix_length at this node
    unordered_map<char, int> children;   // gives index of child node for each character
};

class Trie2 : public DirectedGraph<Trie2Data> {
    public:
      Trie2() : DirectedGraph() {       AddVertex({' ', 0, -1, 0, {}});    }

      string node_prefix(int index) {   // returns prefix of this node
        string ret = "";
        while (index > 0) {
           ret = vertices[index].c + ret;
           index = vertices[index].prev_node;
        }
        return ret;
      }

      int best_fit(const string& s) {  // gives size of largest prefix of s which exists in trie
          return vertices[best_fit_index(s)].pref_length;
      }

      int best_fit_index(const string& s) {  // gives index of trie node with largest prefix of s
          int curr_index = 0, count = 0;
          while (vertices[curr_index].children[s[count]] > 0) {
              curr_index = vertices[curr_index].children[s[count]];
              ++count; if (count == s.size()) { break; }
          }
          return curr_index;
      }

      int no_of_words(int index) {  // no of words at node index
        int ret = vertices[index].no_of_strings;
        for (auto x : vertices[index].children) {
            ret -= vertices[x.second].no_of_strings;
        }
        return ret;
      }

      void AddWord(const string& s) {
          if (s.size() == 0) {       ++vertices[0].no_of_strings; return;        }
          int curr_index = 0, count = 0;
          while (vertices[curr_index].children[s[count]] > 0) {
              ++vertices[curr_index].no_of_strings;
              curr_index = vertices[curr_index].children[s[count]];
              ++count; if (count == s.size()) { break; }
          }
          ++vertices[curr_index].no_of_strings;
          while (count < s.size())  {
              AddVertex({s[count], 1, curr_index, count+1, {}});
              AddEdge(curr_index, vertices.size()-1  );
              vertices[curr_index].children[s[count]] = vertices.size()-1;
              curr_index = vertices.size()-1;
              ++count;
          }
      }

      void print_() {
          bfs_init(0);
          int curr = 0;
          while (!bfs_iterator.end()) {
              if (vertices[*bfs_iterator].pref_length > curr) {  ++curr; cout << endl << curr << " : ";  }
              cout << node_prefix(*bfs_iterator) << ", ";
              ++bfs_iterator;
          }
          cout << endl;
      }
};

template<class T>
class Graph {
    // allows only single connection between two vertices
    public:
      vector<T> vertices;
      vector<set<int>> neighbors;
      depth_first_search_iterator<int> dfs_iterator;
      breadth_first_search_iterator<int> bfs_iterator;
      vector<bool> visited;

      Graph() {}

//      Graph(int n) {  for (int i = 0; i < n; ++i) {  AddVertex(0);  }   }

      void AddVertex(T val) {
          vertices.push_back(val);
          neighbors.push_back(set<int>());      }

      bool EdgeExists(int i, int j) {
          return (neighbors[i].find(j) != neighbors[i].end());
     }

      void AddEdge(int i, int j) {
          if (!EdgeExists(i,j)) {
            neighbors[i].insert(j);
            if (i != j) {         neighbors[j].insert(i);         } 
          }
      }

      T& operator[](int i) {      return vertices[i];      }

      void RemoveEdge(int i, int j) {
          if (EdgeExists(i,j)) {
            neighbors[i].erase(find(neighbors[i].begin(), neighbors[i].end(), j));
            neighbors[j].erase(find(neighbors[j].begin(), neighbors[j].end(), i));
          }
      }

      void dfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          dfs_iterator = depth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void bfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          bfs_iterator = breadth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void print_() {
          forr(i, 0, vertices.size()) {
              for (auto x : neighbors[i]) {
                  if (i <= x) {  cout << i << "-" << x << endl;  }                  
              }
          }
      }
};

void solve(Graph<pair<ll,ll>>& g, int root, int parent) {
  for(auto x : g.neighbors[root]) {
      if (x!=parent) {
          solve(g, x, root);
      }
  }
  ll all_col = 1, root_white = 1;
  for(auto x : g.neighbors[root]) {
      if (x!=parent) {
          root_white *= g[x].first; root_white = root_white % modd;
          all_col *= (g[x].first+g[x].second); all_col = all_col % modd;
      }
  }
  g[root] = make_pair(all_col, root_white);
}


int main()   {

    ios_base::sync_with_stdio(false);

    cout.precision(17);
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<int> rand_gen(0, modd);   // rand_gen(rng) gets the rand no
 
//    auto start = chrono::steady_clock::now();
  
//    readint(test_cases);
    int test_cases = 1;
    forr(t, 1, test_cases) {
        readint(n);
        Graph<pair<ll,ll>> g;
        forr(i,0,n) {g.AddVertex(make_pair(0,0));}
        forr(i,0,n-1) {
            readint(aa); readint(bb);
            g.AddEdge(aa-1,bb-1);
        }

        solve(g, 0, -1);
        cout << (g[0].first+g[0].second) % modd << endl;

    }
 
//    auto end = chrono::steady_clock::now();
//    cout << chrono::duration_cast<chrono::milliseconds>(end - start).count() << endl;
 
    return 0;
}

Measured memory_bytes: 25892