<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[\u001B :wq \u000D]]></title><description><![CDATA[\u001B :wq \u000D]]></description><link>http://iamkush.me/</link><generator>Ghost 0.7</generator><lastBuildDate>Fri, 13 Feb 2026 14:15:09 GMT</lastBuildDate><atom:link href="http://iamkush.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Identifying Airtel middleboxes that censor HTTPS traffic]]></title><description><![CDATA[Airtel has started using SNI inspection to censoring HTTPS traffic. We use iterative network tracing to prove this, and identify some middleboxes doing so.]]></description><link>http://iamkush.me/sni-airtel/</link><guid isPermaLink="false">4ed2b6c6-0715-4ad6-a7a2-d9831b189698</guid><category><![CDATA[internet censorship]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Thu, 11 Jun 2020 16:40:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2020/06/airtel_103-224-212-222_fullhd720-com.png" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2020/06/airtel_103-224-212-222_fullhd720-com.png" alt="Identifying Airtel middleboxes that censor HTTPS traffic"><p>Back in November 2019, <a target="blank" href="https://cis-india.org/internet-governance/blog/reliance-jio-is-using-sni-inspection-to-block-websites">we reported</a> that Reliance Jio is able to block HTTPS internet traffic by means of a deep packet inspection (DPI) technique. In response, some readers messaged us saying that they ran our test and were able to reproduce similar behaviour on Airtel mobile networks. According to <a href="https://trai.gov.in/sites/default/files/PIR_08012020_0.pdf" target="_blank">TRAI's Performance Indicators report for Jul-Sep 2019</a>, Reliance Jio and Airtel serve roughly 52% and 23% of internet subscribers in India respectively. <strong>This essentially means that SNI inspection based censorship is now impacting every 3 out of 4 internet connections in India.</strong></p>

<p>Although the previous test was able to detect the presence of SNI inspection based censorship, it was not very insightful. In this post, we delve into a more informative test which not only confirms the presence of SNI inspection based censorship, but also helps us identify the exact mechanism. Furthermore, it also allows us to identify middleboxes which are actively inspecting SNI in TLS handshakes and censoring requests. <strong>Using this method, we were able to discover 25 different middleboxes registered to Airtel, which are actively censoring HTTPS traffic.</strong></p>

<p>Quick links to different sections of this post: <br>
1. <a href="http://iamkush.me/sni-airtel/#secTLS">Transport Layer Security</a> <br>
1.1 <a href="http://iamkush.me/sni-airtel/#secSNI">Server Name Indication</a> <br>
2. <a href="http://iamkush.me/sni-airtel/#secSNICensor">SNI Inspection based censorship</a> <br>
3. <a href="http://iamkush.me/sni-airtel/#secINT">Iterative Network Tracing</a> <br>
4. <a href="http://iamkush.me/sni-airtel/#secData">Data Preparation</a> <br>
5. <a href="http://iamkush.me/sni-airtel/#secMethod">Methodology</a> <br>
6. <a href="http://iamkush.me/sni-airtel/#secAirtel">Examining Airtel's behaviour</a> <br>
7. <a href="http://iamkush.me/sni-airtel/#secRef">References</a></p>

<p>All the code for replicating this experiment, as well as the logs from our test runs can be found in <a target="_blank" href="https://github.com/kush789/INT-SNI">this repository</a>. Big shout-out to <a href="https://ipinfo.io/" target="_blank">IPinfo</a> for giving us access to their IP address dataset, and <a href="https://gurshabad.github.io/" target="_blank">Gurshabad Grover</a> for his suggestions while ideating the methodology and for editing this post.</p>

<p><a name="secTLS"></a>  </p>

<h1 id="transportlayersecurity">Transport Layer Security</h1>

<p>Transport Layer Security (TLS) is a cryptographic protocol for providing communication confidentiality and authenticity, commonly used for encrypting web traffic (as done in HTTPS). Normally TLS is used over TCP, as it requires a reliable in-order data stream. A <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls" target="_blank">quick refresher</a> on TLS by Cloudflare.</p>

<p><img src="https://i.imgur.com/OlMYujg.png" alt="Identifying Airtel middleboxes that censor HTTPS traffic">
<center style="font-size:16px; margin-top: -30px; line-height: 1.5">A TCP handshake followed by a TLS Handshake. The ClientHello is a message sent by the client, which initiates the TLS handshake. This message can contain extensions such as SNI. Image credits - <a href="https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/">Cloudflare</a></center></p>

<p><a name="secSNI"></a>  </p>

<h3 id="servernameindication">Server Name Indication</h3>

<p>Server Name Indication (SNI), defined first in <a href="https://tools.ietf.org/html/rfc4366" target="_blank">RFC4366</a> and then in <a href="https://tools.ietf.org/html/rfc6066" target="_blank">RFC6066</a>, is a TLS extension designed to facilitate the hosting of multiple HTTPS websites on the same IP address. While sending a <code>ClientHello</code> message (which initiates the establishment of a secure connection), the client is expected to fill in the SNI attribute with the hostname of the website it wishes to connect to. SNI, unfortunately, travels on the network in cleartext, i.e. <strong>network operators can not only see the websites you’re visiting, but also filter traffic based on this information.</strong></p>

<hr>

<p><a name="secSNICensor"></a>  </p>

<h1 id="sniinspectionbasedcensorship">SNI Inspection based censorship</h1>

<p>Since the SNI present is in cleartext, anyone in the network can inspect and filter traffic based on its value. As seen in other countries, ISPs can leverage this to deny access to certain websites. We can observe the same by attempting a TLS connection using openssl and monitoring packets to the host.  </p>

<pre><code>openssl s_client -state -connect 103.224.212.222:443 -servername fullhd720.com  
</code></pre>

<p><img src="https://raw.githubusercontent.com/kush789/INT-SNI/master/images/airtel_103.224.212.222_fullhd720.com.png" alt="Identifying Airtel middleboxes that censor HTTPS traffic">
<center style="font-size:16px; margin-top: -30px; line-height: 1.5">An attempted TLS connection to <code>103.224.212.222</code>, with SNI <code>fullhd720.com</code>. We observe a RST packet immediately after the ClientHello message containing the SNI is sent.</center></p>

<p>For instance, using Airtel, we can see that the client receives a TCP RST packet when it tries to connect to a blocked website "fullhd720.com". The RST packet seems to be originating from the actual host, and is received right after the ClientHello message containing the SNI is sent. <a href="https://github.com/kush789/INT-SNI/blob/master/pcaps/airtel_103.224.212.222_fullhd720.com.pcap" target="_blank">PCAP</a>.</p>

<p>To confirm that the connection termination was indeed due to the SNI, we can reattempt the connection with a different SNI which we don't expect to be blocked (in this case we use <code>facebook.com</code>).</p>

<pre><code>openssl s_client -state -connect 103.224.212.222:443 -servername facebook.com  
</code></pre>

<p><img src="https://raw.githubusercontent.com/kush789/INT-SNI/master/images/airtel_103.224.212.222_facebook.com.png" alt="Identifying Airtel middleboxes that censor HTTPS traffic">
<center style="font-size:16px; margin-top: -30px; line-height: 1.5">An attempted TLS connection to <code>103.224.212.222</code> with a different SNI, <code>facebook.com</code>. In this case, we observe a successful TLS handshake</center></p>

<p>This time we notice a successful connection, indicating that the RST in the previous attempt was indeed due to the specified SNI. <a href="https://github.com/kush789/INT-SNI/blob/master/pcaps/airtel_103.224.212.222_facebook.com.pcap" target="_blank">PCAP</a>.</p>

<p>Although this test does demonstrate the presence of SNI inspection based censorship, the packet dumps are not sufficient to prove that the RST packet was actually forged by a middlebox belonging to the ISP.</p>

<hr>  

<p><a name="secINT"></a>  </p>

<h1 id="iterativenetworktracing">Iterative Network Tracing</h1>

<p>For a given host, let's call the minimum Time to Live (<a href="https://packetpushers.net/ip-time-to-live-and-hop-limit-basics/" target="_blank">TTL</a>) required for a packet to reach from the client to the host, <code>min_ttl</code>. Any packet where the TTL set is less than <code>min_ttl</code> would expire in transit, and never reach the host. Ideally, the router at which the TTL of the packet expired should respond with an ICMP Time Exceeded (<a href="http://www.networksorcery.com/enp/protocol/icmp/msg11.htm" target="_blank">ICMP message type 11</a>) message. However, this is not guaranteed, and some routers are even configured to not send them (in order to hide the topology of the network).</p>

<p><img src="https://i.imgur.com/aSrR375.png" alt="Identifying Airtel middleboxes that censor HTTPS traffic">
<center style="font-size:16px; margin-top: -30px; line-height: 1.5">Iterative Network Tracing; we send ClientHello messages with increasing TTL. In this particular case, the minimum TTL required is 9. A middlebox which censors requests would send back a censored response even when the TTL is less than 9. Image credits - <a href="http://iamkush.me/sni-airtel/#cite1">Yadav et al.</a></center></p>

<p>So if the RST received is forged by a middlebox, we should receive it even when we send the ClientHello message with TTL less than <code>min_ttl</code>. This approach, known as Iterative Network Tracing (INT), has been previously used to ascertain the presence of middleboxes which censor DNS and HTTP traffic in India [<a href="http://iamkush.me/sni-airtel/#cite1">Yadav et al.</a>] and China <a href="http://iamkush.me/sni-airtel/#cite2">Xu et al.</a> Similar to these studies, we use INT to detect censorship of TLS traffic (explained further in the <a href="http://iamkush.me/sni-airtel/#secMethod">methodology</a> section).</p>

<hr>  

<p><a name="secData"></a>  </p>

<h1 id="datapreparation">Data preparation</h1>

<p>We run our tests using a list of potentially blocked websites (PBWs), curated from leaked court and government orders. The list and more information pertaining to it can be found <a href="https://github.com/kush789/How-India-Censors-The-Web-Data" target="_blank">here</a>.</p>

<p>Using Google's DNS over HTTPS (DoH) <a href="https://developers.google.com/speed/public-dns/docs/doh" target="_blank">service</a>, each hostname was resolved to its correct IP address. Using DoH here is important as it ensures that no DNS based censorship intervenes with the test. This resulted in roughly 5000 (hostname, ip) pairs. Next we selected a random subset and checked for TCP connectivity to port 443 to each of those ips (since not all would support HTTPS traffic), filtering our list down to 1370 pairs.</p>

<p>For each of these test points, we establish a TCP connection with the resolved_ip, and send a TLS ClientHello with the SNI set as the correct_hostname. We sniff and save these ClientHello packets (just the SSL layer) for use later. Similarly, we save the ClientHello packet with the SNI set as <code>facebook.com</code>. These sniffed packets can be found <a href="https://github.com/kush789/INT-SNI/tree/master/tls_client_hellos" target="_blank">here</a>.</p>

<hr>  

<p><a name="secMethod"></a>  </p>

<h1 id="methodology">Methodology</h1>

<p>The input to the test is a 2-tuple, (<code>correct_hostname</code>, <code>resolved_ip</code>). We would like to understand the behaviour of a middlebox when it observes a ClientHello message containing an SNI for a website it wishes to block.</p>

<p>First, we calculate the <code>min_ttl</code> for a given test point. We begin by establishing a TCP connection with <code>resolved_ip</code>.</p>

<pre><code class="language-python">import socket  
import random  
from scapy.all import *

resolved_ip = "103.224.212.222"  
dport = 443 # TLS connection  
sport = random.randint(1024, 65535) # Random source port

def create_connection(resolved_ip):  
    s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
    s.bind(("usb0", 0)) # Was using a tethered mobile connection for the experiment

    IP_PACKET = IP(dst = resolved_ip)

    seq = random.randint(12345, 67890) # Randomise initial seq number
    SYN = TCP(sport = sport, dport = dport, flags = "S", seq = seq)
    SYNACK = sr1(IP_PACKET / SYN)
    ACK = TCP(sport = sport, dport = dport, flags = "A", seq = seq + 1, ack = SYNACK.seq + 1)
    send(IP_PACKET / ACK)
    return IP_PACKET, ACK
</code></pre>

<p><strong>Note</strong>: When the linux kernel feature gets a TCP packet to an unknown socket, it sends a RST back to the originator. Since we'll be creating our own raw sockets, we need to suppress these outbound RSTs from the kernel using iptables before running experiments.</p>

<pre><code class="language-bash">sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP  
</code></pre>

<p>Once the TCP connection has been established, we send ClientHello messages (containing <code>facebook.com</code> in SNI) after updating the TTL (<code>probe_ttl</code>) in the underlying IP header. We specify <code>facebook.com</code> in the SNI so that the middlebox doesn't attempt to terminate the connection. <code>min_ttl</code> would be the minimum TTL at which we receive a TLS ServerHello or TLS Alert from the host.</p>

<pre><code class="language-python"># Load ClientHello with garbled hostname in SNI (sniffed earlier, read Data Preparation)
max_ttl = 35

def find_min_ttl(resolved_ip)

    with open("tls_client_hellos/facebook.com", 'rb') as fp:
        tls_client_hello_facebook_com_sni = fp.read()

    for probe_ttl in range(1, max_ttl):
        IP_PACKET, ACK = create_connection(resolved_ip)
        IP_PACKET.ttl = probe_ttl
        del IP_PACKET.chksum # Will force scapy to recalculate checksum after TTL update

        resp, _ = sr(IP_PACKET / ACK / tls_client_hello_facebook_com_sni, timeout = 2, retry = 0, multi = True)

        for _, ans_packet in resp:
            tls_alert = ans_packet.get(tls.TLS, {}).get(tls.TLSAlert)
            tls_server_hello = ans_packet.get(tls.TLS, {}).get(tls.TLSHandshakes, {}).get(tls.TLSServerHello)

            if tls_alert or tls_server_hello:
                return probe_ttl # min_ttl found!
</code></pre>

<p>Next, we send ClientHello messages containing the <code>correct_hostname</code> in the SNI with TTL increasing from 1 to <code>min_ttl</code> - 1. If there is no middlebox interfering with the connection, all such requests should receive either an ICMP Time Exceeded in response or no response at all. If at any point we receive an RST packet which seems to be originating from <code>resolved_ip</code>, we can say with certainty that the packet was forged by a middlebox.</p>

<pre><code class="language-python">min_ttl = find_min_ttl(resolved_ip, tls_client_hello)

with open("tls_client_hellos/fullhd720.com", 'rb') as fp:  
    tls_client_hello_correct_sni = fp.read()

for probe_ttl in range(1, min_ttl):  
    IP_PACKET, ACK = create_connection(resolved_ip)
    IP_PACKET.ttl = probe_ttl
    del IP_PACKET.chksum # Will force scapy to recalculate checksum after TTL update
    resp, _ = sr(IP_PACKET / ACK / tls_client_hello_correct_sni, timeout = 2, retry = 0, multi = True)

    for _, ans_packet in resp:
        icmp_packet = ans_packet.get(ICMP)
        if icmp_packet is not None:
            print ("Found ICMP message of type %d" %(ans_packet[ICMP].type))
            continue

        tcp_packet = ans_packet.get(TCP)
        if tcp_packet and (tcp_packet.flags &gt;&gt; 2) % 2 == 1:
            print ("Found RST at hop %d" % (probe_ttl))
</code></pre>

<p>Using our methodology, we mine the following information for our each point in our test list:  </p>

<ul>  
<li> <b>min_ttl</b>: Minimum TTL at which TLS ServerHello / TLS Alert received (if any) in response to a ClientHello with `facebook.com` as SNI</li>  
<li> <b>min_correct_sni_RST</b>: Minimum TTL at which RST received (if any) in response to a ClientHello with correct_sni</li>  
<li> <b>min_correct_sni_TLS</b>: Minimum TTL at which TLS ServerHello / TLS Alert received (if any) in response to a ClientHello with correct_sni</li>  
</ul>

<p>The script for running this test can be found <a href="https://github.com/kush789/INT-SNI/blob/master/runInt.py" target="_blank">here</a>. Logs for each test run can be found <a href="https://github.com/kush789/INT-SNI/tree/master/airtel_logs" target="_blank">here</a>. Code for mining the information above from the logs is present in <a href="https://github.com/kush789/INT-SNI/blob/master/Log%20Analysis.ipynb" target="_blank">this python notebook</a>.</p>

<hr>

<p><a name="secAirtel"></a>  </p>

<h1 id="examiningairtelsbehaviour">Examining Airtel's behaviour</h1>

<p>From our list of 1370 potentially blocked websites, there were <strong>1058</strong> instances where we received RST packets in response to ClientHellos with <code>correct_sni</code>. In all of these cases, the RST seemed to be originating from <code>resolved_ip</code>, and <code>min_correct_sni_RST</code> was less than <code>min_ttl</code>. <strong>This implies the presence of a middlebox deliberately terminating connections</strong>.</p>

<p>Furthermore, in <strong>170</strong> cases, we also received ICMP Time Exceeded alerts at the same <code>probe_ttl</code> at which we received RST packets. On further analysis, we found these RST packets to be originating from 25 unique middleboxes. Checking with ipinfo.io revealed that <strong>16 of these were registered to <code>airtel.com</code>, and 9 were registered to <code>bhartitelesonic.com</code></strong>. More information regarding these middleboxes can be found in <a href="https://github.com/kush789/INT-SNI/blob/master/Airtel%20middle%20box%20identification.ipynb" target="_blank">this python notebook</a>.</p>

<p>In <strong>290</strong> cases, we received TLS ServerHellos / TLS Alerts in response, indicating no network interference. This is expected, since we started with a list of <strong>potentially</strong> blocked websites.</p>

<p>Apart from the above, there were a few test failure due to connectivity issues, which we did not probe further.</p>

<hr>

<p><a name="secRef"></a>  </p>

<h1 id="references">References</h1>

<ol>  
<a name="cite1"></a>  
<li><b>Where The Light Gets In: Analyzing Web Censorship Mechanisms in India.</b> IMC 2018. Tarun Kumar Yadav, Akshat Sinha, Devashish Gosain, Piyush Kumar Sharma, and Sambuddho Chakravarty. <a href="https://arxiv.org/pdf/1808.01708.pdf" target="blank">PDF</a>  
</li>

<a name="cite2"></a>  
<li><b>Internet Censorship in China: Where Does the Filtering Occur?</b> PAM 2011. Xueyang Xu, Zhuoqing Morley Mao, and J. Alex Halderman. <a href="https://web.eecs.umich.edu/~zmao/Papers/china-censorship-pam11.pdf" target="blank">PDF</a>  
</li>

<a name="citeRFC6066"></a>  
<li><b>RFC 6066; Transport Layer Security (TLS) Extensions: Extension Definitions.</b> 2011. Donald E. Eastlake 3rd</li>

</ol>]]></content:encoded></item><item><title><![CDATA[How India Censors The Web]]></title><description><![CDATA[<p><b>Update (11th April 2020): This paper has been accepted at <a href="https://websci20.webscience.org/">ACM Web Science 2020</a>. A preprint can be accessed on <a href="https://arxiv.org/pdf/1912.08590.pdf" target="_blank">arXiv</a>.</b></p>

<p>Nation states around the world engage in web censorship using a variety of legal and technical methods. India is no different in this regard: the Government of India can</p>]]></description><link>http://iamkush.me/how-india-censors-the-web/</link><guid isPermaLink="false">87e1437a-d9f4-4e2d-9fd8-acfba116b9f1</guid><category><![CDATA[internet censorship]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 26 Jan 2020 15:44:04 GMT</pubDate><media:content url="http://iamkush.me/content/images/2020/01/censorship.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2020/01/censorship.jpg" alt="How India Censors The Web"><p><b>Update (11th April 2020): This paper has been accepted at <a href="https://websci20.webscience.org/">ACM Web Science 2020</a>. A preprint can be accessed on <a href="https://arxiv.org/pdf/1912.08590.pdf" target="_blank">arXiv</a>.</b></p>

<p>Nation states around the world engage in web censorship using a variety of legal and technical methods. India is no different in this regard: the Government of India can legally order internet service providers (ISPs) operating in its jurisdiction to block access to certain websites for its users. This makes the situation different from jurisdictions like Iran and China, where internet censorship is largely centralised. Legal provisions in India, namely Section 69A and Section 79 of the Information Technology (IT) Act, allow the Central Government and the various courts in the country to issue website-blocking orders that ISPs are legally bound to comply with. <strong>Most of these orders are not publically available</strong>.</p>

<p>Recent events and the opaque nature of internet censorship in India motivated us at <a href="https://cis-india.org/" target="_blank">The Center for Internet and Society</a> to study India's censorship mechanism in detail. We spent the last year trying to answer two questions pertaining to how internet users in India experience web censorship:</p>

<ol>  
<li>What are the technical methods of censorship used by ISPs in India?</li>  
<li>Are all ISPs blocking the same websites?</li>  
</ol>

<p><strong>Our work has been so far the largest study of web censorship in India</strong>, both in terms of the number of censorship mechanisms that we test for and the number of potentially-blocked websites (PBWs). </p>

<h3 id="datacuration">Data curation</h3>

<p>We compiled a list of PBWs from three sources:  </p>

<ul>  
<li><b>Government orders</b>: A website/URL blocking order may come from the Government of India (Section 69A, IT Act). These orders are usually not in the public domain, as a confidentiality clause prevents any party from disclosing its contents. We collect published and leaked Government orders.</li>  
<li><b>Court orders</b>: The various courts in India also have the power to issue website blocking orders (Section 79, IT Act). Not all such orders are available in the public domain. However, the Government and BSNL (a public company operating as an ISP) have provided portions of this list when under pressure to respond to Right to Information (RTI) requests.</li>  
<li><b>User reports</b>: <a href="https://internetfreedom.in/" target="_blank">The Internet Freedom Foundation</a> collects and publishes reports from internet users who notice blocked websites.</li>  
</ul>

<p>Collecting data from these sources led to a total of 9673 unique URLs, which yielded 5798 unique websites. To limit ourselves to active websites, we exclude all websites for which we could not resolve via Tor circuits, culminating in a corpus of 4379 PBWs.</p>

<h3 id="networktestsfordetectingcensorship">Network tests for detecting censorship</h3>

<p>We designed four network tests that probe the existence of censorship at the DNS, TCP, HTTP, and TLS level. For the sake of brevity, I'll skip elaborating on the tests in this post; the details can be found in our <a href="https://arxiv.org/pdf/1912.08590.pdf" target="_blank">preprint</a>.</p>

<p>We run these tests for each website in our corpus from connections of six different ISPs (Jio, Airtel, Vodafone, MTNL, BSNL, and ACT), <strong>which together serve more than 98% of Internet users in India</strong>. Our findings not only confirm that ISPs are using different techniques to block websites, but also demonstrate that different ISPs are not blocking the same websites.</p>

<h3 id="results">Results</h3>

<p>In terms of censorship methods, our results confirm that ISPs in India are at liberty to use any technical filtering mechanism they wish: there was, in fact, no single mechanism common across ISPs. </p>

<p>We observe ISPs to be using a melange of techniques for blocking access, such as DNS poisoning and HTTP host header inspection. <b>Our tests also discern the use of SNI inspection being employed by the largest ISP in India (Jio) to block HTTPS communication, the use of which is previously undocumented in the Indian context</b>.</p>

<p><img src="https://i.imgur.com/vDAgGnf.png" alt="How India Censors The Web">
<center style="margin-top : -35px;">Censorship techniques employed by Indian ISPs</center></p>

<p>Further, we notice that all ISPs using multiple censorship mechanisms are not blocking the same websites with each mechanism. For instance, ACT uses only DNS censorship for blocking 233 websites, only HTTP censorship for 1873 websites, and both to block 1615 websites. Such irregularities are illustrated below.</p>

<p><img src="https://i.imgur.com/azWaVJW.png" alt="How India Censors The Web">
<center style="margin-top : -35px;">Censorship techniques used by (i) ACT, (ii) Airtel, and (iii) Jio for blocking websites. We notice the same ISP using multiple techniques for blocking different websites.</center></p>

<h3 id="somealarmingdiscoveries">Some alarming discoveries</h3>

<p>Our study has recorded large inconsistencies in website blocklists of different Indian ISPs. From our list of 4379 PBWs, we find that 4033 are being blocked by at least one ISP’s blocklist. In terms of absolute numbers, we notice that ACT blocks the maximum number of websites (3721). Compared to ACT, Airtel blocks roughly half the number of websites (1892).</p>

<p>Perhaps most surprisingly, we find that only 1115 websites out of the 4033 (just 27.64%) are blocked by all six ISPs. <b>Simply stated, we find conclusive proof that Internet users in India can have wildly different experiences of web censorship.</b></p>

<p><img src="https://i.imgur.com/XXaRpuf.png" alt="How India Censors The Web"></p>

<p>Analysing inconsistencies in blocklists also makes it clear that ISPs in India are:</p>

<ol>  
<li>Not properly complying with website blocking (or subsequent unblocking orders), and/or </li>  
<li>Arbitrarily blocking websites without the backing of a legal order.</li>  
</ol>

<p>This has important legal ramifications: <b>India’s <a href="https://bit.ly/netneutralityframework" target="_blank">Net Neutrality regulations</a>, codified in the license agreements that ISPs enter with the Government of India, explicitly prohibit such behaviour</b>.</p>

<p>Our study also points to how the choice of technical methods used by ISPs to censor websites can decrease transparency about state-ordered censorship in India. While some ISPs were serving censorship notices, other ISPs made no such effort. For instance, Airtel responded to DNS queries for websites it wishes to block with <strong>NXDOMAIN</strong>. Jio used <strong>SNI-inspection</strong> to block websites, a choice which makes it <strong>technically impossible for them to serve censorship notices</strong>. Thus, the selection of certain technical methods by ISPs exacerbates the concerns created by the opaque legal process that allows the Government to censor websites.</p>

<h3 id="summingup">Summing up</h3>

<p>Web censorship is a curtailment of the right to freedom of expression guaranteed to all Indians. There is an urgent need to reevaluate the legal and technical mechanisms of web censorship in India to make sure the curtailment is transparent, and the actors accountable. </p>

<p>The whimsical attitude towards web censorship from both ISPs and the Government necessitates the development of a crowdsourced tool to <strong>monitor and measure such censorship from different vantage points in the country</strong>. This will shed further light into the geographical variation of censorship practices by ISPs across India, which is still unclear.</p>

<p>To probe this further we have ported our network tests into an android application, and are looking for volunteers who are willing to run it on their mobile networks. The entire process will be <strong>completely anonymous</strong>; we will not be collecting any user-specific information. If you live in India, please consider running <a href="https://play.google.com/store/apps/details?id=com.censorwatch.netprobesapp">Censorwatch</a>.</p>

<p><strong>Acks</strong> - This study was done in collaboration with <a href="https://gurshabad.github.io/" target="_blank">Gurshabad Grover</a> and <a href="https://www.linkedin.com/in/bansalvarun96/" target="_blank">Varun Bansal</a> at <a href="https://cis-india.org/" target="_blank">The Center for Internet and Society</a>, graciously supported by the <a href="https://www.macfound.org/" target="_blank">MacArthur Foundation</a>.</p>]]></content:encoded></item><item><title><![CDATA[Searching for ideal facial attributes using the Genetic Algorithm and Facenet's Variational Autoencoder]]></title><description><![CDATA[We come up with a framework where a user can gradually improve the attribute vector (and hence the image decoded by Facenet's VAE), using a Genetic Algorithm]]></description><link>http://iamkush.me/facenetga/</link><guid isPermaLink="false">15dfc193-213a-4521-86c1-8e569b2a45e5</guid><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 14 Apr 2019 11:29:00 GMT</pubDate><content:encoded><![CDATA[<p>During my AI class back in college (taught by <a href="https://www.iiitd.ac.in/mayank">Prof. Vatsa</a>), we were given an interesting exercise which required us to come up with a face morphing technique. The intended use case was one where a user was tasked with describing a face (maybe of a wanted felon?). In such cases, starting from scratch can be incredibly tough. However, morphing different facial attributes (extracted from different face images) as per the user's feedback can make the process faster. </p>

<p>Having recently stumbled across <a href="https://github.com/davidsandberg/facenet">Facenet</a> (back then), I was curious whether I could make use of it to develop something similar. Although Facenet doesn't really allow us to construct an image from scratch, it does allow us to change the facial attributes of an input image. It uses a Variational Autoencoder to learn an attribute vector for input images. By making changes to the attribute vector, we can change the facial attributes of an input image as required.</p>

<p>TL;DR In this post, we try to come up with a framework where a user can gradually improve upon a face image as they want (with respect to its attributes). We use a GA to improve upon the attribute vector which generates the image. At each iteration, we pick the attribute vectors (chromosomes) which generated the top-k images (closest to the target/intended target). In this exercise, we initially pick up a target image and compared the intermediatory images by taking a simple MSE. In case of a real-life scenario, a user could be simply selecting the top k images which fit the criterion. Of course, we can expose this vector directly to the user and ask them to make changes as they deem fit. However, it might be more easy for a user to pick some top-k closest images to their target, rather than changing attributes directly.</p>

<p>The python notebook (as an HTML) is available <a href="http://iamkush.me/gavaefacenet/">here</a>. It's an extremely large file, sorry about that! I no longer have access to the server with the code, so cannot regenerate it with lesser images.</p>

<h4 id="variationalautoencodersandfacenet">Variational Autoencoders and Facenet</h4>

<p>A Variational Autoencoder (VAE) is a variation (haha) of the autoencoder which tries to learn the distribution of the training input samples rather than just a dense representation. We try to map the input onto a distribution (a latent vector), rather than a fixed vector. A simple way to do is to couple the standard autoencoder reconstruction loss with and the deviation of the learnt representation from an expected (pre-decided) distribution. This loss can be computed using something like <a href="https://www.countbayesie.com/blog/2017/5/9/kullback-leibler-divergence-explained">KL divergence</a>. </p>

<p>The <a href="https://github.com/davidsandberg/facenet/wiki/Variational-autoencoder">Facenet VAE</a> employs a much fancier approach to compute the loss, which in fact allows for extremely granular control over features of the generated image. The learnt latent vector, in fact, allows modifying facial attributes of an image (and hence is appropriately named <strong>attribute vector</strong>).</p>

<p><img src="https://raw.githubusercontent.com/wiki/davidsandberg/facenet/20170708-150701-add_smile.png" alt="img">
<center>Adding different degrees of smile to faces by changing the smile attribute value in the Facenet latent vector. Image from the Facenet Github wiki.</center></p>

<p>I will skip explaining VAEs in detail in this post to save up on space. <a href="https://lilianweng.github.io/lil-log/2018/08/12/from-autoencoder-to-beta-vae.html">This blog</a> by Lilian Weng does a great job at explaining autoencoders and other variants, including VAE. Would really recommend having a look if this piques your interest.</p>

<h4 id="learningwithgeneticalgorithm">Learning with Genetic Algorithm</h4>

<p>The genetic algorithm is a fascinating evolutionary algorithm inspired by natural selection. It is an iterative process, each iteration comprising of a population of individuals (called a generation). Each individual is represented by a vector of genes called chromosome. The goal is to search for a set of chromosomes which are the <em>best</em>, according to some criterion.</p>

<p>Each generation gives rise to a set of chromosomes in the next iteration of GA. This is done via two techniques, known as <strong>cross-over</strong> and <strong>mutation</strong>. A crossover takes place between two parents and generates a chromosome (child) with genes taken both the parents. This combination can be done randomly, or via selecting certain contiguous segments of the parents' chromosome. Mutation generates a child by taking a chromosome (parent) and randomly changing any one of its genes.</p>

<h5 id="survivalofthefittest">Survival of the fittest</h5>

<p>We generally restrict the population size at each generation. Not doing so would make the population grow exponentially, making it computationally impossible to simulate. This is done by using a fitness function and selecting those individuals with the highest fitness score. In the beginning, we randomly generate a set of chromosomes. At every generation, we expect to have a set of chromosomes that have a higher (or at least equal) fitness score compared to the chromosomes in the previous generation. This process allows us to gradually reach a target chromosome.</p>

<p>The description of GA above is very brief, would recommend  <a href="https://burakkanber.com/blog/machine-learning-genetic-algorithms-part-1-javascript/">this blog</a> by Burak Kanber for a detailed write-up.</p>

<h4 id="usinggawiththefacenetvae">Using GA with the Facenet VAE</h4>

<p>In our GA formulation, we consider the latent vector used to generate images in the Facenet autoencoder as the chromosome of an individual in the population. We make use of the pre-trained Facenet decoder, passing the chromosome as input to generate an image. Starting off from a population of randomly initialized chromosome attribute vector, we would expect to gradually improve upon the generated image until it becomes the target image. </p>

<p>At each step, we generate a set of images and ask the user to select the top-k best (this can be thought of as the fitness function). Using this set, we generate the next generation of chromosomes (attribute vectors). It is important to note that the fitness function is not applied directly on the individuals in a population (since there is no target latent vector, only a target image). </p>

<p>For our experiment, due to the absence of a user, we chose a target image and then for each chromosome in a population, we calculate the fitness as follows. The chromosome is first decoded into an image, and then the fitness is calculated by taking the inverse of the MSE between the image generated and the target image. Let <code>g(z)</code> be the generator function (the Facenet decoder) which generates the image <code>I</code> after taking <code>z</code> as an input. If the target image is <code>T</code>, we calculate the fitness as the inverse of <code>MSE(I, T)</code>, i.e. the inverse of <code>MSE(I, g(z))</code>.</p>

<p>Below are the images with the highest fitness function at different generations. We see how we slowly move towards a latent vector which generates the required smile.</p>

<p><img src="https://i.imgur.com/icSaXXA.png" alt="img">
<center>Top 10 from initial population, randomly initialised latent vectors</center></p>

<p><img src="https://i.imgur.com/e35iQRJ.png" alt="img">
<center>Top 10 after 100 generations</center></p>

<p><img src="https://i.imgur.com/GIi7uNY.png" alt="img">
<center>Top 10 after 500 generations</center></p>

<p>In another scenario, a user can choose the top few images that he/she thinks is ideal, which can form the next generation of individuals. This way, the user can gradually improve upon how he/she wants the attributes in the image to look like. </p>

<p><img src="https://i.imgur.com/c139rhb.png" alt="img">
<center>The original image, target image, image generated by best latent vector learnt at the end of 500 generations.</center></p>]]></content:encoded></item><item><title><![CDATA[Not all data is important -- Estimating stickiness of DLA simulations using local regressors]]></title><description><![CDATA[We use local regression models to estimate stickiness of a DLA simulation as a function of the surface area of the figure.]]></description><link>http://iamkush.me/sticky/</link><guid isPermaLink="false">5431cff7-2277-40d4-9445-5d3b68d8213d</guid><category><![CDATA[data]]></category><category><![CDATA[estimation]]></category><category><![CDATA[regression]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 13 Jan 2019 12:14:22 GMT</pubDate><media:content url="http://iamkush.me/content/images/2019/01/Screen-Shot-2019-01-11-at-6-01-50-pm-1.png" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2019/01/Screen-Shot-2019-01-11-at-6-01-50-pm-1.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors"><p>This is a follow up to my previous post on <a href="http://iamkush.me/dlasim">fast DLA simulations</a>. Please do read that before continuing further! </p>

<p>In this article, we try to come up with a model which can help us estimate the stickiness of a DLA simulation. Given an image from a DLA simulation (a binary matrix, 1 indicating a particle and 0 free space), we would like to estimate the value of <code>k</code> used for that particular simulation. This was actually given to me as a part of a recruitment process, and for simplicity, the range of <code>k</code> was set to [1e-3, 5e-2]</p>

<p>P.S. Python notebooks with the analysis done in this article can be found <a href="https://github.com/kush789/Diffusion-Limited-Aggregation/blob/master/DLA%20stickiness%20analysis.ipynb">here</a>. The notebook with estimation approach can be found <a href="https://github.com/kush789/Diffusion-Limited-Aggregation/blob/master/Estimate%20k.ipynb">here</a>.</p>

<h3 id="thestickinessparameterk">The stickiness parameter -- <code>k</code></h3>

<p>I briefly touched upon <code>k</code> in the previous post, the stopping criterion of a particle undergoing Brownian motion depends on this parameter. As soon as a moving particle collides with a particle at rest, the moving particle stops with the probability <code>k</code>. The higher the value, the higher the chance of a particle coming to rest (and hence, the stickier the particle). This parameter tends to change a DLA simulation quite a bit. Let's have a look at 3 simulations with 10,000 particles, but with varying stickiness. </p>

<p><img src="https://i.imgur.com/lVJehoF.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Simulations with stickiness value 1.0, 0.5 and 0.1 respectively</center></p>

<p>Just by examining a few such simulations, it is clear that a low k value entails a more "compact" (or "crowded") image. If we can somehow come up with a nice property that can capture this crowding, we can think of modelling that as a function of the stickiness parameter. For my analysis, I used the total surface area of the image to capture the crowding. This was computationally fast to compute (order of \(n^2\)) and gave a good idea about the level of crowding of the image. We would expect the surface area to increase (less crowding) as k increases.</p>

<h4 id="extractinginformation">Extracting information</h4>

<p>Given a DLA simulation, let's look at what all information we can extract</p>

<ul>  
<li>The size of the canvas</li>  
<li>The number of particles</li>  
<li>The position of each particle in the canvas</li>  
</ul>

<p>Clearly, surface area can easily be calculated from the image. We just need to check each particle and see how many of its neighbouring cells are empty. For the rest of the article, I'll use <code>n</code> to denote the total number of particles, and <code>S.A.</code> to denote the surface area.</p>

<h4 id="generatingdata">Generating data</h4>

<p>Before we start modelling anything, it was essential to first get the data. This was pretty straightforward since we had already written the DLA simulation code. We split the range of <code>k</code> into equal intervals using <code>np.linspace(1e-3, 5e-2, 40)</code>, and run 15 simulations of 20,000 particles each. For each run, we calculate and save the surface area after the addition of every 100 particles. This gave us 15 readings of surface area for each n in <code>range(100, 20001, 100)</code>. For each value of n, we take the average surface area across the 15 runs. In total, we generated roughly 8000 <code>(n, k, S.A.)</code> tuples, which we further use in our model. <br>
All this data is available <a href="https://github.com/kush789/Diffusion-Limited-Aggregation/tree/master/logFiles">here</a>.</p>

<h4 id="variationofsurfaceareawithnandk">Variation of Surface Area with <code>n</code> and <code>k</code></h4>

<p>As a sanity check, we first plotted the value of surface area vs n, keeping k fixed. Obviously, this should have a strong positive correlation. </p>

<p><img src="https://i.imgur.com/jTYjFir.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Surface area vs number of particles for different values of stickiness</center></p>

<p>From the above plots, it's clear that the total surface area increases with n. We initially assumed that the graph would be strictly increasing, but later on realised that its wrong to claim this, as in some rare cases addition of a new particle can lead to a decrease in the surface area of the figure (think of a 3x3 square of particles with one of the edges having an empty space).</p>

<p>Next, we tried to see how surface area varies with the stickiness value (keeping n fixed). If this looks like something we can capture using a model, we don't need to look further.</p>

<p><img src="https://i.imgur.com/BDQuRXb.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Surface area vs stickiness for different <code>n</code></center></p>

<p>As soon as I plotted these, I got really excited. This clearly looks somewhat like a log distribution! However the only downside, it's not bounded in a fixed range (look at the y-axis for different n). That should have been expected since the total surface area is bound to depend on <code>n</code> as well. So we need to find a way to eliminate this from our model. A very naive way to this is to divide the surface area with <code>n</code>. </p>

<p><img src="https://i.imgur.com/PKaQTyF.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Surface area / n vs stickiness for different <code>n</code></center></p>

<p>Honestly, I didn't expect this to work since I couldn't come up with a proof that justified that dividing by <code>n</code> (and not some power of <code>n</code>) should normalize the range, but these plots looked really promising. The average surface area seems to be a better property to fit a curve on. Next, I checked how <code>surface area / n</code> varies with <code>log(k)</code></p>

<p><img src="https://i.imgur.com/t5SmhUq.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Surface area / n vs log(stickiness) for different <code>n</code></center></p>

<p>These plots looked pretty good to fit a curve on, so I stopped further alterations to the data. It looks like a straight line as <code>k</code> increases, small values of <code>k</code> don't look good though. So I tried with second-degree polynomials as well.</p>

<h3 id="fittingacurve">Fitting a curve</h3>

<p>There were two important things to decide before beginning to fit a curve</p>

<ul>  
<li>How should we split the data into train/test</li>  
<li>What metric should be used to evaluate the model</li>  
</ul>

<p>The data which we generated from the simulations were <code>(n, k, S.A.)</code> tuples. I really wanted to eliminate <code>n</code> completely, since training separate models for each value of <code>n</code> just wasn't possible. Let's have a look at the scattering of surface area values for different ranges of <code>n</code>, which might help us understand whether we can eliminate <code>n</code> or not.</p>

<p><img src="https://i.imgur.com/nQUel8G.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Surface area / n vs log(stickiness) for different <code>n</code>. Points from all simulations</center></p>

<p>We notice that as the value of <code>n</code> increases, the average surface area tends to scatter less. Let's explore further for smaller values of <code>n</code>.</p>

<p><img src="https://i.imgur.com/rwtccHW.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Surface area / n vs log(stickiness) for different <code>n</code>. Points from all simulations</center></p>

<p>Two inferences can be drawn from the analysis until this point</p>

<ul>  
<li>Values of surface area scatter less as n increases. Hence, the estimation will be more accurate for larger n. </li>  
<li>As n increases, this scattering becomes negligible. Furthermore, for large n (above 1000), the curves are extremely similar. Hence we can eliminate n completely from models which have a large number of particles. </li>  
</ul>

<p>So eliminating <code>n</code> completely was not feasible, since it will tend to fail for smaller values. Another interesting idea here is to use some sort of a windowed curve fitting. For a given <code>n</code>, instead of using complete data, we only use the data from simulations where the number of particles is in the range [n - c, n] for some c. </p>

<p>To check whether my intuition was correct, I simply partitioned the data into two parts and used <code>np.polyfit</code> for curve fitting, checking for up to 3-degree polynomials. I trained models on data where the number of particles were in <code>range(100, 15000, 100)</code> and tested it on data from simulations where the number of particles were in <code>range(15000, 20000, 100)</code>.</p>

<p><img src="https://i.imgur.com/yoHOKK8.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Predictions on the test set, using complete training data</center></p>

<p>This did seem like a good fit, the error in predicted <code>k</code> was low (we're bounded in [1e-3, 5e-2]). Error % = 0.00166000318459 / (5e-2 - 1e-3) = 3.38 %</p>

<p>However, notice that the training error is high. This was expected because we saw high scattering for small <code>k</code>. Let's try to see if using only data closer to our given <code>n</code> can give a better fit. The next plots show predictions by a model trained in <code>range(10000, 15000, 100)</code>, tested on <code>range(15000, 20000, 100)</code>.</p>

<p><img src="https://i.imgur.com/AoPElTB.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Predictions on the test set, using partial training data closer to test set</center></p>

<p>As expected, the training error went down in this case. However, it was really interesting to notice that <strong>error on the test has also decreased!</strong> E % = .00106134661616 / (5e-2 - 1e-3) = 2.16 %. Our estimate improved when we discarded data for n much smaller than the target n. This strongly suggests that we should be using a local window regression model.</p>

<h3 id="traininglocalmodels">Training local models</h3>

<p>The idea is to use only data from a fixed range of n smaller than the given n. Since training time is negligible, we can train the model in real time depending on the given <code>n</code> in the DLA image. We already have simulation data for n up to 20,000 particles. The next obvious question that pops up is, what happens if <code>n</code> is so large that we don't have simulation data. From the above analysis, we can safely say that the curve more or less remains the same for any number of particles above 10,000.</p>

<p>For each value of n in <code>range(1000, 20000, 1000)</code>, we trained regression models on simulation data where the number of particles was in the range [n - 4000, n - 100]. </p>

<p><img src="https://i.imgur.com/DGCIj3d.png" alt="Not all data is important -- Estimating stickiness of DLA simulations using local regressors">
<center>Predictions on the test set, using local regressors</center></p>

<p>For the 3-degree poly, the worst error percentage (0.0019) is better than the average train error in the previous fit. Furthermore, the training error also went down. Hence a local fit is definitely a better one. </p>

<p>So our strategy to predict stickiness is the following. For a given n, we take a window of n from the data and fit a 3rd-degree polynomial. We use this polynomial to predict the k for the given n. Code for this approach can be found <a href="https://github.com/kush789/Diffusion-Limited-Aggregation/blob/master/Estimate%20k.ipynb">here</a>.</p>]]></content:encoded></item><item><title><![CDATA[Fast Diffusion Limited Aggregation Simulations]]></title><description><![CDATA[A mathematical approach to fast diffusion limited aggregation simulations using a minimum bounding circle optimisation. Code available on Github
]]></description><link>http://iamkush.me/dlasim/</link><guid isPermaLink="false">f8561120-498b-489a-80e2-77e492c0736e</guid><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Mon, 07 Jan 2019 14:04:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2019/01/Screen-Shot-2019-01-07-at-7-31-08-pm-2.png" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2019/01/Screen-Shot-2019-01-07-at-7-31-08-pm-2.png" alt="Fast Diffusion Limited Aggregation Simulations"><p>Diffusion Limited Aggregation is a growing object consisting of particles that diffuse and aggregate to the object, much like how flocks of snow are created. Particles are introduced one after the other and made to undergo a random walk. Eventually, a newly introduced particle reaches an existing particle and remains there. This clustering of particles leads to a formation of aggregates of particles. This theory, proposed by T.A. Witten Jr. and L.M. Sander in 1981 is applicable to aggregation in any system where diffusion is the primary means of transport in the system. <a href="https://www.astro.rug.nl/~offringa/Diffusion%20Limited%20Aggregation.pdf">(André Offringa's report)</a>.</p>

<p><img src="https://raw.githubusercontent.com/kush789/Diffusion-Limited-Aggregation/master/k%3D1.gif" alt="Fast Diffusion Limited Aggregation Simulations" title=""> 
<center>A DLA simulation with 10,000 particles</center></p>

<p>Another interesting description (and my favourite) involves a city square surrounded by taverns. Drunks leave the taverns and stagger randomly around the square until they finally trip over one their insensate companions at which time, lulled by the sounds of peaceful snoring, they lie down and fall asleep. The tendril-like structure is an aerial view of the sleeping crowd in the morning (<a href="http://paulbourke.net/fractals/dla/">Paul Bourke's blog</a>).</p>

<p>P.S. Code for the simulations is available in <a href="https://github.com/kush789/Diffusion-Limited-Aggregation">this</a> repository.</p>

<p>This article is the first of two parts on DLA. It's mostly about my implementation of a 2D DLA simulation with a point attractor, and an optimization I use to make these simulations faster. I plan to follow this up with another one on the stickiness factor estimation (more on stickiness below).</p>

<h3 id="bruteforcedlasimulations">'Brute force' DLA simulations</h3>

<p>Generating these images is pretty straightforward.  </p>

<ul>  
<li>We begin by taking a blank square grid (all white pixels) with one black pixel in the centre</li>  
<li>Then we introduce a new particle (a black pixel) randomly at any point on the boundary.</li>  
<li>This new particle undergoes Brownian motion till it comes in contact with another black pixel on the canvas.</li>  
<li>As soon as the new pixel comes in contact with another black pixel, it stops moving and stays there forever.</li>  
</ul>

<p>The pixel undergoing Brownian motion can move to any of its eight neighbours with equal probability. This version of the DLA which we discussed has a point attractor (i.e. new particles get attracted towards a central point). This attractor itself can have a different locus (it could be a line or a circle), but we just focus on point attractors for the moment.</p>

<p>Another attribute which I incorporate in this model is called 'stickiness'. As soon as the new particle undergoing Brownian motion comes in contact with an existing particle at rest, instead of stopping, it stops with some pre-decided probability (stickiness factor) <code>k</code>. </p>

<p>A python notebook of this simulation can be found <a href="https://github.com/kush789/Diffusion-Limited-Aggregation/blob/master/Diffusion%2BLimited%2BAggregation.ipynb">here</a>. It has a <code>DLA</code> class which can be initialised with the size of the square grid and the stickiness factor <code>k</code>. The <code>addPoint</code> method can be used to add any number of particles to the canvas, the <code>printState</code> method can be used to print the grid out (uses <code>matplotlib.pyplot.imshow</code>).</p>

<h3 id="makingthesimulationsrunfaster">Making the simulations run faster</h3>

<p>As you would have guessed, adding a new particle is a very time-consuming process, especially as the number of particles increases. To accommodate a large number of particles, we need a larger grid. As a result, the area which a new particle explores before coming to rest increases. Of course, an easy way to quicken the whole process is to not use Python. Let's discuss a more mathematical approach.</p>

<p>At each iteration of DLA, we introduce a particle \(C_1\) at a randomly selected position \((x, y)\) on the border of our m x m canvas. This particle follows Brownian motion in the canvas until it meets the termination criterion. As m increases, the area in which \(C_1\) can move also increases, making simulations slow. In order to quicken the process, we would like to decrease the search space. Intuitively, we would like to spawn as close as possible to the current group of particles in the canvas.  </p>

<p>We can do this by finding a group of points \(L_2\) such that the probability that a point that spawned on the square border reaches any of the points in \(L_2\) is the same. Formally, this would be a locus such that any two points \(l_{21}\) and \(l_{22}\) in it satisfy the condition</p>

<p>\[W_{reach}(a, b) = \frac{n(a, b)}{\sum_{c \in L_1}n(c, b)}\]</p>

<p>\(L_1\) is the set of points which make up the square boundary. \(W_{reach}(a, b)\) is the ratio of the number of ways of reaching \(b\) from \(a\) to the total number of ways of reaching \(b\) from all points in \(L_1\). We take a ratio since there are infinitely many ways of reaching \(b\) from any point in \(L_1\).</p>

<p>If the area covered by locus \(L_2\) is less than the area of the square boundary, then we can hope to see a faster DLA. Intuitively it seems \(L_2\) can be a circle. We shall be proving it below.</p>

<p><img src="https://i.imgur.com/WCg15R2.png" alt="Fast Diffusion Limited Aggregation Simulations"></p>

<h3 id="minimumboundingcircle">Minimum bounding circle</h3>

<p>\(W_{total}\) of a point \(C_2\) on the circle is calculated by summing \(W_{reach}(c, C_2)\) for all points \(c\) on the square.
\[ W_{total}(C_2) = \sum_{c \in side1}W_{reach}(c, C_2) \quad + \sum_{c \in side2}W_{reach}(c, C_2) \]
\[ \quad \quad \quad \quad \; \; +  \sum_{c \in side3}W_{reach}(c, C_2) \quad + \sum_{c \in side4}W_{reach}(c, C_2) \]</p>

<p>Now we shall show that \(W_{total}\) is equal for all points on the circle. There are two assumptions we make to simplify our analysis</p>

<ol>
<li>Instead of considering discrete coordinates, we shall consider all points on the 2D plane.  </li>
<li>We define \(W_{reach}(a, b) = k(|a_x - b_x| + |a_y - b_y|)\), where \(k\) is a constant.</li>
</ol>

<p>Using our two assumptions,</p>

<p><img src="https://i.imgur.com/smyZvXe.png" alt="Fast Diffusion Limited Aggregation Simulations"></p>

<p>Similarly,</p>

<p><img src="https://i.imgur.com/0QZeauC.png" alt="Fast Diffusion Limited Aggregation Simulations"></p>

<p>Therefore,</p>

<p>\[ W_{total}(C_2) = \sum_{c \in side1}W_{reach}(c, C_2) \quad + \sum_{c \in side2}W_{reach}(c, C_2)  \]
\[ \quad \quad \quad \quad \; \; +  \sum_{c \in side3}W_{reach}(c, C_2) \quad + \sum_{c \in side4}W_{reach}(c, C_2) \] 
\[ = 4\frac{S^2}{2} - kSR\cos\theta + kSR\sin\theta + kSR\cos\theta - kSR\sin\theta \]
\[ \quad \quad \quad \quad + 2R^2\cos^2\theta + 2R^2\sin^2\theta + 2R^2\cos^2\theta + 2R^2\sin^2\theta \]
\[ = 2S^2 + 4R^2 \]</p>

<p>Clearly \(W_{total}(C_2)\) is a constant, for every \(C_2\) on the circle with radius \(R\). Hence the probability of reaching any point on a circle with radius \(R\) is the same. <strong>As a result, we can choose to spawn at any point on the circle with a radius \(R\) instead of the square boundary.</strong></p>

<p>The only constraint is that all the points outside the circle should be free. And obviously, we would like \(R\) to be as small as possible. Hence while spawning a new point, we chose the smallest circle that encloses the current image (minimum bounding circle).</p>

<p>A python notebook of DLA simulations using this optimization can be found <a href="https://github.com/kush789/Diffusion-Limited-Aggregation/blob/master/Diffusion%2BLimited%2BAggregation%2BOptimized.ipynb">here</a>. Unfortunately I haven't been able to do any kind of benchmarking comparing the running times, however, I was able to go up to 10,000 particles (compared to 500 in the previous version) in a reasonable amount of time.</p>]]></content:encoded></item><item><title><![CDATA[Hand Cannot Erase - Realtime sound spatialization using hand gestures]]></title><description><![CDATA[<p>This monsoon semester I picked up a really interesting course, Digital Audio (DES514 @ IIITD). Although it had been ages since I'd done anything related to music (seriously), the realization that it's my last semester made me have the whole "screw grades let's pick up something fun" talk in my head.</p>]]></description><link>http://iamkush.me/spatial/</link><guid isPermaLink="false">5db422e2-c486-49a7-8848-0e3f4d572a66</guid><category><![CDATA[supercollider]]></category><category><![CDATA[kinect]]></category><category><![CDATA[audio]]></category><category><![CDATA[design]]></category><category><![CDATA[digital audio]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sat, 29 Dec 2018 08:44:15 GMT</pubDate><media:content url="http://iamkush.me/content/images/2018/12/final-1.png" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2018/12/final-1.png" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures"><p>This monsoon semester I picked up a really interesting course, Digital Audio (DES514 @ IIITD). Although it had been ages since I'd done anything related to music (seriously), the realization that it's my last semester made me have the whole "screw grades let's pick up something fun" talk in my head.</p>

<p>P.S. This also led me to pick up a philosophy course. <br>
P.P.S. The semester didn't have a happy ending.</p>

<p>Anyway, coming back to the topic. Probably the most intense part of the course was the project, which <a href="https://www.facebook.com/anant3110">Anant</a> and I did together. We wanted to do something related to audio modulation (obviously), but not just stick to computational manipulations.</p>

<p>Before I delve further,  </p>

<ul>  
<li>TL;DR We created an interface which allows a user to move a source of sound around in a ring of speakers using his hand</li>  
<li>A quick demo video can be found <a href="https://drive.google.com/file/d/1x3B64rgcRc3Vv2wGzT-TozqwEyDNoiPC/view?usp=sharing">here</a></li>  
<li>All code can be found in this <a href="https://github.com/anant3110/HandCannotErase">repo</a></li>  
</ul>

<p>Although it is really tough to capture this project on camera (since we are experimenting with 8 channels, whereas a standard camera has mostly 2 input channels), you may be able to observe some changes in sound amplitude (mostly unwarrented due to the reverb in the small room hahaha).</p>

<h3 id="designphilosophy">Design Philosophy</h3>

<p>We decided that we'd like to build an interface that would meet three broad goals  </p>

<ul>  
<li>It should be capable of modifying an input audio signal real-time.</li>  
<li>It should allow the user to have a high degree of control over how the input is signal is modified.</li>  
<li>The interface design should be minimal and it should feel natural (no one likes wires hanging from their body).</li>  
</ul>

<p>The first goal was easy to meet. We had been using <a href="https://supercollider.github.io/">SuperCollider</a> (an open source platform for audio synthesis and algorithmic composition) throughout the semester, which can be used to modify an input audio signal in a few lines of code. An example would be passing the input signal through a low pass filter (LPF). And the best part, we can change parameters (like the max pass frequency of the LPF) and it gets reflected in the output signal <strong>in real-time</strong>.</p>

<p>The real challenge was merging this with our last two goals. We definitely did not want a wearable device, and at the same time wanted to allow the user to use his/her entire body to modify the input signal. As you would have guessed, we decided to move forward with a <a href="https://en.wikipedia.org/wiki/Kinect">Kinect sensor</a>. Although that meant coding in Visual Studio (and using Windows), it was perfect for our use case (especially since it came with its own gesture detection APIs).</p>

<h3 id="designingaprototype">Designing a prototype</h3>

<p>For our project (since we just had a week to finish), we decided to move forward with just spatialization of the input sound signal in a circle using hand movements of the user. Once this was possible, porting it to include other effects (such as an LPF/HPF would be just grunt work).</p>

<p>Our interface would constitute of a server running the Kinect SDK, using which we detect the direction in which the user's hand is pointing. This information is sent to another server running supercollider. The angle is used to pan a given input signal across the speakers in such a way that it seems as if the source of sound is at the direction in which the user is pointing.</p>

<p>We had access to a brand new 8.1 surround system (big shout out to Prof. Timothy!), so panning the input signal across speakers was something which we could actually test. A rough schema of what we had in mind</p>

<p><img src="https://i.imgur.com/Z3QoOym.png" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures"></p>

<p>It was a straightforward design which has 3 different components, each of them explained in detail below. Getting these three components together was the tricky part.</p>

<h3 id="detectinghandmotionusingmicrosoftkinect">Detecting hand motion using Microsoft Kinect</h3>

<p>The Kinect sensor uses depth sensing and other computer vision approaches to estimate the skeleton of the person standing in front of it. The Kinect developer SDK allows us to use this information captured by the hardware. Using this SDK, we can capture the 3-D coordinates of the certain important points detected on the body (joints, fingers etc). </p>

<p>Since we can detect the coordinates for both the body centre and the fingers, we used these to calculate the x and y coordinate of the hand in the plane perpendicular to the body of the user. This allows us to understand two things</p>

<ul>  
<li>The direction in which the user is pointing towards.</li>  
<li>How close is the user's hand from the center of his/her body.</li>  
</ul>

<p align="center">  
<img src="https://www.codeproject.com/KB/dotnet/KinectGettingStarted/8.png" width="300px" height="300px" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures"><br>  
Image is borrowed  
</p>

<p>We send this information to the superCollider server running on a different machine using OSC, a protocol running over UDP.</p>

<h3 id="panningsoundinsupercollider">Panning sound in superCollider</h3>

<p>For computing the output signal streams for the eight different channels (each for a given speaker), we used the <a href="http://doc.sccode.org/Classes/VBAP.html">VBAP plugin</a> for Supercollider3. This takes a signal and an angle from the median plane and redistributes the signal into a number of channels assuming the angle to be the source of the sound. This plugin is based on Vector base amplitude panning, more information on which can be found <a href="http://legacy.spa.aalto.fi/research/cat/vbap/">here</a>.</p>

<p>For ease in testing, we create a GUI which displays the entire ring of speakers. There is a movable pointer, which is used to indicate the source of the sound. The pointer can be moved around in the circle as shown below.</p>

<p><img src="https://lh3.googleusercontent.com/z--4Wr-r_tfNWhWhr1rXYPTQA-uCgxBQC86s3tSrHxT05p2KPgxrT1stlrJGbZQOlpdS07C1F0bkXKiOtUnaQjYeFSoGyI65hao1rjK71Oe0KWD0PwpRWyfrWFYDjTZtyStnah9LItYAUfI0Ipg-t1BQB4HbMfq-u8cI5LJnrUdcpBdKalHpyWZJLOgQmmetiL3CpRANtAsjajz2sKXn7ACWf1pIWlEiJrCCz6XkiusaoWRMMiKcXJ6AfwqFHBgBdi7jfvGp2mJV3dRaHJkn_3HK1ZrdKuI63bOnqFgWJCzT28YkcKrkh_gUuYKIse5tvfifwCEZpdXaVeH3LU5ZgpJT6oE-BCWbpSKXq0D775bTdGSjMkUJ_GpNXlTt6dljh89-vPh6S6bzFGrITAYDI_yvRsf6hIU9laAHW5_6fn0U4XqtvQpcy4YXyyImWBg6tmtoH3J2HcXPLqViyTzHJN1sg5Y4q6mkisY7Qa1CjghKUOxBYEEs-ttI_T45G3XwaJRuCpnC27Ff0kh8VEJRR8y6yTfOGsjZdgXN4rmRTMNexcnWaRzQWMLxCinh_d5I8CjscjvcpvkPzmNpCiMyXVhNV7zYeLtcywaY0fqJUekYC5zsdqIosNpcQAnULPSWlKthC8E_-ABKYpxk-sr4wDF6=w635-h379-no" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures"></p>

<p>This pointer is used to indicate the source of the sound. If we point towards the speaker C, it would sound as if the source of sound is the speaker C. </p>

<p><img src="https://lh3.googleusercontent.com/FRWY1f7Xzpux938u2T0y_T8qCKKzY-LQUOI4x9OLlRZSUutk4xCezm5RwSQTDeppTsHnktQKK1V95zeX7LhprdqYrBHVpd5EdBFjMe8RSGDq4jAhMZTAmQBNEeatoM182S24YNQOy9kVxk6RmEJB_5sOZukGUIzMl4t_OlDrm8n4BVGEWngPZg-we9SmP-ZW2h9gCmYRmfw_6Fzocgtl5biUlAMHsbPSzC5rruV4QYxF6W9zfVRhZ-jUMj7EB1mSP5uYxv0wctx0HIZTotdDMJMqKKgpBvrroyjYu5F4y9jg-vBPCQTNraajBTB36AVXfOSSKHRBf747IAMkE3AEpY17ZOuPJJGJ1Il92YST8dgrxJEIv_q_Tn1SGwRLjD9M3orlT8klXbhvnMflPK_Yr0-wCzQMDK4ifhVdYlQVvgZnTIWEHIqdgstIx3GECxVLxgtUZcwyUa3NOdwl9MjZ_0Sg3zJIbefGQBx8SMUlKQ-PHb9Ff7bBWXszX13ghT2GRbq3AdPJKa0GSxDOfzJBGp9SPIebjTEK7V93k0civwQaeKonU5TwsYcDmBQnhhN-wiEkCyLUipBJSFdOM61PXeF4ZXGt_P55KhBp7HiiigcpaYl6IJnm4NFoGxKEp23dFu0iounmWWnDrG5VBUXal0Gs=w627-h371-no" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures"></p>

<p>We also introduce a parameter ‘spread’, which allows us to widen the area from which the sound comes. This allows us to change the distance of the sound source (or how far the user perceives it to be).</p>

<p><img src="https://lh3.googleusercontent.com/WrtfZb-zS4PDRcSYyVlZbifnC3zTsMWq5GHJ0LWN2fCW4PyJZbD-fHdQSZVjguKKMNSllo1EjK4fsJF-8yIuLw_AVQ1RKjsreUN2viYmt77DcLnIUCawgtXSx7wiQedZgrtyhrxMTJZ44yCmWQH5Y0g-rvCce7V0ewGcCKcyrxsUyIZ8o0RA7h1zKZXDPjr4rLJVpEB0k3SD9UvbNf7plNc7VKamX4VqeYEBt4ExUPc69nuz-jUJ7k-pu2seYVVk1OBoRq6e5zb1aBEcPMsOC_4_CoDnyjz1e1Gid-iGwZDyrCoIhJYYtgT-gE9UQ1swIaQUhn_5MLhLglOEvwOYd1GhsdcLB-ISs-hsbo1L1PvgttrpsZFSBX5T68eF2sbbt0jIH6uawd-0-1qEZKOHa0fPPuicCJUgruvjHwX2kBhxm0Ez7XTVKnyLaNwgXSGC6wSmnz58blOZBiCOp99rP9jHRYPuuNWUVTFYh1P2Ro0pwbpkuH-plEg_UwO_Wp5V4CZWwcCmWMKPRPPlOSFvkX3m8pIYRKZK4dlv9T8Wl08SnQDGSRGKLxknWDJMdQHGemQbGTre3Za9CI7l7trf_HAVHScqrZQvlxCV-w7ZZu4OJfpMSKApQJuGd0liaMksZl5bFwaXRhXpLkepkYnPh0o1=w555-h329-no" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures">
<img src="https://lh3.googleusercontent.com/Y-8MygQTMjVUVkvFqYOV-csLoT9vqe-kW2NavWKdko4W_lMOuB3eVXiDxPOcX9EGtQq3WJx9Oq7z0grzgXL274ufB2Xec7jUUoipI6lD04Q-TPgWoviSffxxlJoYISDZ71Uc8YhFYlFIoVZOodDmOhGlP1JTqoNamOw9amLG3fsJzOsTHkeXHB36gMFjUr0hRsuMCf8yoqgP91qxUICWAHQPQIHh_sKsdzJ845MqomSMgHEdMDmiUaq2xHnsD8LtLq91Q-aY1cXkcVo-js6Msu2loj3NKOHPdyxPuGXqxl_gWi3s6NTcRjpiaOxsBqlaE5OiD5EtSL8EuBJ-pBzbBJsIx83K8FkWeGE5Pjg5E9i8unVjb5KyL5nLFLV3NBhq7ypIboecklXsp0_0WMp6A775bq6AVOavhV9QzkuFJwtvE49enKNKE2owz_48qHdxBXcdyWA65z4G6VW1tZkwbr7VUAPLLFQ-dt7Zut-md__JYo2oDd5nm87_hNr_2gr6x3B3P7AvzXc21vBanIVvfTJ3gy01NmwB6YoiCpO1hQoIBg7fpw4j1EmHjd8ep_y7HzCc3-sreoWsdP036L6dfUypiEblUs2HR74Rk19j2-8evc7WGRGJc1wfKLO6aIEL2FTIDOIvVip6OgOg3rba2uUR=w558-h332-no" alt="Hand Cannot Erase - Realtime sound spatialization using hand gestures"></p>

<p>We were able to modify the code for a 5.1 surround panner (in superCollider examples) and adapt it to the 8.1 surround setup which we had. </p>

<p>The pointer can be moved around using the mouse, the spread can be changed using a slider. However, our end goal was to link this with the user’s movements. We do so by using the information captured by the Kinect sensor. </p>

<h3 id="coordinateupdateinrealtime">Coordinate update in real time</h3>

<p>We make use of Open Sound Control protocol to transfer detected coordinates from the server running Kinect to the server running supercollider. OSC makes use of the UDP, which can be used between two machines on the same local network. While coding from the C# environment, we had to import a SharpOSC library into Visual Studios to be able to create a SharpOSC object and send information over the server that 2 laptops were connected to. In supercollider, we simply had to open a port and start listening to whatever was being transmitted by the Kinect server to the supercollider server IP (on the same port). </p>

<p>After receiving the user coordinates (at the superCollider server) form the Kinect stream, we simply updated the coordinates of the pointer and the spread values based on the distance between the user’s center and his/her hands. This update was done every 10 microseconds (using the AppClock API), allowing us to make updates in almost real time.</p>]]></content:encoded></item><item><title><![CDATA[An artificial intelligence for the 2048 game]]></title><description><![CDATA[An artificial intelligence  solver for the 2048 game using the depth bounded expectimax search algorithm. Watch the AI in action at iamkush.me/2048]]></description><link>http://iamkush.me/an-artificial-intelligence-for-the-2048-game/</link><guid isPermaLink="false">b98a985b-e683-416f-bde3-527e78e0c3c7</guid><category><![CDATA[Algorithms]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Code]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 19 Nov 2017 21:32:00 GMT</pubDate><content:encoded><![CDATA[<p>Long ago, I saw a friend playing the <a href="https://gabrielecirulli.github.io/2048/">2048</a> game. One go at it, and I have been hooked on ever since. Sadly though, I haven't been able to get the 2048 tile and win it. It was really frustrating, so I decided to write an artificial intelligence to solve the game. In this post, I will discuss my approach for writing the solver.</p>

<blockquote>
  <p><a href="http://iamkush.me/2048/"><strong>Check out the AI in action</strong></a></p>
</blockquote>

<p>I forked gabrielecirulli's <a href="https://github.com/gabrielecirulli/2048">2048 repository</a> and wrote the AI on top of it. This saved a lot of time, as I just had to write the AI, and not implement the whole game from scratch. <strong>The source code for the AI is in</strong> <a href="https://github.com/kush789/2048"><strong>this repository</strong></a>.</p>

<h3 id="goalofthegame">Goal of the game</h3>

<p>2048 is played on a 4 X 4 grid, with four possible moves up, left, down and right. The objective of the game is to slide numbered tiles on the grid to combine them and <strong>create a tile with the number 2048</strong>. </p>

<p>There are <strong>four possible moves</strong> from which a player can choose at each turn. After each move, <strong>a new tile is inserted into the grid at a random empty position</strong>. The value of the tile can be either 2 or 4. </p>

<p>From the source code of the game, it was clear that the <strong>value is 2 with a probability 0.9</strong>. From the source code of the game,</p>

<pre><code class="language-javascript">var value = Math.random() &lt; 0.9 ? 2 : 4;  
</code></pre>

<h3 id="theaialgorithm">The AI algorithm</h3>

<p>I used a <strong>depth bounded expectimax</strong> search to write the AI. At every turn, the AI explores all the four possible directions and then decides the best one. The recurrence relation that it follows is </p>

<p><img src="http://iamkush.me/content/images/2015/10/CodeCogsEqn--1-.gif" alt="recurrance"></p>

<p>The <strong>max node</strong> is the one in which the <strong>player chooses a move</strong> (out of the four directions), and the <strong>chance node</strong> is the one in which the <strong>board inserts a random tile</strong>. The max node decides the correct move by maximising the score of its children, which are the chance nodes.</p>

<p>Due to space and time restrictions, it is obviously not possible to explore the complete search space. The <strong>search is bounded by a depth</strong>, at which the AI <strong>evaluates the score of the state</strong> (the leaf node) using some heuristic.</p>

<p>The pseudocode is given below.</p>

<pre><code class="language-python">bestMove(grid, depth, agent):  
    if depth is 0:
        return score(grid)
    elif agent is BOARD:
        score = 0
        for tile in grid.emptyTiles():
            newGrid = grid.clone()
            newGrid.insert(tile, 2)
            score += 0.9 * bestMove(grid, depth - 1, PLAYER)
            newGrid = grid.clone()
            newGrid.insert(tile, 4)
            score += 0.1 * bestMove(grid, depth - 1, PLAYER)
        return score/len(grid.emptyTiles())
    elif agent is PLAYER:
        score = 0
        for dir in [left, up, right, down]:
            newGrid = grid.clone()
            newGrid.move(dir)
            score = max(score, bestMove(newGrid, depth - 1, BOARD)
        return score
</code></pre>

<h3 id="evaluatingaleafnode">Evaluating a leaf node</h3>

<p>I used a combination of two heuristics to evaluate how <strong>good</strong> a state was. </p>

<h4 id="1usingaweightmatrix">1. Using a weight matrix</h4>

<p>In most of the games that I came close to winning, the bigger tiles were around the corner. Hence the first idea that I used was to <strong>push the higher value tiles to one corner</strong> of the grid. For this, I assigned different weights to different cells.</p>

<p><center> <br>
$$
W = \begin{pmatrix} <br>
6 &amp; 5 &amp; 4 &amp; 3 \\ <br>
5 &amp; 4 &amp; 3 &amp; 2 \\ <br>
4 &amp; 3 &amp; 2 &amp; 1 \\ <br>
3 &amp; 2 &amp; 1 &amp; 0 \\ <br>
\end{pmatrix}
$$
</center></p>

<p>The score of a given 4 X 4 grid was calculated as following</p>

<pre><code class="language-javascript">score = 0  
for i in range(0, 4):  
    for j in range(0, 4):
        score += W[i][j] * grid[i][j].value
</code></pre>

<p>This had another advantage, the new randomly generated tiles got generated in the opposite corner, and hence were closer to the smaller value ones. This <strong>increased the chances of the newly generated tiles getting merged</strong>.</p>

<h4 id="2formingclustersofequalvaluedtiles">2. Forming clusters of equal valued tiles</h4>

<p>It is obviously better for us if more tiles get merged. This happens if in a state two same valued tiles are present next to each other. </p>

<p>I calculate another value, a <strong>penalty</strong>, and subtract it from the score calculated from heuristic one. This penalty is calculated as following:</p>

<pre><code class="language-python">penalty = 0  
for each tile:  
    for each neighbour of tile:
        penalty += absolute(tile.value - neighbour.value)
</code></pre>

<p><strong>This penalty becomes large when high value tiles are scattered across the grid</strong>, hence indicating that that particular state is <strong>bad</strong>.</p>

<p>The heuristic function I'm using calculates returns <code>score - penalty</code>, each calculated as mentioned above.</p>

<h3 id="optimisationsofsearch">Optimisations of search</h3>

<p>As far as I know, there is no method to prune an expectimax search. The only way out is to avoid exploring branches that are highly improbable, however this has no use for us as each branch has an equal probability (the new tile has equal probability of popping up on any of the empty tiles).</p>

<p>Initially I was exploring nodes even if the move played by the PLAYER had no effect on the grid (i.e. the grid was stuck and could not move in a particular direction, which is a common occurrence during gameplay). Eliminating such branches did enhance the performance a bit.</p>

<h3 id="performanceoftheai">Performance of the AI</h3>

<p>The solver performs quite well. With a <strong>search depth of 6</strong>, it formed the 2048 tile <strong>9 times out of 10</strong>. With a <strong>search depth of 8, it formed the 2048 tile every time</strong> I tested it, and went on to 4096 every time as well.</p>

<p>For a better winning rate, and taking the time taken to search per move into consideration, I keep a 8 ply lookahead if the number of empty tiles in the grid is less than 4, otherwise a 6 ply lookahead. This combination leads to a win every time, and <strong>8 out of 10 times</strong> forms the 4096 tile. </p>

<p><strong>The AI achieved a best score of 177352, reaching the 8192 tile. The average score was around 40000, and the AI always won the game.</strong></p>

<p>I forgot to take the screenshot of the best run, but a closer one's here.</p>

<p><img src="http://iamkush.me/content/images/2015/11/Screen-Shot-2015-11-03-at-3-21-44-am-1.png" alt="high score">
Each move takes anywhere from 10ms to 200ms, depending upon the search space and how complex the grid is at that moment. The version <a href="http://iamkush.me/2048/">here</a> has a timeout function, calling the AI to find the next move every 50 ms. </p>

<p>It can perform better if the search depth is more, however that would make it quite slow. The fact that the code is in javascript doesn't makes things any better :D. <a href="http://iamkush.me/2048/">Have a look at the AI beat the game!</a></p>

<p>Writing the artificial intelligence was quite a lot of fun, and I learnt a lot during the process. Do let me know what you think about it in the comments section!</p>

<p>Till next time, <br>
Kush</p>

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>

<script type="text/x-mathjax-config">  
    MathJax.Hub.Config({  
        tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']],
                  displayMath: [['$$','$$'], ['\[','\]']]}
    });
</script> ]]></content:encoded></item><item><title><![CDATA[Computing cube roots using bit level hacks]]></title><description><![CDATA[Computing unsigned integer cube roots using bit level hacks. FLINT, A Fast Library for Number Theory. ]]></description><link>http://iamkush.me/computing-cube-roots-using-bit-level-hacks/</link><guid isPermaLink="false">21ac2eca-e4fe-4bfc-a8a4-1bba56da4a03</guid><category><![CDATA[Algorithms]]></category><category><![CDATA[Bit masks]]></category><category><![CDATA[Code]]></category><category><![CDATA[FLINT]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Wed, 23 Dec 2015 21:36:00 GMT</pubDate><content:encoded><![CDATA[<p>Some time back, I was working on some primality testing algorithms in the FLINT <code>ulong_extras</code> module (the <strong>Brillhart-Lehmer-Selfridge</strong> test to be precise). It required computing the cube root of the integer being tested. While implementing it, I realized that the ulong_extras module was missing a cube root function. We couldn't have used the <code>cbrt()</code> function available in <code>math.c</code>, as it was introduced in <strong>C99</strong> and FLINT is guaranteed to work with <strong>C90</strong> (You'll notice that I've used unsigned long long int instead of uint64_t below, because of the same reason). We decided to use <code>pow(n, 1./3.)</code> temporarily, however it was not reliable due to the way 1./3. is stored in memory. So I decided to write one for the ulong_extras module.</p>

<p>It did sound like a trivial task initially, given that <strong>we had to calculate only the floor of the cube root</strong> and not a floating point answer. However there was so many different algorithms to try and test against each other for efficiency, that it did end up taking quite a lot of time. One of them which I really liked involved a lot of bit level hacks, and is probably one of the most interesting pieces of code I have worked on.</p>

<p>The implementation can be found here</p>

<blockquote>
  <p><a href="https://github.com/wbhart/flint2/blob/trunk/ulong_extras/cbrt_chebyshev_approximation.c">FLINT :: ulong_extras :: n_cbrt_chebyshev_approx()</a></p>
</blockquote>

<p>The implementation exploits the way an <a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">IEEE Double-precision floating-point</a> is stored in memory. Do read up a bit on it before proceeding further.</p>

<p><img src="https://upload.wikimedia.org/wikipedia/commons/a/a9/IEEE_754_Double_Floating_Point_Format.svg" alt=""></p>

<p>This representation makes our task to calculate the cube root very simple. In this form, the integer <strong>n</strong> is stored as </p>

<p>$$
n = {(-1)}^{sign} * 1.{{fraction}} * 2^{(exponent - 1023)} <br>
$$ </p>

<p><strong>The core idea is to remove a multiple of three from the exponent, which can be easily extracted using bit masks. This reduces our problem to computing a cube root on $[0.5, 1)$.</strong></p>

<p>Since we are doing our computations on <strong>unsigned integers</strong>, we can stop worrying about the sign bit in the formula. The above expression can be rewritten as,</p>

<p>$$
n = {{residue}} * 2^{(exponent - 1022)} <br>
$$ </p>

<p>where residue is $(1.{fraction}) / 2$. Calculating the cube root,</p>

<p>$$
{\sqrt[3] n} =  {{residue}}^{1/3}* 2^{{(exponent - 1022)}/3}
$$</p>

<p>We can evaluate this value in a very efficient manner by using some <em>lookup tables</em>. There are two parts which need to be calculated, the <strong>exponent</strong> part and the cube root of the <strong>residue</strong> part. </p>

<p>Calculating the exponent part is quite easy. We calculate</p>

<p>$$x =  1 &lt;&lt; {(exponent - 1022)}/{3} $$</p>

<p>and </p>

<p>$$y = 2^{(exponent - 1022)\%{3}} $$</p>

<p>y can have three possible values (since we're working mod 3 there), and hence we can store these three values in a lookup table. Let's call this <strong>factor_table</strong>. It comprises of the values <strong>1^(1/3), 2^(1/3), 4^(1/3)</strong>. The value of the cube root of the exponent part is <strong>$x * y$</strong>.</p>

<p>Calculating the cube root of the residue is slightly trickier, since obviously we want to avoid making a call to <code>pow()</code>. We know that $ residue \in [0.5, 1)$. I split this interval into sixteen equal ones, of range <strong>0.03125</strong> each, and then used <strong>approximating polynomials</strong> to compute a good approximation of $ \sqrt[3] {residue}$.</p>

<p>I used <a href="http://docs.sympy.org/dev/modules/mpmath/calculus/approximation.html"><strong>mpmath's chebyfit</strong></a> function to compute approximation polynomials of degree 2 for each of the 16 intervals, and stored their coefficients in another lookup table. Let's call this table <strong>coefficient_table</strong>. Mpmath's chebyfit function uses the <a href="http://mathworld.wolfram.com/ChebyshevApproximationFormula.html"><strong>chebyshev approximation formula</strong></a>. I used the following call to compute the coefficients for each interval. i and j are the endpoints of the range.</p>

<pre><code class="language-python">mpmath.chebyfit(lambda x: mpmath.root(x,3), [i, j], 3, error = False)  
</code></pre>

<p>Using this, I computed a 16 X 3 2-dimensional float array (16 intervals between $[0.5, 1)$ and 3 coefficients per interval). The tricky part was <strong>how</strong> to use these values. I could have used an if else ladder to decide which of the coefficients to use, depending upon the value of the $residue$. However introducing so many branches seemed like a very bad idea. <strong>Imagine an if-else ladder with 16 branches!</strong></p>

<p>It took me a while to figure out a better solution. A much better way to do was to <strong>simply use a few initial bits of the residue</strong>. Since the residue is greater than or equal 0.5, the first bit is definitely set. The next 4 bits of the residue are sufficient to decide which set of values to use (or which interval the residue lies in).</p>

<p>For example if the residue was 0.58, then it would be represented as <strong>100101000...</strong> in the  <code>double</code>. Using a bit mask, we can extract bits 2..5, which will give us a value between $[0, 16)$. In this case, it will be 0010, i.e. 2. So we know that we have to use the values present in $coefficient\_table[2]$. Let's call this value the <strong>table_index</strong>. </p>

<p>So $\sqrt[3] {residue}$ can now be calculated as following. Let this value be <strong>z</strong>.</p>

<p><img src="http://iamkush.me/content/images/2015/12/tex2png--10-cgi.png" alt=""></p>

<p>We can save one floating point multiplication here (quite expensive), by using <a href="https://en.wikipedia.org/wiki/Estrin's_scheme"><strong>Estrin's scheme</strong></a> of evaluating polynomials.</p>

<p>Now we have solved both our subproblems, our cube root is simple the <strong>integral part</strong> of $x * y * z$, as calculated above. </p>

<p>Now that we have a way to calculate the cube root, the question is, <strong>how do we actually compute the values of exponent and residue?</strong> C does not allow bit level operations on a floating point data type.  </p>

<pre><code>&gt;kush@kush:~/Desktop$ gcc -o a test.c -lflint -lm
&gt;test.c: In function ‘main’:
&gt;test.c:162:13: error: invalid operands to binary | (have ‘double’ and ‘long int’)
  testDouble | 0xF;
             ^
</code></pre>

<p>Here I was trying to use the <code>or</code> operation on a <code>double</code>, and as expected, the code gave errors on compilation. This causes problems, as we <em>cannot access the bits of the <code>double</code> directly</em>. The solution to this, was to use <strong>unions</strong>.</p>

<p><a href="http://www.tutorialspoint.com/cprogramming/c_unions.htm"><strong>Unions</strong></a> let us store different data types, in the same memory location. The same 64 bits stored somewhere in the memory, can be accessed both as a <code>double</code>, as well as an <code>unsigned long long int</code> using this union! </p>

<p>We can declare a union comprising of a <code>double</code> and an <code>unsigned long long int</code> (an unsigned word), and perform the bit level operations on the <code>unsigned long long int</code> to access the bits of the <code>double</code>.</p>

<pre><code>typedef union {  
    unsigned long long int  uword_val;
    double double_val;
} uni;
</code></pre>

<p>So basically, if we're trying to compute the cube root of an <code>unsigned long long int</code> <strong>n</strong>, we take the following steps,</p>

<pre><code>uni alias;  
alias.doube_val = (double) n;  
unsigned long long int wanted_bits = alias.uword_val &amp; bit_mask;  
</code></pre>

<p>Now we can access all the bits using <code>alias.uword_val</code>. </p>

<p>You can have a look at the complete implementation of this method below. The <code>mp_limb_t</code> data type used is the same as an unsigned word, or a 64 bit unsigned integer in most (64 bit) processors.</p>

<blockquote>
  <p><a href="https://github.com/wbhart/flint2/blob/trunk/ulong_extras/cbrt_chebyshev_approximation.c">FLINT :: ulong_extras :: n_cbrt_chebyshev_approx()</a></p>
</blockquote>

<p>The implementation is accurate, and manages to beat pow(n, 1./3.) in all cases. I timed the two a lot of times for various sizes of n and plotted them. The timings are for a 1000 random numbers of the given size. The new method is 1.1 to 1.2 times faster than calling pow(n, 1./3.).</p>

<p><img src="http://iamkush.me/content/images/2015/12/figure_1.png" alt=""></p>

<p>The actual cube root function present in FLINT currently is slightly more complex. I used <strong>Newton iterations</strong> and an algorithm designed by <strong><a href="http://iamkush.me/computing-cube-roots-using-bit-level-hacks/v">W. Kahan</a></strong> for smaller integers, as it was turning out to be more efficient. In case of larger integers (> 46 bits), the method discussed above is more efficient, and hence used. Check it out here,</p>

<blockquote>
  <p><a href="https://github.com/wbhart/flint2/blob/trunk/ulong_extras/cbrt.c">FLINT :: ulong_extras :: n_cbrt()</a></p>
</blockquote>

<p>Let me know what you think about it in the comments section.</p>

<p>Till next time, <br>
Kush</p>

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>

<script type="text/x-mathjax-config">  
    MathJax.Hub.Config({  
        tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']],
                  displayMath: [['$$','$$'], ['\[','\]']]}
    });
</script> ]]></content:encoded></item><item><title><![CDATA[Viewing blurred content on Scribd without paying]]></title><description><![CDATA[<p>I had an economics assignment due some time back, and those who know me would be aware of my hatred towards HSS courses. I tried finding the solution manual online (shh), and was really happy when I did. But then I realized it was on Scribd, and <strong>most of the</strong></p>]]></description><link>http://iamkush.me/viewing-blurred-content-on-scribd-without-paying/</link><guid isPermaLink="false">b4ead2f7-85bc-494f-be9e-52802cc27376</guid><category><![CDATA[Code]]></category><category><![CDATA[Hacks]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 30 Aug 2015 04:06:00 GMT</pubDate><content:encoded><![CDATA[<p>I had an economics assignment due some time back, and those who know me would be aware of my hatred towards HSS courses. I tried finding the solution manual online (shh), and was really happy when I did. But then I realized it was on Scribd, and <strong>most of the PDF was blurred!</strong></p>

<p>The last time I came across such a thing, I was in the 10th grade and at that point, I couldn't do anything about it. However this time, powered with some knowledge about the <strong>awesome</strong> JavaScript (LOL), I decided to unblur the damn page.</p>

<p>After a quick inspection of the HTML code, I realized that Scribd isn't even trying -_- . I saw that Scribd was <strong>actually sending the complete document to the client</strong>, and not just the two unblurred pages. This is really dumb I believe, because once you send something to the browser, <strong>any attempt to keep it secure/inaccessible is futile</strong>. </p>

<p>All that had to be done, was change the style of each div which contained a part of the document (yes, Scribd inserts the document as unselectable text in the HTML!!), and increase the opacity of the image and voila, <strong>the page is unblurred</strong>. The script is in <a href="https://github.com/kush789/Unblur-Scribd">this repository</a>.</p>

<pre><code class="language-javascript">// Remove the boxes that say you have to pay to view
$('.autogen_class_views_read2_page_blur_promo').remove();

// Removing irritating addverts
$('.between_page_ads').remove();

// Unbluring the text
$('.text_layer').css('text-shadow', '0px 0px 0px');

// Making images darker
$('.text_layer').css('color', '#000');
$('.absimg').css('opacity', '1.0');
</code></pre>

<p>The script requires jQuery to work.</p>

<p>I used this script to make a <strong>chrome extension <a href="http://iamkush.me/unblur">Unblur Scribd</a></strong>. Install the extension, and as soon as you come across a blurred Scribd page, just click the extension to view the page.</p>

<p>One thing which I really liked about Scribd was that they <strong>load their pages dynamically</strong>. As soon as you reach a page x, an AJAX call to load page x + y is made. This saves so much overhead in the beginning when you open the URL, as you're actually loading just two pages of the document! However this makes unblurring the complete document impossible in one go, <strong>without loading the complete document first</strong>. The extension needs to be clicked every time the user sees a blurred page (that's about once every 5 to 6 pages).</p>]]></content:encoded></item><item><title><![CDATA[GSoC Pencils Down]]></title><description><![CDATA[<p>The pencils down deadline for GSoC was <strong>21st Aug, the previous Friday</strong>. It has been quite a journey, working with FLINT for over three months. I've learnt quite a lot, not just math but how to write good quality code and testing.</p>

<p>Although I haven't been able to do everything</p>]]></description><link>http://iamkush.me/gsoc-pencils-down/</link><guid isPermaLink="false">7a222e76-b20e-4051-b47a-516c2c2ee122</guid><category><![CDATA[GSoC]]></category><category><![CDATA[FLINT]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Mon, 24 Aug 2015 13:04:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2015/10/GSoC-13.jpeg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2015/10/GSoC-13.jpeg" alt="GSoC Pencils Down"><p>The pencils down deadline for GSoC was <strong>21st Aug, the previous Friday</strong>. It has been quite a journey, working with FLINT for over three months. I've learnt quite a lot, not just math but how to write good quality code and testing.</p>

<p>Although I haven't been able to do everything I had mentioned in my proposal, I worked on a lot of stuff which I didn't mention. I worked extensively on the ECM, more than I had planned and ended up writing a very good stage I and II using mpn's. Apart from that I worked on the Pollard Rho Brent algorithm, and the MPQS. The MPQS has some problems and is not functional though.</p>

<p>This week I tried to debug the MPQS. The problem with MPQS is that the sieve value is not exceeding a limit, and hence does not get evaluated. No relations are being found. I believe this is because the roots of the polynomials are being computed incorrectly. I will be looking into this eventually, once I get a break in the semester.</p>

<p>Apart from the MPQS, I also worked on the <code>fmpz_factor_smooth()</code> function. It aims to find a factor of a number, given that it is not semi prime (around n^1/3). It would be a combination of trial division, Pollard Rho Brent, p + 1 and ECM. The profiling of Pollard Rho Brent is yet to be done. Once I do that, I can finish this off.</p>

<p>What's next? I have a lot of stuff which I plan to do. First, is obviously to finish off MPQS and adding a double large prime version. Some other things which I want to work on is to improve the ECM stage II, and the <code>fmpz_factor_smooth()</code> function. I will be working on them, however at a slower speed due to the course load. </p>]]></content:encoded></item><item><title><![CDATA[MPQS in FLINT]]></title><description><![CDATA[<p>I started working on a simple MPQS this week. Most of the code required (Knuth Mutiplier, factor base, linear algebra, square root functions) was already available in the SIQS module, written/ported by Nitin. I just had to make small changes to the code to make it compatible with the</p>]]></description><link>http://iamkush.me/mpqs-in-flint/</link><guid isPermaLink="false">a17c707a-9121-4e17-bbe2-1084fdff4ba7</guid><category><![CDATA[Algorithms]]></category><category><![CDATA[Code]]></category><category><![CDATA[GSoC]]></category><category><![CDATA[FLINT]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 16 Aug 2015 08:25:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2015/10/GSoC-12.jpeg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2015/10/GSoC-12.jpeg" alt="MPQS in FLINT"><p>I started working on a simple MPQS this week. Most of the code required (Knuth Mutiplier, factor base, linear algebra, square root functions) was already available in the SIQS module, written/ported by Nitin. I just had to make small changes to the code to make it compatible with the sieve I am writing. The part which I had to write again was the <strong>polynomial code</strong>. It wasn't that tough as self initialization was not required.</p>

<p>The polynomial I am using in the sieve is <code>Q(x) = (Ax + B)^2</code>. Hence, we have to solve the equation <code>Q(x) - kN = 0</code>, i.e. <code>A^2x^2 + 2ABx + B^2 - kN = 0</code>.</p>

<p>While evaluating, we pull the factor <strong>A</strong> out, and try to factor <code>Ax^2 + 2Bx + (B^2 - kN)/A</code>. So for evaluating the sieve at different positions, we have to compute <strong>A</strong>, <strong>B</strong>, and <strong>C</strong> (C equals B^2 - kn).</p>

<p>I am using <a href="http://iamkush.me/article/www.karlin.mff.cuni.cz/~krypto/mpqs/main_file.pdf">this</a> paper as a reference. The ideal value of <strong>A</strong> is the square of a prime <strong>d</strong> near the value <strong>(N/(2 * M^2))^1/4</strong>. <strong>B</strong> is chosen as the <strong>odd square root of kN (mod A)</strong>. This is done by first calculating the square root mod d (which is a prime), and then using Hensel lifting to calculate the square root mod <strong>A</strong> (which is d^2). <strong>C</strong> can be computed easily by computing <strong>(B^2 - kN)/A</strong>.</p>

<p>d^2). C can be computed easily by computing (B^2 - kN)/A.</p>

<p>I have coded a couple of functions which implement selecting multiple polynomials and computing associated data, the most important being the roots of <code>Q(x) = N (mod p)</code>, for each prime p in the factor </p>

<ul>
<li>Functions to compute A, B &amp; </li>
<li>Functions to compute the Q(x) = N mod p for each prime p in the factor </li>
<li>Function to initialize</li>
<li>Function to compute the next polynomial</li>
</ul>

<p>The code can be found in <a href="https://github.com/kush789/flint2/blob/trunk/mpqs/compute_poly.c">this</a> file.</p>

<p>I also ported the code from the existing SIQS module for <a href="https://github.com/kush789/flint2/tree/trunk/mpqs">MPQS module</a>. The current implementation has some problems, it does not factor. The control never reaches inside the <code>mpqs_evaluate_candidate()</code> function. Hence <strong>no relations are found</strong>. I am looking into this problem currently. </p>]]></content:encoded></item><item><title><![CDATA[GSoC Update]]></title><description><![CDATA[<p>Since I've missed out on some blog posts the last couple of weeks, I'll be summing up the stuff I've been up to in this post. The past month hasn't really been as productive as I would have wished, the reason being some wrong assumptions I made. I'll be giving</p>]]></description><link>http://iamkush.me/gsoc-update/</link><guid isPermaLink="false">207d5e6e-2607-4817-ab95-2e6efa0e9b83</guid><category><![CDATA[Algorithms]]></category><category><![CDATA[Code]]></category><category><![CDATA[GSoC]]></category><category><![CDATA[FLINT]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 09 Aug 2015 13:27:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2015/10/GSoC-11.jpeg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2015/10/GSoC-11.jpeg" alt="GSoC Update"><p>Since I've missed out on some blog posts the last couple of weeks, I'll be summing up the stuff I've been up to in this post. The past month hasn't really been as productive as I would have wished, the reason being some wrong assumptions I made. I'll be giving week wise summary below.</p>

<h3 id="13thjuly19thjuly">13th July - 19th July</h3>

<p>This week I started reading the existing SIQS code, which I pulled from <a href="https://github.com/n1tz53/flint2/tree/SIQS">Nitin's SIQS branch</a>. I took me some time to understand the existing code, and where exactly I would start working to add the double large prime version.</p>

<p>While reading the code, I also found a small bug in the code. In the <code>qsieve_evaluate_candidate()</code> function, all powers of 2 were being removed before the Knuth Schroeppel multiplier is removed from the relation. <strong>This would have slowed down factorisations in which the knuth schroeppel multiplier was even.</strong></p>

<p>An issue was reported this week, concerning one of the functions I had written earlier (<a href="https://github.com/wbhart/flint2/issues/150">#150</a>). I spent some time reading the <code>n_cbrt()</code> code, and noticed that I had made a terrible mistake earlier. <strong>I had assigned wrong constants, which would have definitely caused overflows in 32 bit systems</strong>. Although this did not solve the issue reported, it was definitely something which would have caused problems. I never noticed it earlier on as I never tested the code on a 32 bit machine.</p>

<h3 id="20thjuly26thjuly">20th July - 26th July</h3>

<p>This week I started coding the double large prime version. I was using <a href="http://infoscience.epfl.ch/record/149704/files/EPFL-CONF-149704.pdf?version%3D1?ln=fr">Factoring with two large primes, A. K. Lenstra and M. S. Manasse</a> as a reference. I started by reducing the cut off limit in <code>qsieve_evaluate_sieve()</code> to <strong>produce more partials and pp relations</strong>. Once I got these relations, I used <code>n_factor()</code> and <code>n_isprime()</code> in <code>qsieve_evaluate_candidate()</code> to check whether the remainder after pulling out the factor base primes is a prime / semi prime. If so, I wrote them to a file of the format <code>[type, number of factor base primes that divide it, factor base prime's index, factor base prime's exponent, large prime I, large prime II]</code>. In case of a partial, the large prime II was simply 1. </p>

<p>Here is when I started to face problems. <strong>I simply couldn't manage to produce a lot of partials and pp's</strong>. I tried to explore the code and check whether I was making any mistake, but couldn't figure out what to do to fix it. At this point I should have switched over to the tested FLINT2 code, and extend it to a a double prime version, however I was under the impression that an SIQS was required before I could add a double large prime version. This was a wrong assumption I made.</p>

<h3 id="27thjuly2ndaugust">27th July - 2nd August</h3>

<p>I couldn't really do a lot this week, I had refresher modules and exams throughout the week at my institute (they take place before the beginning of the semester).</p>

<p>I did some minor work towards the FLINT 2.5 release this week. There was an issue <a href="https://github.com/wbhart/flint2/issues/32">#32</a> reported some time back. It was required to check that in each call of a function <code>*_si()</code>, the si parameter is actually an <strong>slong</strong> and not a <strong>ulong</strong> or <strong>mp_limb_t</strong>. This had to be done for every call across the FLINT code base. I volunteered and checked some. </p>

<h3 id="3rdaugust9thaugust">3rd August - 9th August</h3>

<p>At the beginning of this week, I realised that it is too late to implement the double large prime MPQS completely. I decided to go back to the ECM code I had written and complete some of the work I had postponed till after the GSoC period. The main reason was that I knew what was going around in the ECM really well, and thought that I could do considerable work working on it.</p>

<p>The ECM code I had written was doing good, <strong>under a factor of 2 compared to GMP-ECM</strong>. There were a couple of algorithmic enhancements I had in mind, which could make it even faster and comparable to GMP ECM. These included <strong>multi point evaluation in stage II</strong>, the <strong>Montgomery PRAC algorithm in stage I</strong> and maybe <strong>Edward coordinates</strong>.</p>

<p>I started off by checking the code for any memory leaks and unnecessary memory allocations. I found unnecessary allocations in stage II, and cut them out using better indexing (in stage II). This <strong>reduced the memory being used by almost half in the stage II code</strong>. There were a couple of places where there were memory leaks. I resolved this as well.</p>

<p>I also fixed the hack I was using in <code>fmpz_factor_ecm_select_curve()</code>. I couldn't figure out how exactly to call <code>mpn_gcdext()</code> earlier on. I realised this week that I was passing an <code>mp_limb_t</code>, whereas I should have been passing an <code>mp_size_t</code> (<strong>the latter is signed!</strong>). So when the inverse was negative, I was facing problems (the function sets the "size" argument as negative if the mp_ptr is negative). Earlier on, I avoided using <code>mpn_gcdext()</code>. When it was required, I was converting back to <code>fmpz_t's</code>, calling <code>fmpz_gcdinv()</code> and then converting back to mp_ptr's! Now I am making a direct call to <code>mpn_gcdext()</code>.</p>

<p>I also started to incorporate multi point evaluation in stage II this week. I had tried this earlier on, however contrary to my expectation, it was almost two times slower. I coded the stage II again (thankfully I had the ECM fmpz code from some time back, coding directly using mpn's would have been very difficult). However yet again, it didn't turn out to be faster than than the existing code. Maybe I'm doing something wrong theoretically? However in such a case, stage II should never be able to factor, whereas it is definitely.</p>

<p>I made some more minor changes to the ECM code and polished it, it seems good to be merged to me.</p>

<h3 id="whatnext">What next</h3>

<p>After a brief conversation with my mentor, I have decided to go back to MPQS. In the coming week, I will be working on a simple sieve, which uses multiple polynomials, without any self initialisation. A lot of code required for this already exists in the FLINT trunk.</p>]]></content:encoded></item><item><title><![CDATA[FLINT ECM for large integers]]></title><description><![CDATA[<p>This week I finished writing ecm using the mpn functions and reduction using pre-computed inverse for integers larger than one word. The code is present in these files:</p>

<ul>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_init.c">ECM structure init</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_clear.c">ECM structure clear</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_add.c">Addition on curve</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_double.c">Doubling on curve</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_mul_montgomery_ladder.c">Scalar multiplication of a point on curve (Montgomery Ladder)</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_select_curve.c">Function</a></li></ul>]]></description><link>http://iamkush.me/ecm-flint-for-large-integers/</link><guid isPermaLink="false">040589b8-4af2-45cc-88e7-f0e51a819864</guid><category><![CDATA[Algorithms]]></category><category><![CDATA[Code]]></category><category><![CDATA[GSoC]]></category><category><![CDATA[FLINT]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Sun, 12 Jul 2015 08:15:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2015/10/GSoC-10.jpeg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2015/10/GSoC-10.jpeg" alt="FLINT ECM for large integers"><p>This week I finished writing ecm using the mpn functions and reduction using pre-computed inverse for integers larger than one word. The code is present in these files:</p>

<ul>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_init.c">ECM structure init</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_clear.c">ECM structure clear</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_add.c">Addition on curve</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_double.c">Doubling on curve</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_mul_montgomery_ladder.c">Scalar multiplication of a point on curve (Montgomery Ladder)</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_select_curve.c">Function to select Curve Parameters</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_stage_I.c">ECM stage I</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_stage_II.c">ECM stage II</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm.c">ECM outer function</a></li>
</ul>

<p>Apart from these, I also wrote a couple of <strong>helper functions</strong> for mpn arithmetic:</p>

<ul>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_addmod.c">MPN Addmod</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/ecm_submod.c">MPN Submod</a></li>
</ul>

<p>These are not regular functions, and cannot be used with all elliptic points. These assume that the "n" passed has its highest bit set. I did this to save a couple of additions. We are anyway working with <strong>normalized</strong> (shifted) mpn's, and hence don't cause a problem. </p>

<p>Documentation and tests for <code>fmpz_factor_ecm()</code> is present here:</p>

<ul>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/test/t-ecm.c">Test Function</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/fmpz_factor/doc/fmpz_factor.txt">Documentation</a></li>
</ul>

<p>The current ECM is doing a good job, it takes around <strong>0.1 to 0.2 seconds</strong> on an average to find a <strong>50 bit factor</strong> (~15 digits). I never got around to finding why using multi point polynomial evaluation was slowing down stage II, whereas theoretically it should have been faster. I'll probably look at it later on. Also, I haven't profiled it and found optimum parameters (B1 &amp; B2) for different sized factors. These two things ought to speed it up further.</p>

<p>Another problem, or rather a code inconsistency exists in the <code>fmpz_factor_ecm_select_curve()</code> function. I was having some trouble using <code>mpn_gcdext()</code> at one point, so I am converting back to <code>fmpz_t</code>, using <code>fmpz_gcdinv()</code>, and then converting back to <code>mp_ptr</code>. This works, although it's quite hacky. I'll figure out the right way to use <code>mpn_gcdext()</code> and alter it. </p>

<p>The only serious problem, is that the current ECM implementation <strong>cannot find factors below 7 bits</strong>. The <code>fmpz_factor_ecm_select_curve()</code> function fails to find a suitable curve for such integers.</p>

<p>Another thing I worked upon is <code>n_factor()</code> in the ulong_extras module. <strong>Brent-Pollard Rho and ECM seem to do a better job than SQUFOF</strong>, which is used currently. So instead of just calling <code>factor_one_line()</code> and SQUFOF, I changed it so that it calls Brent-Pollard Rho once, and then ECM for some tries. This gives almost a <strong>4 to 5 time speedup</strong> when factoring semi primes, and 1.5 to 2 times for random cases. SQUFOF is still there in the code, so as to make sure that n_factor doesn't fail (in case Brent Pollard Rho and ECM do), however I never saw it being called in all the random cases I tried.</p>

<p>Including Brent-Pollard Rho and ECM did cause a problem though. <strong>Both of them require random states</strong> to be passed, and changing the function definition of <code>n_factor()</code> <strong>will break existing FLINT code</strong>. My mentor suggested that we should cache a <code>flint_rand_t</code> in the factor function itself. I am working on this right now.</p>

<p>This week, I plan to finish the small part remaining in <code>n_factor()</code> (caching of the <code>flint_rand_t</code>) and then go back to double large prime MPQS. I am using the paper <a href="http://homes.cerias.purdue.edu/~ssw/ppp.pdf">MPQS with three large primes</a> as a reference. Although named three large primes, it also gives quite a lot of information on the double large prime version.</p>]]></content:encoded></item><item><title><![CDATA[Rewriting the FLINT ECM]]></title><description><![CDATA[<p>I spent the last week rewriting the ECM using <strong>mpn's and pre-computed inverses, and ulongs</strong>. Pollard Rho did show a significant speedup when I rewrote it using mpn's and pre-computed inverses, and I was hoping for the same here. Initially I was planning to write only till stage I using</p>]]></description><link>http://iamkush.me/rewriting-the-flint-ecm/</link><guid isPermaLink="false">d72d0694-368f-4d75-8277-72b66678363c</guid><category><![CDATA[GSoC]]></category><category><![CDATA[FLINT]]></category><category><![CDATA[Algorithms]]></category><category><![CDATA[Code]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Tue, 07 Jul 2015 07:42:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2015/10/GSoC-9.jpeg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2015/10/GSoC-9.jpeg" alt="Rewriting the FLINT ECM"><p>I spent the last week rewriting the ECM using <strong>mpn's and pre-computed inverses, and ulongs</strong>. Pollard Rho did show a significant speedup when I rewrote it using mpn's and pre-computed inverses, and I was hoping for the same here. Initially I was planning to write only till stage I using the faster options available, however I ended up working on both stage I and stage II.</p>

<p>The flow which I had in mind was similar to many existing functions in FLINT. If the integer to be factorized fits in one word, use the ulong module else use fmpz's/mpn's. So I decided to rewrite ECM using the ulong module first.</p>

<h3 id="theulongversioncode">The ulong version code :</h3>

<ul>
<li><a href="https://github.com/kush789/flint2/blob/trunk/ulong_extras/factor_ecm_add.c">Group Addition of co-ordinates</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/ulong_extras/factor_ecm_double.c">Group Double of co-ordinates</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/factor_ecm_mul_montgomery_ladder.c">Group Scalar Multiplication of co-ordinates</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/factor_ecm_select_curve.c">Function to select Curve Parameters</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/factor_ecm_stage_I.c">ECM Stage I</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/factor_ecm_stage_II.c">ECM Stage II</a></li>
<li><a href="https://github.com/kush789/flint2/blob/trunk/ulong_extras/factor_ecm.c">Outer wrapper function for ECM</a></li>
</ul>

<p>Next I attempted to write ECM using <strong>mpn's</strong>. The code which I have written so far (using mpn's) works fine for single word integers, however causes problems for larger integers. It is still work in progress.</p>

<p>The current version of ECM still has a lot of room for improvement. This includes <strong>tuning</strong> it so as to find optimal values for B1 and B2. Also I still haven't been able to pinpoint my error in the multipoint evaluation stage II I wrote a couple of weeks back. It works, however is slightly slower than than the initial stage II. </p>

<h3 id="whatiplanforthis">What I plan for this</h3>

<ul>
<li>Figure out what's causing problems for integers larger than one word in the mpn version of ECM and finish writing </li>
<li>Time ECM against GMP </li>
<li>Understand the double prime QS more thoroughly, go thorough the QS which Nitin (who is working on the SIQS) has written and start writing a double prime version using it.</li>
</ul>]]></content:encoded></item><item><title><![CDATA[GSoC - Halfway through]]></title><description><![CDATA[<p>Hi!</p>

<p>I missed out last week's blog, couldn't write it as I was traveling and had poor internet connection. In the last two weeks, I have been trying out different implementations of stage II, using <strong>multi point evaluation</strong>.</p>

<p>Mid term evaluations began on the 26th of June, and I have</p>]]></description><link>http://iamkush.me/gsoc-halfway-through/</link><guid isPermaLink="false">ffdf66f1-7889-42d6-b9fc-66832d6b64cf</guid><category><![CDATA[GSoC]]></category><category><![CDATA[FLINT]]></category><dc:creator><![CDATA[Kushagra Singh]]></dc:creator><pubDate>Mon, 29 Jun 2015 13:41:00 GMT</pubDate><media:content url="http://iamkush.me/content/images/2015/10/GSoC-8.jpeg" medium="image"/><content:encoded><![CDATA[<img src="http://iamkush.me/content/images/2015/10/GSoC-8.jpeg" alt="GSoC - Halfway through"><p>Hi!</p>

<p>I missed out last week's blog, couldn't write it as I was traveling and had poor internet connection. In the last two weeks, I have been trying out different implementations of stage II, using <strong>multi point evaluation</strong>.</p>

<p>Mid term evaluations began on the 26th of June, and I have met most of the targets I had set. I have added an optimized version of pollard rho, and the ECM code should be completed by the end of this week. Although there are still a lot of things I'd like to try to make it faster, I don't want to deviate a lot from the timeline now. I'll probably get back to it after GSoC.</p>

<p>The current ECM timings are close to GMP-ECM, I'll be posting some timings next week, once I code it using mpn's. My main worry right now is the fact that <strong>using multi point evaluation is slowing down ECM, contrary to the expected speedup</strong>.</p>

<p>This week, I plan to find the reason why the multi point evaluation technique is not turning out to be faster, as expected. Also I would be rewriting ECM, at least the stage I, using mpn's.</p>]]></content:encoded></item></channel></rss>