Buggy python submission (verdict: Time Limit Exceeded):
# coding: utf-8
import sys,pprint
from fractions import Fraction
from collections import defaultdict

MOD = 1000000007
N = int(input())
params =[]
hatemasks = []
tilts = defaultdict(int)
zerozero = 0
zerodiv = 0
for i in range(N):
    (ai, bi) = map(int,input().split())
    if ai == 0 and bi == 0:
        zerozero += 1
    elif bi == 0:
        zerodiv += 1
    else:
        t = Fraction(ai,bi)
        tilts[t] += 1

# print(tilts,file=sys.stderr)

ans = 1
f1 = Fraction(0,1)
if f1 in tilts:
    ans *= 1+pow(2,tilts[f1],MOD)-1 + pow(2,zerodiv,MOD)-1
    del tilts[f1]
else:
    ans *= 1 + pow(2,zerodiv,MOD)-1
ans %= MOD

klist = list(tilts.keys())
for k in klist:
    kinv = Fraction(-1*k.denominator,k.numerator)
    if kinv in tilts:
        #print(k,kinv, file=sys.stderr)
        ans *= 1+pow(2,tilts[k],MOD) -1+pow(2,tilts[kinv],MOD)-1
        ans %= MOD
        del tilts[k]
        del tilts[kinv]
    else:
        ans *= 1+pow(2,tilts[k],MOD)-1
        ans %= MOD

print((ans + zerozero -1) % MOD)


Fixed submission:
# coding: utf-8
import sys,pprint, math
from fractions import Fraction
from collections import defaultdict

MOD = 1000000007
N = int(input())
params =[]
hatemasks = []
tilts = defaultdict(lambda: [0,0])
zerozero = 0
zerodiv = 0
for i in range(N):
    (ai, bi) = map(int,input().split())
    if ai == 0 and bi == 0:
        zerozero += 1
        continue
    if (ai < 0) or (ai == 0 and bi < 0):
        ai *= -1
        bi *= -1
    g = math.gcd(ai,bi)
    ai = ai // g
    bi = bi // g
    if bi > 0:
        tilts[(ai,bi)][0] += 1
    else:
        tilts[(-bi,ai)][1] += 1
print(tilts,file=sys.stderr)

#sys.exit()
ans = 1
for(v1,v2) in tilts.values():
    ans *= 1 +pow(2,v1,MOD)-1 +pow(2,v2,MOD)-1
    ans %= MOD
ans = (ans+zerozero-1) % MOD
print(ans)

Buggy python submission (verdict: Time Limit Exceeded):
# coding: utf-8
import sys,pprint
from fractions import Fraction
from collections import defaultdict

N = int(input())
params =[]
hatemasks = []
tilts = defaultdict(int)
zerozero = 0
zerodiv = 0
for i in range(N):
    (ai, bi) = map(int,input().split())
    if ai == 0 and bi == 0:
        zerozero += 1
    elif bi == 0:
        zerodiv += 1
    else:
        t = Fraction(ai,bi)
        tilts[t] += 1

print(tilts,file=sys.stderr)

ans = 1
f1 = Fraction(0,1)
if f1 in tilts:
    ans *= 1+pow(2,tilts[f1])-1 + pow(2,zerodiv)-1
    del tilts[f1]
else:
    ans *= 1 + pow(2,zerodiv)-1

klist = list(tilts.keys())
for k in klist:
    kinv = Fraction(-1*k.denominator,k.numerator)
    if kinv in tilts:
        #print(k,kinv, file=sys.stderr)
        ans *= 1+pow(2,tilts[k]) -1+pow(2,tilts[kinv])-1
        del tilts[k]
        del tilts[kinv]
    else:
        ans *= 1+pow(2,tilts[k])-1

print((ans + zerozero -1)%1000000007)


Fixed submission:
# coding: utf-8
import sys,pprint, math
from fractions import Fraction
from collections import defaultdict

MOD = 1000000007
N = int(input())
params =[]
hatemasks = []
tilts = defaultdict(lambda: [0,0])
zerozero = 0
zerodiv = 0
for i in range(N):
    (ai, bi) = map(int,input().split())
    if ai == 0 and bi == 0:
        zerozero += 1
        continue
    if (ai < 0) or (ai == 0 and bi < 0):
        ai *= -1
        bi *= -1
    g = math.gcd(ai,bi)
    ai = ai // g
    bi = bi // g
    if bi > 0:
        tilts[(ai,bi)][0] += 1
    else:
        tilts[(-bi,ai)][1] += 1
print(tilts,file=sys.stderr)

#sys.exit()
ans = 1
for(v1,v2) in tilts.values():
    ans *= 1 +pow(2,v1,MOD)-1 +pow(2,v2,MOD)-1
    ans %= MOD
ans = (ans+zerozero-1) % MOD
print(ans)