<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://angelhof.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://angelhof.github.io/" rel="alternate" type="text/html" /><updated>2026-09-08T20:50:19+00:00</updated><id>https://angelhof.github.io/feed.xml</id><title type="html">Konstantinos Kallas</title><entry><title type="html">How to effectively read a systems paper</title><link href="https://angelhof.github.io/2024/08/21/how_to_read_a_systems_paper.html" rel="alternate" type="text/html" title="How to effectively read a systems paper" /><published>2024-08-21T00:00:00+00:00</published><updated>2024-08-21T00:00:00+00:00</updated><id>https://angelhof.github.io/2024/08/21/how_to_read_a_systems_paper</id><content type="html" xml:base="https://angelhof.github.io/2024/08/21/how_to_read_a_systems_paper.html"><![CDATA[<p>Reading a research paper takes time, and in order to get something out of it it is helpful to guide the process using a set of concrete questions about the paper and its thesis. Having a set of questions in mind can help the reader (1) better place the paper in the context of the literature, and (2) evaluate it in a more critical way.
Of course there is no one universal best way of reading a paper, but I have found that this approach works for my first reading pass.</p>

<p>Here is a set of questions that I have found to be relevant for many systems papers.
These questions have been inspired by the ones suggested at a graduate seminar course taught by <a href="https://www.seas.upenn.edu/~leebcc/">Benjamin C. Lee</a> that I attended while in grad school.
Note that different questions apply to papers in different (sub)areas and that the questions below mostly apply to papers that propose a novel computer system, usually published in venues such as SOSP, OSDI, NSDI, EuroSys, ATC, PLDI, ASPLOS, etc.</p>

<ul>
  <li><strong>Problem statement:</strong>
    <ul>
      <li>What is the problem that this paper tries to address?</li>
      <li>Why is this problem important?</li>
      <li>How is it motivated?</li>
      <li>Why is the problem not solved yet?</li>
      <li>What is the state of the art?</li>
    </ul>
  </li>
  <li><strong>Key insight:</strong>
    <ul>
      <li>What is the key insight/technique of the system solution?</li>
      <li>What assumptions does it make?</li>
      <li>Are these assumptions satisfied in general or do they break in some contexts?</li>
      <li>Is this insight portable to different domains with similar assumptions?</li>
    </ul>
  </li>
  <li><strong>Evaluation:</strong>
    <ul>
      <li>What are the key claims that the paper evaluates?</li>
      <li>Do these claims relate to the problem that was phrased in the problem statement?</li>
      <li>Does satisfying these claims address the central paper problem?</li>
      <li>For each claim:
        <ul>
          <li><strong>Benchmarks and workloads:</strong>
            <ul>
              <li>What is the set of benchmarks and workloads that are used?</li>
              <li>Are the benchmarks realistic and representative of the stated problem?</li>
            </ul>
          </li>
          <li><strong>Experimental infrastructure:</strong>
            <ul>
              <li>What is the experimental infrastructure?</li>
              <li>Do conclusions acquired using this infrastructure transfer to the problem domain?</li>
            </ul>
          </li>
          <li><strong>Baselines:</strong>
            <ul>
              <li>What are the baselines used to compare the target system against?</li>
              <li>Do the baselines correspond to the state of the art?</li>
              <li>How does the target system compare against them?</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>]]></content><author><name></name></author><category term="systems" /><category term="paper reading" /><category term="PhD" /><summary type="html"><![CDATA[Reading a research paper takes time, and in order to get something out of it it is helpful to guide the process using a set of concrete questions about the paper and its thesis. Having a set of questions in mind can help the reader (1) better place the paper in the context of the literature, and (2) evaluate it in a more critical way. Of course there is no one universal best way of reading a paper, but I have found that this approach works for my first reading pass. Here is a set of questions that I have found to be relevant for many systems papers. These questions have been inspired by the ones suggested at a graduate seminar course taught by Benjamin C. Lee that I attended while in grad school. Note that different questions apply to papers in different (sub)areas and that the questions below mostly apply to papers that propose a novel computer system, usually published in venues such as SOSP, OSDI, NSDI, EuroSys, ATC, PLDI, ASPLOS, etc. Problem statement: What is the problem that this paper tries to address? Why is this problem important? How is it motivated? Why is the problem not solved yet? What is the state of the art? Key insight: What is the key insight/technique of the system solution? What assumptions does it make? Are these assumptions satisfied in general or do they break in some contexts? Is this insight portable to different domains with similar assumptions? Evaluation: What are the key claims that the paper evaluates? Do these claims relate to the problem that was phrased in the problem statement? Does satisfying these claims address the central paper problem? For each claim: Benchmarks and workloads: What is the set of benchmarks and workloads that are used? Are the benchmarks realistic and representative of the stated problem? Experimental infrastructure: What is the experimental infrastructure? Do conclusions acquired using this infrastructure transfer to the problem domain? Baselines: What are the baselines used to compare the target system against? Do the baselines correspond to the state of the art? How does the target system compare against them?]]></summary></entry><entry><title type="html">Using try to make Copilot command suggestion a bit saner</title><link href="https://angelhof.github.io/2023/01/09/try-on-gh-copilot.html" rel="alternate" type="text/html" title="Using try to make Copilot command suggestion a bit saner" /><published>2023-01-09T00:00:00+00:00</published><updated>2023-01-09T00:00:00+00:00</updated><id>https://angelhof.github.io/2023/01/09/try-on-gh-copilot</id><content type="html" xml:base="https://angelhof.github.io/2023/01/09/try-on-gh-copilot.html"><![CDATA[<p>Here is a cool usecase of our recently published tool, <a href="https://github.com/binpash/try">try</a> that can help when using <a href="https://docs.github.com/en/copilot/github-copilot-in-the-cli">Github Copilot in the CLI</a>. Thanks to <a href="https://mkeoliya.github.io/">Mayank</a> who came up with it!</p>

<p><strong>TL;DR:</strong> Use <code class="language-plaintext highlighter-rouge">try</code> around commands suggested by LLMs to not accidentally destroy your system 😌</p>

<p>LLMs are here and developers use them increasingly often to suggest programs and commands to them by describing what they want in natural language. A particular usecase is <a href="https://docs.github.com/en/copilot/github-copilot-in-the-cli">Github Copilot in the CLI</a> which suggests shell commands that can then be easily copied to the clipboard or explained. Here is an example that I recently used to delete some garbage files that get generated when copying files from a location in Windows to WSL:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>gh copilot suggest <span class="s2">"delete all files in this directory recursively that end with :Zone.Identifier"</span>
<span class="go">
Welcome to GitHub Copilot in the CLI!
version 0.5.4-beta (2024-01-04)

I'm powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve. For more information, see https://gh.io/gh-copilot-transparency

? What kind of command can I help you with?
</span><span class="gp">&gt;</span><span class="w"> </span>generic shell <span class="nb">command</span>
<span class="go">
Suggestion:

  find . -name "*:Zone.Identifier" -delete

? Select an option
</span><span class="gp">&gt;</span><span class="w"> </span>Explain <span class="nb">command</span>
<span class="go">
Explanation:

  • find is used to search for files and directories.
    • . specifies that we want to start the search from the current directory.
    • -name "*:Zone_Identifier" specifies that we want to search for files or directories with names ending in :Zone_Identifier.
    • -delete deletes the files found.


? Select an option
</span><span class="gp">&gt;</span><span class="w"> </span>Copy <span class="nb">command </span>to clipboard
<span class="go">
Command copied to clipboard!

? Select an option  [Use arrows to move, type to filter]
</span><span class="gp">&gt;</span><span class="w"> </span>Copy <span class="nb">command </span>to clipboard
<span class="go">  Explain command
  Revise command
  Rate response
  Exit
</span></code></pre></div></div>

<p>Even though Copilot offers to explain this command, can we actually trust it??<a class="footnote" href="#fn-1"><sup>1</sup></a> <span class="footnoteText">In fact this command does not exactly capture the original intent, because it can also delete directories! One should use <code class="language-plaintext highlighter-rouge">-type f</code> to prevent that, but how could you have guessed to ask?!.</span> It is not very wise to immediately run this command on your system before first carefully looking at <code class="language-plaintext highlighter-rouge">find</code>’s manpage to determine if this usage is correct—but that beats the purpose of using an LLM in the first place. This is where <code class="language-plaintext highlighter-rouge">try</code> can be very helpful as a safeguard when running the suggested command.</p>

<p><a href="https://github.com/binpash/try">try</a> is a tool that allows you to easily run a shell command in a sandbox to first inspect its effects before commiting it. We can use <code class="language-plaintext highlighter-rouge">try</code> to run the Copilot suggested command and inspect all the modifications that it would do:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>try <span class="s1">'find . -name "*:Zone.Identifier" -delete'</span>
<span class="go">
Changes detected in the following files:

/home/konstantinos/University/job-applications/univ1/research-statement.pdf:Zone.Identifier (deleted)
/home/konstantinos/University/job-applications/univ2/teaching-statement.pdf:Zone.Identifier (deleted)
/home/konstantinos/University/job-applications/univ3/cover-letter.pdf:Zone.Identifier (deleted)
/home/konstantinos/University/job-applications/univ3/teaching-statement.pdf:Zone.Identifier (deleted)
/home/konstantinos/University/job-applications/univ4/cover-letter.pdf:Zone.Identifier (deleted)
/home/konstantinos/University/job-applications/univ5/cover-letter.pdf:Zone.Identifier (deleted)
/home/konstantinos/University/job-applications/univ6/cover-letter.pdf:Zone.Identifier (deleted)

Commit these changes? [y/N]
</span></code></pre></div></div>

<p>If the changes look OK, we can ask <code class="language-plaintext highlighter-rouge">try</code> to commit, otherwise we can revise the command by mentioning the counterexample. <code class="language-plaintext highlighter-rouge">try</code> has more features to help with exploring the changes, like using <code class="language-plaintext highlighter-rouge">try explore</code> to spawn a shell in the sandbox and play around seeing if the changes are actually reasonable. You can find out more about it on <a href="https://github.com/binpash/try">Github</a>.</p>

<p>Links:</p>

<ul>
  <li>try: <a href="https://github.com/binpash/try">Github repo</a></li>
  <li>Github Copilot: <a href="https://docs.github.com/en/copilot/github-copilot-in-the-cli">Documentation</a></li>
</ul>]]></content><author><name></name></author><category term="try" /><category term="copilot" /><category term="LLM" /><summary type="html"><![CDATA[Here is a cool usecase of our recently published tool, try that can help when using Github Copilot in the CLI. Thanks to Mayank who came up with it! TL;DR: Use try around commands suggested by LLMs to not accidentally destroy your system 😌 LLMs are here and developers use them increasingly often to suggest programs and commands to them by describing what they want in natural language. A particular usecase is Github Copilot in the CLI which suggests shell commands that can then be easily copied to the clipboard or explained. Here is an example that I recently used to delete some garbage files that get generated when copying files from a location in Windows to WSL: $ gh copilot suggest "delete all files in this directory recursively that end with :Zone.Identifier" Welcome to GitHub Copilot in the CLI! version 0.5.4-beta (2024-01-04) I'm powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve. For more information, see https://gh.io/gh-copilot-transparency ? What kind of command can I help you with? &gt; generic shell command Suggestion: find . -name "*:Zone.Identifier" -delete ? Select an option &gt; Explain command Explanation: • find is used to search for files and directories. • . specifies that we want to start the search from the current directory. • -name "*:Zone_Identifier" specifies that we want to search for files or directories with names ending in :Zone_Identifier. • -delete deletes the files found. ? Select an option &gt; Copy command to clipboard Command copied to clipboard! ? Select an option [Use arrows to move, type to filter] &gt; Copy command to clipboard Explain command Revise command Rate response Exit Even though Copilot offers to explain this command, can we actually trust it??1 In fact this command does not exactly capture the original intent, because it can also delete directories! One should use -type f to prevent that, but how could you have guessed to ask?!. It is not very wise to immediately run this command on your system before first carefully looking at find’s manpage to determine if this usage is correct—but that beats the purpose of using an LLM in the first place. This is where try can be very helpful as a safeguard when running the suggested command. try is a tool that allows you to easily run a shell command in a sandbox to first inspect its effects before commiting it. We can use try to run the Copilot suggested command and inspect all the modifications that it would do: $ try 'find . -name "*:Zone.Identifier" -delete' Changes detected in the following files: /home/konstantinos/University/job-applications/univ1/research-statement.pdf:Zone.Identifier (deleted) /home/konstantinos/University/job-applications/univ2/teaching-statement.pdf:Zone.Identifier (deleted) /home/konstantinos/University/job-applications/univ3/cover-letter.pdf:Zone.Identifier (deleted) /home/konstantinos/University/job-applications/univ3/teaching-statement.pdf:Zone.Identifier (deleted) /home/konstantinos/University/job-applications/univ4/cover-letter.pdf:Zone.Identifier (deleted) /home/konstantinos/University/job-applications/univ5/cover-letter.pdf:Zone.Identifier (deleted) /home/konstantinos/University/job-applications/univ6/cover-letter.pdf:Zone.Identifier (deleted) Commit these changes? [y/N] If the changes look OK, we can ask try to commit, otherwise we can revise the command by mentioning the counterexample. try has more features to help with exploring the changes, like using try explore to spawn a shell in the sandbox and play around seeing if the changes are actually reasonable. You can find out more about it on Github. Links: try: Github repo Github Copilot: Documentation]]></summary></entry><entry><title type="html">A preliminary proposal on an explicit faculty mentoring role</title><link href="https://angelhof.github.io/2021/06/10/faculty_mentors.html" rel="alternate" type="text/html" title="A preliminary proposal on an explicit faculty mentoring role" /><published>2021-06-10T00:00:00+00:00</published><updated>2021-06-10T00:00:00+00:00</updated><id>https://angelhof.github.io/2021/06/10/faculty_mentors</id><content type="html" xml:base="https://angelhof.github.io/2021/06/10/faculty_mentors.html"><![CDATA[<p>The summary of this (preliminary) proposal is that we need to have two types of advisors for PhD students at universities.</p>
<ul>
  <li>A mentor/advisor: the goal of whom is to offer general purpose advice and support (more on that later).</li>
  <li>A “classic” research advisor: the goal of whom is to help the student grow as a researcher.</li>
</ul>

<p>I argue that decoupling mentoring from research advising (which involves a power dynamic and an incentive tension) has several benefits for the students and for our community as a whole.</p>

<p>A disclaimer of limited-perspective: This mostly applies to the university system in the US and more specifically my field (computer science) and might not be generalizable or even make sense outside of this intersection. For example, in Europe PhDs are usually 3 or 4-years long, and students usually commit to a research advisor before even joining the program since their funding depends on them and so it is difficult to imagine how to apply such a proposal.</p>

<h4 id="competing-interests">Competing interests</h4>

<p>The main idea behind this proposal is to shift faculty incentives, so that they better align with the students’.
Currently, advisors have strong incentives to publish many<a class="footnote" href="#fn-1"><sup>1</sup></a> <span class="footnoteText">The word “many” here can be replaced with “impactful” and merely serves as an indication of the requirement for hard work.</span> research papers with their students.
This is especially true for younger faculty, who need to “prove” their worth to the department to get tenure and to funding agencies to get grants.
This leads to advisors pushing their students to work harder in order to have adequare research output.
This might not always align with students’ interests, 
    either due to temporary causes, e.g., if a student is having personal issues for a period of time, 
    or on a more fundamental level, e.g., if a student is interested in an industry or teaching career, both of which have different requirements.
In this situation, there is tension between the interests of the student and the advisor, and due to the power dynamic between them, an advisor might choose to push the students for research output even if it does not align with the student’s goals.</p>

<h4 id="lack-of-options-for-students">Lack of options for students</h4>

<p>Students in this position are usually powerless: the power dynamic makes it very hard for them to argue with their advisor in order to satisfy their own interests and agenda. 
If a student lacks perspective and has not experienced a healthy advisor-advisee relationship they might even consider that the situation is completely normal and how PhDs should be.
Currently, the best a student can do is to seek help through univesity procedures or their support network,
    and the usual response is that their alternatives are to 
    (i) push through and live with the issues until they finish their PhD,
    (ii) abandon the program (which might not always be possible if they don’t have another job opportunity streamlined),
    or in rare cases (iii) switch advisors, a process that is not encouraged in many universities and even when it is, it is stressful and requires significant effort from the student’s side.</p>

<h4 id="mentoring-as-an-explicit-faculty-role">Mentoring as an explicit faculty role</h4>

<p>Creating an explicit mentor role that is decoupled from research gives us the ability to be more specific about goals such as the student’s well-being and fullfilment; goals that are currently mostly ignored giving their place to more tangible ones like the research output and success of a student.
These goals could become part of faculty evaluation (similarly to other criteria like research output, grants, service, and teaching).
A mentor would be successful in their role if the mentee student feels that they are empowered to pursue and satisfy their personal needs, even if these needs align with the student leaving from the PhD program to pursue an alternative career.
Making the mentor role explicit and part of faculty evaluation also helps by giving mentors more power to argue for the student internally with other faculty, since their incentives are also aligned with their mentees.
A mentor’s incentive is to be an ally to their mentee, making sure that their needs and opinions are not swept under the rug.</p>

<p>Another benefit of this role separation is that it lowers the stakes of students’ decision of which PhD group to join, since they can choose the research project that they want to work in without being concerned that their mentoring needs will not be satisfied.
Mentors should be assigned to the student from the start of their PhD, while a student could commit to a research advisor later, after exploration and more discussion.
Furthermore, this proposal also benefits the university’s reputation as PhD students will be better equipped to pursue their goals and succeed.</p>

<h4 id="discussion">Discussion</h4>

<p>Note that such mentorship relations between students and faculty already exist for students that approach faculty and create connections with them.
However, the fact that it is not a proper and explicit role gives less incentive to faculty members to commit a lot of time to it.
Furthermore, students that have the ability and confidence to seek out help and mentorship from faculty members are often not the ones that need the most help.
The less connected and less confident students are often the ones that would benefit the most from such mentoring relationships; even having an external perspective from another faculty member is a important to realize that one’s situation might be problematic.</p>

<p>This proposal is complementary to mentorship services like <a href="https://www.sigplan.org/LongTermMentoring/">SIGPLAN-M</a> that focus on inter-institution mentorship relations in a specific field of CS.
Intra-institution mentors have internal power to argue for their mentees and give them leverage for their interactions with their research advisors.
Furthermore, it is unlikely (and maybe not preferable) that the mentor is in the exact same field as the student, so field-specific mentorship services like SIGPLAN-M are still beneficial since they can offer more targeted mentorship services (including additional networking, internships, connections, etc).</p>

<h4 id="open-questions">Open Questions:</h4>

<p>Here are some open questions that the above proposal leaves unanswered (some of which might also affect its feasibility). It would be interesting to think about them and discuss them with others in the community:</p>

<ul>
  <li>
    <p>What is the exact role of the mentors?</p>
  </li>
  <li>
    <p>What are the precise faculty evaluation criteria for their mentorship service? Should it be based on explicit student feedback using evaluation forms?</p>
  </li>
  <li>
    <p>Who funds students? The current proposal (conveniently) ignores this.</p>
  </li>
  <li>
    <p>Should there be research intersection between mentor and mentee? It would make sense if they are both in the same field, e.g., CS, but not necessarily in the same subfield (PL, systems, ML, theory, etc).</p>
  </li>
  <li>
    <p>Should the students be able to choose their mentor (as they do with research) advisors, or should they be assigned someone by the university and then have the ability to request for a change, e.g., if they do not feel comfortable with the pairing?</p>
  </li>
</ul>

<h4 id="conclusion">Conclusion</h4>

<p>Explicitly naming the mentoring role for faculty members in universities will allow us to focus, precisely define, and evaluate this role’s <em>extremely important</em> goals.
This could potentially help students who need it the most; ones that have weaker support networks and could end up in an unfavorable situation with their research advisors without any means to escape from it or anyone to get support from.</p>

<p>This is a very rough proposal and possibly has many holes, so feel free to contact me if you have any thoughts or feedback. I would love to hear everyone’s opinion on this :)</p>]]></content><author><name></name></author><category term="mentoring" /><summary type="html"><![CDATA[The summary of this (preliminary) proposal is that we need to have two types of advisors for PhD students at universities. A mentor/advisor: the goal of whom is to offer general purpose advice and support (more on that later). A “classic” research advisor: the goal of whom is to help the student grow as a researcher.]]></summary></entry><entry><title type="html">Advice for prospective PhD students on deciding which program to join</title><link href="https://angelhof.github.io/2020/07/05/advice_for_prospective_phd_students.html" rel="alternate" type="text/html" title="Advice for prospective PhD students on deciding which program to join" /><published>2020-07-05T00:00:00+00:00</published><updated>2020-07-05T00:00:00+00:00</updated><id>https://angelhof.github.io/2020/07/05/advice_for_prospective_phd_students</id><content type="html" xml:base="https://angelhof.github.io/2020/07/05/advice_for_prospective_phd_students.html"><![CDATA[<p>I wanted to write this post for some time now since the longer I wait,
the more my viewpoint diverges from the one I had when I was a
prospective PhD student. At the moment, my memory of looking for PhD
programs is still pretty fresh, but I have also seen the process from
the other side, getting to know some other students and whether their
decisions played out well or not. The goal of this post is to propose
some advice regarding picking a university for your PhD. Note that
this is all very subjective (and non complete), meaning that you
should take it into account together with information and advice from
other sources. Also note that I am doing a PhD in the field of
Computer Science, so the advice might not be as relevant to students
in other fields.</p>

<p>I often see that prospective PhD students (myself included) heavily
weigh their options based on the research interests of faculty
members. Event though this is important to factor in your decision, I
believe that in the end of the day, it doesn’t correlate that much
with satisfaction from the PhD program for two reasons. First of all,
it is extremely difficult to correctly assess what are someone’s
research interests before you work with them, especially considering
that faculty members often don’t maintain their websites. Second, I
think that the specifics of your PhD research are often left abstract
until the very last moment—even senior PhDs often don’t have a very
precise plan of their research agenda.</p>

<p>In contrast, what matters a lot for many people is the daily
interaction with their advisors and group. Being content during the
everyday struggles of a PhD depends a little on whether you are
optimizing a compiler pass or a distributed system, and more on
whether you appreciate and enjoy working with your collaborators. If
your advisor wants to micromanage you but you prefer a more
independent relationship, you might suffer even if you work on your
favorite problem. Of course this is mostly based on my own experience,
and it might be different for you.</p>

<p>Before moving forward I would like to note that my opinion is biased
since I have been fortunate to be surrounded by amazing collaborators,
a lot of which are now my dear friends. This includes my advisor, who
is extremely supportive, helpful, and flexible, the other faculty
members in the group, who are also very helpful and supportive, and
finally the other students and researchers, with all of whom I have an
amazing relationship.</p>

<p>Based on the above assertion, i.e. that a PhD student’s relationship
with their advisor and collaborators is more important than their
exact research topic, I would like to give some advice on how
prospective PhD students can pick a group to join. Even though the
prospective relationship with an advisor is very hard to predict and
difficult to assess from brief visits or chats, there are several
qualities that an advisor can have and don’t work for most
people. Based on this, I think that there are a couple things that one
should do to improve the chance that they end up with an advisor that
they like working with.</p>

<p>In my opinion the most impactful action by far is asking tons of
questions to everyone: present and past PhD students, postdocs, and
faculty members. Especially with students and postdocs, it helps if
the questions are not abstract, but concrete things that some people
might even feel a little uncomfortable answering. Of course the goal
is not to make people feel uncomfortable, but rather to get
as-honest-as-possible opinions on important matters. The good thing is
that after you are accepted in the program, there is nothing that can
go wrong, so you have nothing to worry about when asking these
questions.</p>

<p>Asking questions could help figure out if there are students that have
any issues with the faculty member that you are interested in working
with. Possible issues could be (but are certainly not limited to):</p>

<ul>
  <li>Being overworked</li>
  <li>Having no guidance or help</li>
  <li>Being overly micromanaged</li>
  <li>Lack of any interest from the faculty member</li>
  <li>General bad attitude and communication</li>
</ul>

<p>Even though it is difficult for people to be completely honest with
strangers, there is a lot that one can get out of their responses. In
my experience, people tend to positively boost their responses to
these types of questions, e.g. if a PhD student is unhappy with their
advisor, they won’t express their discontent, but they might just say
OK things about them. Spotting a lack of enthusiasm or positivity and
poking it with more questions could lead to some important information
about possible issues. Furthermore, if more than one person has
similar opinions about a faculty member, this strongly indicates that
the opinion might indeed be valid.</p>

<p>Since the goal is to figure out the bad aspects of the working
environment, it is beneficial talking with past PhD students too,
since they might feel more comfortable opening up about issues; in
contrast to current students whose future depends a lot on their
advisor’s future too.</p>

<p>Here are some concrete questions that one could ask (current and
past) PhD students and research assistants:</p>

<ul>
  <li>What is the worst quality about your advisor?</li>
  <li>Is there something that you don’t enjoy in your everyday life
while working with advisor?</li>
  <li>How do you like interacting/working with advisor?</li>
  <li>What is the usual interaction that you have with advisor?</li>
  <li>How does your advisor check for progress? Do you have weekly
meetings? Progress reports, etc?</li>
</ul>

<p>Finally, it is important to consider universities that have a diverse
group of faculty that you would to work with, as well as allow (and
ideally encourage) collaboration and experimentation with different
faculty members at the beginning of your PhD. As far as I know this is
mostly applicable in the US, where prospective students are accepted
by the department and are not necessarily tied to a specific professor
from the beginning. The reason why this is important is obvious; in
the end of the day, it is possible to initially misjudge a person, and
only figure out that you don’t totally align with them after the
fact. Being in a university where working with different advisors or
changing advisor is easy, gives you an opportunity to continue with
your PhD without leaving the program. Even better, the ability to work
with several people in your first couple years allows you to get more
data and then make an educated decision of you want to work with.</p>

<p>To summarize, I believe that when people decide which PhD program to
join, they often think more about their research interests, and less
about the future relationship with their advisor, assuming that it
will be fine by default. However in my experience, PhD student
dissatisfaction stems very often from issues related to a student’s
interaction with their advisor. An unhealthy student-advisor
relationship can not be easily solved, often forcing the student to
leave the program. On the other hand, if the relationship is good, a
healthy solution can be found to most of the other issues that might
arise during the degree.</p>

<p>EDIT 2023-01-25: Stephen Chong gave an <a href="https://popl23.sigplan.org/details/PLMW-POPL-2023-papers/5/Care-and-Feeding-of-Advisors">amazing talk</a> at PLMW@POPL23 about selecting 
and working with one’s advisor, which I would highly recommend to any student that
is searching for an advisor or already has one!</p>]]></content><author><name></name></author><category term="PhD" /><category term="prospective students" /><category term="advice" /><category term="opinion" /><summary type="html"><![CDATA[I wanted to write this post for some time now since the longer I wait, the more my viewpoint diverges from the one I had when I was a prospective PhD student. At the moment, my memory of looking for PhD programs is still pretty fresh, but I have also seen the process from the other side, getting to know some other students and whether their decisions played out well or not. The goal of this post is to propose some advice regarding picking a university for your PhD. Note that this is all very subjective (and non complete), meaning that you should take it into account together with information and advice from other sources. Also note that I am doing a PhD in the field of Computer Science, so the advice might not be as relevant to students in other fields.]]></summary></entry><entry><title type="html">Discussing “An optimality theory of concurrency control for databases” by Kung &amp;amp; Papadimitriou</title><link href="https://angelhof.github.io/2019/03/12/an_optimality_theory_of_concurrency_control_for_databases.html" rel="alternate" type="text/html" title="Discussing “An optimality theory of concurrency control for databases” by Kung &amp;amp; Papadimitriou" /><published>2019-03-12T00:00:00+00:00</published><updated>2019-03-12T00:00:00+00:00</updated><id>https://angelhof.github.io/2019/03/12/an_optimality_theory_of_concurrency_control_for_databases</id><content type="html" xml:base="https://angelhof.github.io/2019/03/12/an_optimality_theory_of_concurrency_control_for_databases.html"><![CDATA[<p>This blog post is an intro to a (potentially long) series of posts that revolve around correctness criteria of concurrent objects and systems, most notably linearizability, sequential consistency, serializability, etc. My objective is to write these blog posts to help me better understand the above concepts, because I have found out that writting helps me organize my thoughts on a topic, and as a consequence, understand it more deeply.</p>

<p>I was initially planning to read several important papers in the field and then organize them in a series of posts and that is why I started reading [<a href="#kung-papadimitriou-1978">Kung &amp; Papadimitriou 1978</a>]. However, while reading it, I was amazed by their results and I thought that this specific paper deserves to be a post on its own.</p>

<p>In this post, I will try to write my thoughts on this this paper, rephrasing and explaining their results, and trying to elaborate more on them. While this post was mostly written to help me understand the paper, I think that it might be helpful to read it side by side with the original paper, to maybe get a firmer grasp of some ideas or implications that could be easily skipped if one doesn’t pay too much attention (they certainly did skip me when I first read the paper). As I am far from an expert on this field (maybe not even knowledgable) I would <em>strongly</em> encourage any comment or feedback on anything that is wrong, misphrased, etc in this post.</p>

<p>After this long (and possibly unnecessary) introduction let’s get right into the paper.</p>

<h2 id="motivation">Motivation</h2>

<p>Object systems are data systems that are shared among multiple users. An object system, usually comes with a description of what it means for the data that it stores to be consistent<a class="footnote" href="#fn-1"><sup>1</sup></a>. <span class="footnoteText">A standard instance of an object system is a database.</span> It is important for the data that is stored in the object system to always satisfy those requirements. For example, imagine a banking system which contains the data about the bank accounts of the bank’s clients. A consistency requirement is that an account should never contain negative amounts of money.</p>

<p>User interact with an object system through sequences of requests (aka transactions) that read or update values of the system. Therefore an issue that naturally arises is making sure that executing the user requests on a consistent object system, should preserve its consistency. This is the task of concurrent “access” control mechanisms. In a single user environment, this task is relatively simple, since each transaction is executed as a contiguous block, and a transaction starts executing only when the previous has finished. In this setting, the problem reduces to verifying that each transaction preserves the consistency requirements of the object system (if executed on a consistent state of the system). In the multi-user setting however, the problem is much harder, because even if each transaction preserves consistency; arbitrarily interleaving their requests might not.</p>

<p>It is clear that consistency preservation should be the primary objective of a concurrent “access” control mechanism. However, it can’t be considered in isolation. A trivial solution to preserve consistency would be to execute all transactions of the first user until they log off, then execute the transactions of the second user until they log off, etc. This execution order clearly preserves consistency, and enforcing it requires minimal information from each transaction (just the id of the user who issued it). However, its performance is disastrous! The more users interact with the system, the more each user has to wait until its requests are processed. Imagine wanting to transfer some money between some accounts on an online banking system, and having to wait for all the users who logged in before you to log out before the system processes your request.</p>

<p>Therefore, there are two necessary properties to consider when designing a concurrent “access” control mechanism (which from now on we will call scheduler).</p>
<ul>
  <li><strong>Performance:</strong>
Which ideally represents how long does it take for a set of transactions to complete under the scheduler. Note that there is no universal metric of performance, so reasoning about it can be tricky.</li>
  <li><strong>Information:</strong>
Which represents the information that the scheduler needs to make its decision. This typically includes syntactic and semantic information for the transactions as well as the integrity constraints.</li>
</ul>

<p>Our objective as the designers of a scheduler is for it to preserve the consistency requirements of the database, while striking a balance between performance and information needed. In this paper, the authors formalize this tradeoff between performance and needed information, and present upper bounds for several information granularities. They show that given only syntactic information, <em>serialization</em> (often called serializability) [<a href="#bernstein-1978">Bernstein et al. 1978</a>, <a href="#papadimitriou-1978">Papadimitriou 1978</a>] is the best one can hope for and when also given semantic information, it can be relaxed to the weaker notion of <em>weak-serialization</em>.</p>

<h2 id="transaction-systems">Transaction Systems</h2>

<p>A transaction system is intuitively a triple of data, integrity constraints, and a set of prespecified transaction programs. In a sense a transaction system is like shared object, containins private fields, a set of invariants (integrity constraints) that must always hold for this data, and a set of methods (transactions) that are used to access and modify the private data.</p>

<h3 id="syntax">Syntax</h3>

<p>A <em>transaction system</em> \(T\) contains a finite set of <em>transactions</em> \(\{T_1, ..., T_n \}\), where each transaction is a finite sequence of steps \(T_{i1}, ..., T_{im}\). The transactions operate on a set of <em>variables</em> \(V\), which are abstract variables and the values that they represent are not important. Each transaction step \(T_{ij}\) contains a read from a global variable \(x_{ij}\) to a local variable \(t_{ij}\) and then an assignment to the global value \(x_{ij} := f_{ij}(t_{i1}, ... t_{ij})\). Note that the two commands are indivisible, and so each transaction step is executed atomically. Transactions are straight line programs in this simplified model, but their results also apply to more general programs.</p>

<h3 id="semantics">Semantics</h3>

<p>A state of the transaction system \(T\) is a triple \((J, L, G)\) where:</p>
<ul>
  <li>\(J\) is a tuple of program counters (one for each transaction) showing the next step of each transaction.</li>
  <li>\(L\) is a tuple of the values of all local variables.</li>
  <li>\(G\) is a product of the values of all global variables \(V\).</li>
</ul>

<p>The <em>integrity constraints</em> \(IC\) can be represented as a subset of all possible \(G\) values. A state \((J, L, G)\) is called <em>consistent</em> if \(G \in IC\). A sequence of transaction steps is said to be <em>correct</em> if a serial execution of the steps in the sequence maps <em>any</em> consistent state to a consistent state.</p>

<p><strong>Note:</strong> As stated above, the basic assumption that they make in this paper is that all transactions in the transaction system are individually correct, and so the sequential composition of any transactions is also correct. The problem that they are tackling in this paper, i.e. finding an interleaving of transactions that is correct, is orthogonal to the sequential verification of each transaction.</p>

<h3 id="schedules">Schedules</h3>

<p>They define a <em>schedule</em> \(\pi\) of a transaction system to be a permutation of the steps in \(T\) such that the steps in each specific transaction are kept in order. Intuitively a schedule is an interleaving of all transactions. The set of all schedules of \(T\) is denoted by \(H(T)\). Following the definition of correct sequences of transaction steps, a schedule is <em>correct</em> if its execution preserves the consistency of the database. The set of all correct schedules of \(T\) is denoted by \(C(T)\). Note that the set \(C(T)\) is non-empty for all transaction systems, as it contains at least all the serial schedules as described above.</p>

<h3 id="scheduler">Scheduler</h3>

<p>As stated above, the main problem that the paper deals with is designing a concurrency access control mechanism that orders (serializes) transaction steps from individual users, preserving the database consistency while not degrading performance. From now on, we will call the concurrency access control mechanism a <em>scheduler</em>. Formally a scheduler is a mapping \(S\) from \(H\) to \(C(T)\). A scheduler is correct when all of the schedules that it produces are correct, \(S(H) \subseteq C(T)\).</p>

<p>An interesting point is how they measure a performance. They define the performance of a scheduler \(S\) to be its <em>fixpoint set</em> \(P\), which is defined to be the largest subset of \(H(T)\) satisfying:</p>

\[\forall h \in P : S(h) = h\]

<p>In a sense, a scheduler’s fixpoint set is the set of transaction step sequences (schedules) that the scheduler keeps intact, allowing them to be executed in the order that they appear for execution. At first sight, it is not clear why this is a reasonable performance metric, however they justify it by arguing that the size of the fixpoint set correlates with the waiting time for each user (which can also be thought of as the latency of a request).</p>
<ul>
  <li>Assuming that the probability distribution schedules is uniform, the probability that none of the transaction steps have to wait is 
\(|P| / |H|\).</li>
  <li>The more schedules \(P\) contains, the “easier” it is to rearrange a history originaly not in \(P\) into one in \(P\).</li>
  <li>If the fixpoint set of a scheduler \(S_1\) is a strict superset of the fixpoint set of a scheduler \(S_2\), then scheduler \(S_1\) is clearly better performing than scheduler \(S_2\), so this metric allows them to partially order schedulers based on their performance.</li>
</ul>

<p>Except for the performance of the scheduler (i.e. how long do transaction steps have to wait until they are released), we must also think about the cost of the scheduler making decisions. In this paper, they address the <em>information</em> that the scheduler needs to make a decision, whereas in [<a href="#papadimitriou-1978">Papadimitriou 1978</a>] they examine the time that schedulers need to make decisions in relation to their performance.</p>

<p>The fixpoint set \(P\) of an optimal scheduler (performance wise) would be equal to \(C(T)\), as it would let all correct schedules be executed without any reordering. However, it is not always possible (nor sometimes desirable) to have a scheduler that executes all correct schedules in the order that they happened, because of the amount of information needed.</p>

<h2 id="information-levels">Information Levels</h2>

<p>In order to capture this relation of the information available to the scheduler with its performance, it is important to formally define the notion of information.</p>

<p>We say that a <em>level of information</em> that is available to a scheduler about a transaction system \(T\) is a set \(I\) of transaction systems that contains \(T\). Intuitively, the scheduler knows that \(T\) is in \(I\) but cannot distinguish it from the rest of the transaction systems. Alternatively, one could think about \(I\) as a function that maps any transaction system to an information object \(I(T)\). Then two transaction systems \(T\) and \(T'\) cannot be distinguished with level of information \(I\) if they map to the same \(I(T) = I(T')\).</p>

<p>One of the main theorems of the paper follows below:</p>

<blockquote>
  <p><strong>Theorem 1:</strong> 
For any scheduler using information I, its fixpoint set P must satisfy:</p>

\[P \subseteq \bigcap_{(T' \in I)} C(T')\]
</blockquote>

<p>The scheduler cannot distinguish between the different transaction systems in \(I\), and because of that, all the schedules that it allows to be executed without any reordering, have to be in the correct schedule sets of all the indistinguishable transaction systems in \(I\). Otherwise the scheduler might end up allowing an incorrect schedule to be executed as is, which would lead to an inconsistent state. Thus, this upper bound on \(P\) indictates, that the coarser the level of information that is available to the scheduler, the less efficient the scheduler can be.</p>

<p>As a corollary, an optimal scheduler (in regards to the size of the fixpoint set as a metric of performance), has:</p>

\[P = \bigcap_{(T' \in I)} C(T')\]

<p>Using the above definitions of levels of information and performance, we can partially order schedulers based on sophistication (a scheduler \(S\) is more sophisticated than \(S'\), if it uses finer information for its decisions \(I \subseteq I'\)) and we can also partially order them in respect to performance (\(S\) performs better than \(S'\), if \(P' \subseteq P\)).</p>

<p>Then, the mapping from a level of information \(I\) to the fixpoint set of the optimal scheduler for \(I\) captures the trade-off between scheduler information and performance. As stated above, the more information the scheduler has, the better performance it can have.</p>

<h2 id="optimal-schedulers">Optimal Schedulers</h2>

<p>We have now arrived to the main section of the paper, which contains results about optimal schedulers for several standard levels of information.</p>

<h3 id="maximum-and-minimum-information-optimal-schedulers">Maximum and Minimum Information Optimal Schedulers</h3>

<p>The optimal <em>maximum information</em> scheduler knows all the information about the transaction system \(I = \{T\}\) and because of that \(P = C(T)\).</p>

<p>The optimal <em>minimum information</em> scheduler produces only serial schedules, that is schedules where each transaction is executed after another finishes and only one transaction is executed at each time. Those schedules are correct by definition as performing a transaction on a consistent state, returns a consistent state.</p>

<h3 id="optimal-schedulers-for-complete-syntactic-information">Optimal Schedulers for Complete Syntactic Information</h3>

<p>Suppose that all syntactic information about \(T\) is available, so \(I\) is the set of all transaction systems with the same syntax. A schedule \(h\) is <em>serializable</em> if its execution results (the values of the variables in the final state of the system) are the same as the execution results of some serial schedule under Herbrand semantics<a class="footnote" href="#fn-2"><sup>2</sup></a>
<span class="footnoteText">Herbrand semantics is a way to interpret syntactic constructs without losing any information. A function call is interpreted as the name of the function being called and the names of the parameters passed to it. Intuitively it captures the history of the values of the variables during the computation. For example, the value of \(z\) after the end of transaction \(t_1 := f_1(x); z := f_2(y, t_1)\) under Herbrand semantics is \(f_2(y, f_1(x))\).</span>
[<a href="#manna-1974">Manna 1974</a>].</p>

<p>A serializable schedule is one that cannot be distinguished (by looking at the execution results) by a serial schedule (which is correct by definition). Based on the Herbrand semantics, two final results are equal, when exactly the same function calls and arguments where used to compute them. So “same execution results” means syntactic equality of the expressions used to compute a variable.</p>

<p>Let’s denote the set of all serializable histories of \(T\) as \(SR(T)\). A <em>serialization scheduler</em> is defined to be a scheduler which satisfies:</p>

\[\forall T, P = SR(T) \wedge S(H) = P\]

<p>So it is a scheduler that allows all serializable schedules to pass without an issue, and maps every other schedule to a serializable one. A serialization scheduler is “faster” compared to a serial scheduler, in the sense that its fixpoint set contains all serializable schedules (which are a superset of serial schedules). It ensures correctness for any history \(h\) by reordering it to a serial schedule which has the same execution results (under Herbrand semantics) and is correct by definition.</p>

<p>The serialization scheduler intuitively looks well-performing when only given syntactic information. Well now comes the main theorem of the paper and its very elegant proof.</p>

<blockquote>
  <p><strong>Theorem 2:</strong> 
The serialization scheduler is correct, and is optimal(!) among all schedulers using complete syntactic information.</p>
</blockquote>

<p><strong>Proof:</strong></p>

<p>Let’s first focus on correctness, so the fact that the scheduler always produces a correct schedule:</p>

\[\forall T' \in I, SR(T') \subseteq C(T')\]

<p>To prove the above, they use Herbrand’s theorem [<a href="#manna-1974">Manna 1974</a>] which states that if two sequences of steps are equivalent under the Herbrand interpretation, they are equivalent under any interpretation<a class="footnote" href="#fn-3"><sup>3</sup></a>.
<span class="footnoteText">Of course this assumes that functions are pure and don’t produce any side effect.</span> 
Thus for every \(h \in SR(T')\) there exists a serial schedule which has the same execution results with \(h\). As we know that all serial schedules are correct, the above implies that every \(h \in SR(T')\) is also correct.</p>

<p>To prove optimality, they show that for any history \(h \notin SR(T)\) there exists a transaction system \(T' \in I\) such that \(h \notin C(T')\). Because the information that the scheduler has, does not allow it to distinguish between the transaction systems is \(I\), if the scheduler produces a non serializable schedule \(h\), then an adversary could force the scheduler to produce this incorrect schedule by giving it to execute the transaction system \(T'\).</p>

<p>Based on the above, given a history \(h \notin SR(T)\), they want to define a transaction system \(T' \in I(T)\) with very strict integrity constraints, such that \(h \notin C(T')\). Let the semantics of \(T'\) be the Herbrand interpretation. Now given the initial values of the global variables \((v_1, v_2, ..., v_k)\), the values \((a1, a2, ..., ak) \in IC\) if and only if there exists a possibly empty sequence \(S\) of steps that is a concatenation of <em>serial executions</em> of transactions such that the initial values of the global variables are transformed by \(S\) to \((a1, a2, ..., ak)\). By this definition, all transactions are individually correct, and the basic assumption holds. Now it is easy to see that, if \(h\) is any history, that doesn’t belong to \(SR(T')\), then it transforms the initial values \((v1, ..., vk)\) to values that are not in \(IC\), so \(h \notin C(T')\). \(\square\)</p>

<p>To get a better intuition of the construction of \(T'\) in the proof, here follows an example (inspired by <em>Figure 1</em> in section 4.3 of the paper) of such a transaction system \(T'\).</p>

<p>Suppose \(T'\) is a system with one global variable \(x\) with initial value \(x_0\), and two transactions:</p>

\[T_1: x := f_1(x); x := f_2(x) \\
T_2: x := f_3(x)\]

<p>The set of acceptable final states \(IC = \{ x_0, x_1, x_2, x_{12}, x_{21} \}\) 
where \(x_1\) is the value of \(x\) after executing \(T_1\), so \(x_1 = f_2(f_1(x_0))\) (similarly \(x_2 = f_3(x_0)\)) and \(x_{12}\) is the value of \(x\) after executing \(T_1; T_2\), so \(x_1 = f_3(x_1)\) (similarly \(x_{21} = f_2(f_1(x_2))\)).</p>

<p>Note that for this specific transaction system, the set of serializable schedules is the set of serial schedules, as there is no way to reorder any non serial schedule to give the same results (under Herbarnd semantics) with any serial schedule.</p>

<p>The above theorem shows that given complete syntactic information of the transactions, one can hope to create a scheduler whose fixpoint set \(P\) is at best equal to \(SR(T)\), because anything more than that wouldn’t be correct for some interpretation and integrity constraints. That is why most approaches to concurrency control (at the time) had serialization as their objective.</p>

<p>In practice, we cannot even expect to get a scheduler with \(P = SR(T)\), as in [<a href="#papadimitriou-1978">Papadimitriou 1978</a>] it is shown that serialization is intractable, and for some transaction systems of restricted syntax, it can be <em>approximated</em> by more restrictive schedulers.</p>

<h3 id="optimal-schedulers-for-complete-semantic-information-without-integrity-constraints">Optimal Schedulers for Complete Semantic Information, without Integrity Constraints</h3>

<p>In the example above, the history \(h = T_{11}; T_{21}; T_{12}\) is not serializable since its final value (under Herbrand semantics) does not satisfy the integrity constraints. However, given an interpretation of the functions as \(f_1(x) = x + 1\), \(f_2(x) = 2 * x\), and \(f_3(x) = 3 * x\) the history \(T_{11}; T_{21}; T_{12}\) returns the same value for \(x\) as \(T1;T2\). This shows, that given semantic information, a serialization scheduler, is not optimal, and we can do better.</p>

<p>Therefore, they define a generalized notion of serialization as follows:</p>

<p>A schedule \(h\) is said to be <em>weakly serializable</em><a class="footnote" href="#fn-4"><sup>4</sup></a>,
<span class="footnoteText">The name <em>weak serialization</em> is (in my opinion) not the best way to define this generalized notion of serializability, because it doesn’t indicate anything about its difference with serialization (except that it is weaker).</span> if starting from any state \(E\), the execution of the schedule will end with a state that is achievable by some concatenation of transactions also starting from state \(E\). This way the fixpoint set \(P\) is extended to be the set of schedules that lead to a semantically equivalent final state, instead of a syntactically equivalent one (which is semantic equivalence under Herbrand interpretation).</p>

<p>Denote by \(WSR(T)\) the set of all weakly serializable schedules of \(T\). It is clear that \(SR(T) \subseteq WSR(T)\). The weak serialization scheduler \(S\) is defined so that it satisfies:</p>

\[\forall T, P = WSR(T) \wedge S(H) = P\]

<p>Similarly to the above theorem, it holds that the weak serialization scheduler is optimal among all schedulers using all information (syntactic and semantic) but the integrity constraints.</p>

<p>Note that they don’t propose an optimal scheduler that also has access to the integrity constraints.</p>

<h2 id="discussion">Discussion</h2>

<p>An important question is the relation of the above results to the real performance of a database using a specific scheduler. As the database is used by clients, a reasonable performance metric is the execution time of a transaction from the perspective of the clients. This can be divided into three parts:</p>

<ul>
  <li>
    <p><em>Scheduling time:</em> This is the time that it takes for the scheduler to make its decision. It mostly depends on the sophistication of the scheduler and the amount of information that it has available (and uses).</p>
  </li>
  <li>
    <p><em>Waiting time:</em> The total time that the scheduler delayed executing each transaction step of a transaction, so that it could preserve consistency.</p>
  </li>
  <li>
    <p><em>Execution time:</em> The time that is actually spent in executing each step of the transaction.</p>
  </li>
</ul>

<p>In this paper, they mostly focus on the waiting time, and they argue that the fixpoint set \(P\) is a good metric of the waiting time as described <a href="#scheduler">above</a>.</p>

<p>Another important issue that needs to be noted is that there is an implicit assumption underlying the model that all information is available to the scheduler from the start. This implies that the transactions to be executed are fixed and known by the scheduler statically. However, that is not a reasonable assumption as in practice the scheduler would acquire most information (especially the knowledge about which transactions are to be executed) dynamically as the clients make requests. They pinpoint this issue, and suggest it as future work.</p>

<p>Before closing this post, I would like to briefly touch on the following issue. There is often a confusion of the connection between correctness criteria for data objects (such as linearizability, sequential consistency, serializability) as these notions are very subtle. At first sight, it seems like linearizability and serializability are very similar notions (their names also mislead to this conclusion). However, this is not the case and I will try to clarify this confusion here.</p>

<p>Linearizability is used as a correctness criterion of a concurrent data object implementation against a sequential specification \(Spec\). An execution \(e\) of a concurrent system is linearizable (to \(Spec\) if there exists an execution \(e'\), such that \(e'\) can be produced by \(Spec\) and if \(op_1\) was completed before \(op_2\) in \(e\), then \(op_1\) completes before \(op_2\) in \(e'\) (in other words, \(e'\) contains the same operations as \(e\) with possibly some reordering of overlapping operations). An implementation of a data object is linearizable to \(Spec\) if all executions that it produces are linearizable to \(Spec\). Intuitively, linearizability means that a concurrent implementation behaves (from an observer’s perspective [<a href="#filipovic-2010">Filipovic et al. 2010</a>]) as a sequential specification<a class="footnote" href="#fn-5"><sup>5</sup></a>.
<span class="footnoteText">Linearizability and other correctness criteria for concurrent data objects are very interesting topics that deserve many blogposts, so I won’t get into many details here. However, I plan to make a few posts related to these topics in the future.</span></p>

<p>In contrast to the above, this work approaches the issue of correctness from a different viewpoint. Assuming that there exists a correctness specification of the system (defined based on the semantics and the integrity constraints) under which a set of schedules \(O\) of the transaction system are correct, the goal is to design a scheduler with the largest subset of those schedules (ideally all of them) as its fixpoint set \(P\), which reorders any other schedule to its “closest” reordering in \(P\). The intuition is that the larger \(P\) is, the more schedules are executed without any waiting (while preserving consistency of the database) and therefore the better the performance of the scheduler is. Specifically in this work, they study upper bounds on the fixpoint sets for several different levels of information available to the scheduler. The set of all serializable schedules for example, is the largest subset of schedules that a scheduler can hope to have in its fixpoint set if it only has syntactic information about the transaction system.</p>

<hr />

<h3 id="bibliography">Bibliography</h3>

<p><a name="bernstein-1978">[Bernstein et al. 1978]</a>: P.A. Bernstein , N. Goodman, J.B. Rothnie, and C.H. Papadimitriou. 1978. A System of Distributed Databases (the Fully Redundant Case).</p>

<p><a name="filipovic-2010">[Filipovic et al. 2010]</a>: I. Filipovic, P. O’Hearn, N. Rinetzky, and H. Yang. 2010. Abstraction for Concurrent Objects.</p>

<p><a name="kung-papadimitriou-1978">[Kung &amp; Papadimitriou 1978]</a>: H.T. Kung and C.H. Papadimitriou. 1978. An optimality theory of concurrency control for databases.</p>

<p><a name="manna-1974">[Manna 1974]</a>: Z. Manna. 1974. Mathematical Theory of Computation</p>

<p><a name="papadimitriou-1978">[Papadimitriou 1978]</a>: C.H. Papadimitriou. 1978. Serializability of Concurrent Updates.</p>]]></content><author><name></name></author><category term="serialization" /><category term="concurrency" /><category term="weak-serialization" /><category term="scheduling" /><category term="paper" /><category term="linearizability" /><summary type="html"><![CDATA[This blog post is an intro to a (potentially long) series of posts that revolve around correctness criteria of concurrent objects and systems, most notably linearizability, sequential consistency, serializability, etc. My objective is to write these blog posts to help me better understand the above concepts, because I have found out that writting helps me organize my thoughts on a topic, and as a consequence, understand it more deeply.]]></summary></entry><entry><title type="html">ICFP Programming Contest 2018</title><link href="https://angelhof.github.io/2018/10/23/icfp2018_programming_competition.html" rel="alternate" type="text/html" title="ICFP Programming Contest 2018" /><published>2018-10-23T00:00:00+00:00</published><updated>2018-10-23T00:00:00+00:00</updated><id>https://angelhof.github.io/2018/10/23/icfp2018_programming_competition</id><content type="html" xml:base="https://angelhof.github.io/2018/10/23/icfp2018_programming_competition.html"><![CDATA[<p>In July, I participated together with a friend of mine, <a href="https://github.com/abenetopoulos">Achilles Benetopoulos</a>, in the ICFP programming contest. Our team name was <a href="https://icfpcontest2018.github.io/lgtn/final-standings.html">“no need for a type system”</a> because we implemented our solution in Erlang (which has a type system, though not a static one) and at the time (after programming for “a couple” of hours straight) it seemed like a very entertaining name. On the lightning round we managed to rank on the 10th place (which was surprisingly higher than our expectations).</p>

<p><img src="/posts_files/icfp2018-final-rank.png" alt="Our final rank" /></p>

<p>In this article, I will try to describe the experience of my team  during the lightning round of the ICFP 2018 programming contest, the solution that we submitted, and general ideas and notes that came up during and after the contest. Our solution is certainly not the best for the specific problem, but I believe that there is benefit in documenting the whole process.</p>

<p>The full code of our submission can be found at this <a href="https://github.com/abenetopoulos/icfp2018">repo</a>.</p>

<h2 id="lightning-round-problem-overview">Lightning round problem overview</h2>

<p>Here is a brief overview of the problem. We have a fleet of 3D printing nanobots that operate in time steps. Each nanobot can either move, wait, spawn another nanobot, or print a voxel in a 3D space, and they are each commanded by a sequence of instructions. However, the nanobots need energy to be operated, and their energy consumption depends on many factors, such as the number of time steps that they are operating for. We are given a 3D sculpture, whose size is bounded by <code class="language-plaintext highlighter-rouge">R</code> in each dimension (<code class="language-plaintext highlighter-rouge">R</code> ranges from 1 to 250), and we want to generate a sequence of instructions for each nanobot, that minimizes the total energy that is consumed.</p>

<p>The detailed problem description can be found <a href="https://icfpcontest2018.github.io/lgtn/task-description.html">here</a> for anyone that is interested.</p>

<h3 id="system-frequency">System frequency</h3>

<p>Before describing the different nanobot commands, it is essential to state that the 3D printing nanobot system can be in one of two states at any time, low or high frequency. The difference between those states in practice is whether “floating” voxels can be 3D printed or not. If the system is in “low-frequency” only voxels in contact with grounded voxels can be printed. Otherwise, voxels can be printed anywhere, even if they are not in contact with grounded voxels. Grounded voxels are recursively defined as voxels which are in contact with the ground level (Their y-coordinate is zero) or are in contact with a grounded voxel.</p>

<h3 id="nanobot-commands">Nanobot commands</h3>

<p>There are 8 nanobot commands supported by the system.</p>

<ul>
  <li>
    <p><strong>Halt:</strong> This command is used to terminate the execution, when there is only one nanobot left at coordinates <code class="language-plaintext highlighter-rouge">(0,0,0)</code>.</p>
  </li>
  <li>
    <p><strong>Wait:</strong> When a nanobot executes this command it just spends its turn without doing anything.</p>
  </li>
  <li>
    <p><strong>Flip:</strong> This command is used to change frequency from high to low, or vice versa.</p>
  </li>
  <li>
    <p><strong>SMove lld:</strong> This command is used to move a nanobot straight up to 15 voxels away. The direction and length of the move is given by.</p>
  </li>
  <li>
    <p><strong>LMove sld1 sld2:</strong> This command is used to move a nanobot in an L move up to 5 voxels in two directions. The direction and length of each of the two parts of the L move are given by sld1 and sld2.</p>
  </li>
  <li>
    <p><strong>Fission nd:</strong> This command is used to spawn a new nanobot next to the current nanobot in the direction that is given by nd.</p>
  </li>
  <li>
    <p><strong>Fill nd:</strong> This command is used to print a voxel next to the current nanobot in the direction that is given by nd.</p>
  </li>
  <li>
    <p><strong>Fusion nd:</strong> This command must be performed at the same turn by two nanobots that are next to each other. When it is performed, those two nanobots merge back into one.</p>
  </li>
</ul>

<h3 id="energy-costs">Energy costs</h3>

<p>There are five ways in which energy is consumed during the execution of the system.</p>

<ul>
  <li>Each time step that the system is active costs <code class="language-plaintext highlighter-rouge">3*R*R*R</code> energy units when being in “low frequency” and <code class="language-plaintext highlighter-rouge">30*R*R*R</code> when being in “high frequency”.</li>
  <li>Each active nanobot consumes <code class="language-plaintext highlighter-rouge">20</code> energy units each time step.</li>
  <li>Each move command consumes double the manhattan distance it covered in energy units.</li>
  <li>Printing a voxel costs <code class="language-plaintext highlighter-rouge">12</code> energy units.</li>
  <li>Spawning a new nanobot costs <code class="language-plaintext highlighter-rouge">24</code> energy.</li>
</ul>

<p>The energy cost associated with printing a voxel cannot be reduced, as all voxels of the sculpture must be printed. In addition the energy cost of spawning new nanobots is negligible because combining two nanobots (with the <code class="language-plaintext highlighter-rouge">fusion</code> command) returns that energy to the system.</p>

<p>Thus the main costs are the first three, with the first one dominating when <code class="language-plaintext highlighter-rouge">R</code> is large enough. If we have <code class="language-plaintext highlighter-rouge">20</code> nanobots, which is the maximum possible number of nanobots, all making the longest possible move at the same time step, the amount of energy that will be consumed is <code class="language-plaintext highlighter-rouge">1000</code> which is less than the total system energy consumption for one time step even when <code class="language-plaintext highlighter-rouge">R &gt;= 10</code>. In addition, having the system in “low frequency” seemingly saves a lot of energy, but constrains parallelism a lot, as being in low frequency requires that voxels are printedfrom the ground up.</p>

<p>Because of the above, we decided to focus on minimizing the total number of time steps that the system runs, without initially caring about keeping the system in “low-frequency”.</p>

<h2 id="first-naive-solution-to-the-problem">First naive solution to the problem</h2>

<p>Instead of just giving our solution to the problem, I will first try to describe our approach and how this led us to our first naive solution.</p>

<p>The solution that the problem requires is analogous to a compiler which transforms a high level specification (the target 3D sculpture) to a program, that when executed, produces this 3D structure.</p>

<p>Having this view, we first tried to implement the simplest (and therefore completely inefficient) correct program that produces this 3D sculpture. That is, a single nanobot that “scans” the entire 3D space voxel by voxel, and prints the target voxels. Our nanobot traverses space from the lowest to the highest xz plane, and in each plane from the left to the right line by line. Every time it finishes with a line, it moves to the beginning of the next line and starts again. Each time it finishes with a whole plane, it goes to the beginning of the next plane and starts again.</p>

<p>Of course this solution is completely inefficient, however it acted as a great foundation, to incrementally add optimization passes to the generated programs, thus improving their efficiency.</p>

<p>There are two main optimization directions, one is improving the sequential performance of each nanobot, and the other is utilizing more nanobots to parallelize the computation, as the largest energy overhead is caused by the number of rounds that the program is executed. A separate section is dedicated to each optimization direction.</p>

<h2 id="sequential-optimizations">Sequential Optimizations</h2>

<p>In theory, an optimal sequential strategy would visit the voxels that a nanobot needs to print with the goal of minimizing the movement (both in distance and in rounds) that the nanobot needs to do. Well this problem sounds familiar (<a href="https://en.wikipedia.org/wiki/Travelling_salesman_problem">TSP</a>) and we decided that it was not a feasible solution to the problem. At that time we also thought that a solution like this could introduce a lot of crashes of a robot with the already produced voxels<a class="footnote" href="#fn-1"><sup>1</sup></a>.<span class="footnoteText">In retrospect, it seems like if a nanobot is going to crash to a voxel at round <code class="language-plaintext highlighter-rouge">r2</code> that was already produced by itself at round <code class="language-plaintext highlighter-rouge">r1</code>, it means that it could have delayed printing this voxel until the round <code class="language-plaintext highlighter-rouge">r2</code> as it would pass from there a second time. Thus if a nanobot prints all voxels at the latest possible time, then it will never crash with voxels created by itself. However this does not hold when many robots are printing voxels concurrently.</span> Based on the above, we decided to go for a greedier approach.</p>

<p>As I also mentioned before, in our naive solution a nanobot traverses the whole 3D space and for every voxel that it passes, it checks whether it should print it or not as follows:</p>

<div class="language-erlang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">,</span> <span class="nv">Model</span><span class="p">)</span> <span class="k">when</span> <span class="nv">Z</span> <span class="o">=:=</span> <span class="nv">R</span> <span class="o">-&gt;</span>
  <span class="p">[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}]}]</span> <span class="o">++</span> <span class="nf">return</span><span class="p">(</span><span class="n">z</span><span class="p">,</span> <span class="nv">R</span><span class="p">,</span> <span class="nv">Z</span><span class="p">)</span> <span class="o">++</span> 
  <span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="nv">X</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">Model</span><span class="p">);</span>
<span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">,</span> <span class="nv">Model</span><span class="p">)</span> <span class="k">when</span> <span class="nv">X</span> <span class="o">=:=</span> <span class="nv">R</span> <span class="o">-&gt;</span>
  <span class="p">[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">}]}]</span> <span class="o">++</span> <span class="nf">return</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nv">R</span><span class="p">,</span> <span class="nv">X</span><span class="p">)</span> <span class="o">++</span> 
  <span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Z</span><span class="p">,</span> <span class="nv">Model</span><span class="p">);</span>
<span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">,</span> <span class="nv">Model</span><span class="p">)</span> <span class="k">when</span> <span class="nv">Y</span> <span class="o">=:=</span> <span class="nv">R</span> <span class="o">-&gt;</span>
  <span class="nf">return</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="nv">R</span><span class="p">,</span> <span class="nv">Y</span><span class="p">)</span> <span class="o">++</span> <span class="p">[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">}]}];</span>
<span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">,</span> <span class="nv">Model</span><span class="p">)</span> <span class="o">-&gt;</span>
  <span class="nv">Fill</span> <span class="o">=</span>
    <span class="k">case</span> <span class="nf">nth</span><span class="p">(</span><span class="nv">Z</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nf">nth</span><span class="p">(</span><span class="nv">Y</span><span class="p">,</span> <span class="nf">nth</span><span class="p">(</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Model</span><span class="p">)))</span> <span class="k">of</span>
      <span class="mi">0</span> <span class="o">-&gt;</span> <span class="p">[];</span>
      <span class="mi">1</span> <span class="o">-&gt;</span> <span class="p">[{</span><span class="n">fill</span><span class="p">,</span> <span class="p">[{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">}]}]</span>
    <span class="k">end</span><span class="p">,</span>
  <span class="nv">Move</span> <span class="o">=</span> <span class="p">[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">}]}],</span>
  <span class="nv">Fill</span> <span class="o">++</span> <span class="nv">Move</span> <span class="o">++</span> <span class="nf">print_voxel</span><span class="p">(</span><span class="nv">R</span><span class="p">,</span> <span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="nv">Model</span><span class="p">).</span>
</code></pre></div></div>

<p>The nanobot moves on a z-axis line, until it reaches the end of the space, where it moves to the next line (by incrementing x) and then moves back to the start of the z-axis (with the <code class="language-plaintext highlighter-rouge">return(z, R, Z)</code> call). When it reached the end of the x-axis, it moves one plane up (by incrementing y), and it goes to the start of the x and z axes.</p>

<p>Based on that naive solution, the first optimization that we performed was to shorten the path of each nanobot, by implementing a back and forth movement, instead of having to return to the start of the z-axis everytime the nanobot reached the end of the line. The new <code class="language-plaintext highlighter-rouge">print_voxel</code> looks like this:</p>

<div class="language-erlang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="o">=</span><span class="p">{_,_,</span><span class="nv">MinZ</span><span class="p">},</span> <span class="nv">Max</span><span class="o">=</span><span class="p">{_,_,</span><span class="nv">MaxZ</span><span class="p">},</span> <span class="nv">Curr</span><span class="o">=</span><span class="p">{</span><span class="nv">X</span><span class="p">,</span><span class="nv">Y</span><span class="p">,</span><span class="nv">Z</span><span class="p">},</span> <span class="n">plus</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">Acc</span><span class="p">)</span> <span class="k">when</span> <span class="nv">Z</span> <span class="o">&gt;</span> <span class="nv">MaxZ</span> <span class="o">-&gt;</span>
  <span class="nv">NewAcc</span> <span class="o">=</span> <span class="p">[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}]}|</span><span class="nv">Acc</span><span class="p">],</span>
  <span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="p">,</span> <span class="nv">Max</span><span class="p">,</span> <span class="p">{</span><span class="nv">X</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">},</span> <span class="n">minus</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">NewAcc</span><span class="p">);</span>
<span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="o">=</span><span class="p">{_,_,</span><span class="nv">MinZ</span><span class="p">},</span> <span class="nv">Max</span><span class="o">=</span><span class="p">{_,_,</span><span class="nv">MaxZ</span><span class="p">},</span> <span class="nv">Curr</span><span class="o">=</span><span class="p">{</span><span class="nv">X</span><span class="p">,</span><span class="nv">Y</span><span class="p">,</span><span class="nv">Z</span><span class="p">},</span> <span class="n">minus</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">Acc</span><span class="p">)</span> <span class="k">when</span> <span class="nv">Z</span> <span class="o">&lt;</span> <span class="nv">MinZ</span> <span class="o">-&gt;</span>
  <span class="nv">NewAcc</span> <span class="o">=</span> <span class="p">[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}]}|</span><span class="nv">Acc</span><span class="p">],</span>
  <span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="p">,</span> <span class="nv">Max</span><span class="p">,</span> <span class="p">{</span><span class="nv">X</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">},</span> <span class="n">plus</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">NewAcc</span><span class="p">);</span>
<span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="o">=</span><span class="p">{</span><span class="nv">MinX</span><span class="p">,_,</span><span class="nv">MinZ</span><span class="p">},</span> <span class="nv">Max</span><span class="o">=</span><span class="p">{</span><span class="nv">MaxX</span><span class="p">,_,</span><span class="nv">MaxZ</span><span class="p">},</span> <span class="nv">Curr</span><span class="o">=</span><span class="p">{</span><span class="nv">X</span><span class="p">,</span><span class="nv">Y</span><span class="p">,</span><span class="nv">Z</span><span class="p">},</span> <span class="nv">Direction</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">Acc</span><span class="p">)</span> <span class="k">when</span> <span class="nv">X</span> <span class="o">&gt;</span> <span class="nv">MaxX</span> <span class="o">-&gt;</span>
  <span class="nv">MoveToStart</span> <span class="o">=</span> 
    <span class="nn">lists</span><span class="p">:</span><span class="nf">flatten</span><span class="p">([</span><span class="nf">move_robot</span><span class="p">({</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">},</span> <span class="p">{</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">MinZ</span> <span class="o">-</span> <span class="mi">1</span><span class="p">}),</span> 
                   <span class="nf">move_robot</span><span class="p">({</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">MinZ</span> <span class="o">-</span> <span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="nv">MinX</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">MinZ</span> <span class="o">-</span> <span class="mi">1</span><span class="p">})]),</span>
  <span class="p">{</span><span class="nn">lists</span><span class="p">:</span><span class="nf">reverse</span><span class="p">(</span><span class="nv">Acc</span><span class="p">)</span> <span class="o">++</span> <span class="nv">MoveToStart</span><span class="p">,</span> <span class="p">{</span><span class="nv">MinX</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="nv">Y</span><span class="p">,</span><span class="nv">MinZ</span><span class="o">-</span><span class="mi">1</span><span class="p">}};</span>
<span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="p">,</span> <span class="nv">Max</span><span class="p">,</span> <span class="p">{</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">Z</span><span class="p">},</span> <span class="nv">Direction</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">Acc</span><span class="p">)</span> <span class="o">-&gt;</span>
  <span class="p">{</span><span class="nv">Move</span><span class="p">,</span> <span class="nv">NewZ</span><span class="p">}</span> <span class="o">=</span> 
    <span class="k">case</span> <span class="nv">Direction</span> <span class="k">of</span>
      <span class="n">plus</span> <span class="o">-&gt;</span> <span class="p">{[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">}]}],</span> <span class="nv">Z</span> <span class="o">+</span> <span class="mi">1</span><span class="p">};</span>
      <span class="n">minus</span> <span class="o">-&gt;</span> <span class="p">{[{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">}]}],</span> <span class="nv">Z</span> <span class="o">-</span> <span class="mi">1</span><span class="p">}</span>
    <span class="k">end</span><span class="p">,</span>
  <span class="p">{</span><span class="nv">Lookup</span><span class="p">,</span> <span class="nv">ToFill</span><span class="p">}</span> <span class="o">=</span> 
    <span class="k">case</span> <span class="nv">Direction</span> <span class="k">of</span>
      <span class="n">plus</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="p">{</span><span class="nv">X</span><span class="p">,</span><span class="nv">Y</span><span class="p">,</span><span class="nv">NewZ</span> <span class="o">-</span> <span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">}};</span>
      <span class="n">minus</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="p">{</span><span class="nv">X</span><span class="p">,</span><span class="nv">Y</span><span class="p">,</span><span class="nv">NewZ</span> <span class="o">+</span> <span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">}}</span>
    <span class="k">end</span><span class="p">,</span>
  <span class="nv">Fill</span> <span class="o">=</span>
    <span class="k">case</span> <span class="nf">model_get</span><span class="p">(</span><span class="nv">Lookup</span><span class="p">,</span> <span class="nv">Model</span><span class="p">)</span> <span class="k">of</span>
      <span class="mi">0</span> <span class="o">-&gt;</span> <span class="p">[];</span>
      <span class="mi">1</span> <span class="o">-&gt;</span> <span class="p">[{</span><span class="n">fill</span><span class="p">,</span> <span class="p">[</span><span class="nv">ToFill</span><span class="p">]}]</span>
    <span class="k">end</span><span class="p">,</span>
  <span class="nv">NewAcc</span> <span class="o">=</span> <span class="nv">Fill</span> <span class="o">++</span> <span class="nv">Move</span> <span class="o">++</span> <span class="nv">Acc</span><span class="p">,</span>
  <span class="nf">print_voxel</span><span class="p">(</span><span class="nv">Min</span><span class="p">,</span> <span class="nv">Max</span><span class="p">,</span> <span class="p">{</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="nv">NewZ</span><span class="p">},</span> <span class="nv">Direction</span><span class="p">,</span> <span class="nv">Model</span><span class="p">,</span> <span class="nv">NewAcc</span><span class="p">).</span>
</code></pre></div></div>

<p>The second optimization that we performed was bounding the movement of a nanobot by the bounding box of the sculpture on each plane. By doing that a nanobot does not need to scan space where there is no voxel to be printed.</p>

<p>The third was the most important sequential optimization that we performed. The generated sequence of commands for a nanobot, contains a lot of unecessary 1 voxel moves, as the nanobot scans the whole space voxel by voxel. However, we are allowed to move a nanobot up to 15 voxels away with a single one axis move command, or up to 5 voxels in 2 axes. This means that a lot of energy can be conserved, by merging all those 1 voxel move instructions to longer move instructions, as the total execution time would drastically reduce.</p>

<p>This is the code that performs this optimization:</p>

<div class="language-erlang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">optimize_seq_trace</span><span class="p">(</span><span class="nv">Commands</span><span class="p">)</span> <span class="o">-&gt;</span>
  <span class="nf">optimize_seq_trace</span><span class="p">(</span><span class="nv">Commands</span><span class="p">,</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">},</span> <span class="p">[]).</span>

<span class="nf">optimize_seq_trace</span><span class="p">([],</span> <span class="nv">Buffer</span><span class="p">,</span> <span class="nv">Acc</span><span class="p">)</span> <span class="o">-&gt;</span>
  <span class="nv">FinalMoves</span> <span class="o">=</span> <span class="nf">instantiate_moves</span><span class="p">(</span><span class="nv">Buffer</span><span class="p">),</span>
  <span class="nn">lists</span><span class="p">:</span><span class="nf">reverse</span><span class="p">(</span><span class="nv">Acc</span><span class="p">)</span> <span class="o">++</span> <span class="nv">FinalMoves</span><span class="p">;</span>
<span class="nf">optimize_seq_trace</span><span class="p">([</span><span class="nv">Com</span><span class="p">|</span><span class="nv">Commands</span><span class="p">],</span> <span class="nv">Buffer</span><span class="p">,</span> <span class="nv">Acc</span><span class="p">)</span> <span class="o">-&gt;</span>
  <span class="k">case</span> <span class="nv">Com</span> <span class="k">of</span>
    <span class="p">{</span><span class="n">smove</span><span class="p">,</span> <span class="p">[</span><span class="nv">Cd</span><span class="p">]}</span> <span class="o">-&gt;</span>
      <span class="nf">optimize_seq_trace</span><span class="p">(</span><span class="nv">Commands</span><span class="p">,</span> <span class="nf">add_coords</span><span class="p">(</span><span class="nv">Cd</span><span class="p">,</span> <span class="nv">Buffer</span><span class="p">),</span> <span class="nv">Acc</span><span class="p">);</span>
    <span class="p">{</span><span class="n">lmove</span><span class="p">,</span> <span class="p">[</span><span class="nv">Cd1</span><span class="p">,</span> <span class="nv">Cd2</span><span class="p">]}</span> <span class="o">-&gt;</span>
      <span class="nf">optimize_seq_trace</span><span class="p">(</span><span class="nv">Commands</span><span class="p">,</span> <span class="nf">add_coords</span><span class="p">(</span><span class="nv">Cd2</span><span class="p">,</span> <span class="nf">add_coords</span><span class="p">(</span><span class="nv">Cd1</span><span class="p">,</span> <span class="nv">Buffer</span><span class="p">)),</span> <span class="nv">Acc</span><span class="p">);</span>
    <span class="p">_</span> <span class="o">-&gt;</span>
      <span class="nv">BufferMoves</span> <span class="o">=</span> <span class="nf">instantiate_moves</span><span class="p">(</span><span class="nv">Buffer</span><span class="p">),</span>
      <span class="nv">NewAcc</span> <span class="o">=</span> <span class="p">[</span><span class="nv">Com</span><span class="p">|</span><span class="nn">lists</span><span class="p">:</span><span class="nf">reverse</span><span class="p">(</span><span class="nv">BufferMoves</span><span class="p">)]</span> <span class="o">++</span> <span class="nv">Acc</span><span class="p">,</span>
      <span class="nf">optimize_seq_trace</span><span class="p">(</span><span class="nv">Commands</span><span class="p">,</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">},</span> <span class="nv">NewAcc</span><span class="p">)</span>
  <span class="k">end</span><span class="p">.</span>
</code></pre></div></div>

<p>It doesn’t only merge consecutive small move instructions into longer ones, but it also removes opposite move commands by collecting all of the consecutive move instructions in a buffer, and then instantiating them with the least amount of commands whenever a non move command is encountered<a class="footnote" href="#fn-2"><sup>2</sup></a>.<span class="footnoteText">Looking back, it seems like merging consecutive move commands, “includes” the bounding box optimization, as all the moves outside of the bounding box would be merged into the least possible amount of moves to reach the bounding box.</span></p>

<h2 id="parallel-optimizations">Parallel Optimizations</h2>

<p>The second optimization direction is increasing the number of nanobots, so that they can print voxels in parallel, thus reducing the total system execution time. However, this introduces new challenges as two nanobots cannot move through the same space during a time step, and nanobots can crash if they try to move through an already printed voxel. Because of that, parallelization should be guided by a strategy that avoids any interference between nanobots and voxels.</p>

<p>We tried to achieve that “orchestrated” parallelization by partitioning the space in levels based on height, and by assigning a set of those levels to each nanobot in a static fashion (later on we improved this static level assignment by assigning a level to a nanobot only after it is done with its previously assigned level, thus reducing the nanobot idle time).</p>

<p>By having the space partitioned in levels, we can still use the sequential printing algorithm and optimizations that we implemented before without any changes. Moreover, nanobots can never crash with already printed voxels or with each other when they both move horizontally, and interference is limited between two nanobots when at least one nanobot is transitioning in a vertical manner.</p>

<p>In order to eliminate this “vertical” intereference, we constrained parallelization in the following way. Each nanobot is allowed to move up and down between different levels on a vertical line that is unique to itself. This way we eliminate interference when both nanobots move vertically<a class="footnote" href="#fn-3"><sup>3</sup></a>.<span class="footnoteText">This is actually the worst type of interference, as it cannot be solved by stalling, but needs a more complicated interference elimination strategy (which gets even more complicated when more than 2 nanobots intefere at the same time step), where one nanobot has to temporarily move to the side, allowing the other nanobot to move, and then return back to its original position to proceed with its move.</span></p>

<p>Finally, we created an interference checker, which also contains an interpreter of nanobot programs, that executes all nanobot commands in parallel and checks for any possible interference between all pairs of nanobots. When an interference is detected, a <strong>Wait</strong> instruction is inserted to the vertically moving nanobots’ programs, in order to allow any interfering horizontally moving nanobots to complete their moves. It is important to note, that this simplistic collision avoidance algorithm wouldn’t work in situations where two nanobots try to move on the opposite (or the same) direction through the same line.</p>

<h2 id="discussion">Discussion</h2>

<p>In total, our solution was decent but I believe that there were some mistakes in our approach that we could learn from in the future.</p>

<p>First of all, the way we parallelized our solution felt “ultra-hacky” and not well thought out, as we never seriously considered its correctness during the contest. Our interference elimination algorithm is not general enough, and does not work for all corner cases. Consequently, during the final hours of the contest, we failed really hard when we tried to refine the parallelism strategy to a finer-grain by assigning sections of horizontal levels to each nanobot (instead of the whole level). In retrospect, the nanobot 3D printing system looks very similar to a multi-process concurrent system where processes compete in getting hold of specific resources (where processes are nanobots and resources are sections of the 3D space). Based on that, we could have searched for, and applied a much more sophisticated (and provably correct) analysis in order to eliminate interference as there exists a lot of relative work in the field of concurrent systems.</p>

<p>In addition, we shouldn’t have handled all testcases with the same algorithm, as the smaller (and less dense) sculptures could be printed with much less energy by keeping the system in “low-frequency” and only printing the closest grounded voxel in every timestep. This could have improved our score in a lot of the smaller testcases.</p>

<p>On the contrary, a final positive note is that I was surprised by how fast we managed to get the first naive implementation up and running. After reading and understanding the problem description, we agreed on the interface for the parse module, the output module, and the state representation and we set out to separately implement each part. This allowed us to completely parallelize the “dirty” initial process, and have a working <em>ultra naive</em> solution in about 3 hours after the beginning of the competition. (The following screenshot was taken when we succesfully managed to run our solution for the first time and we realised that despite being ultra naive it returned a reasonable first result.)</p>

<p><img src="/posts_files/icfp2018-early-rankings.png" alt="Our rank after about 3 hours" /></p>

<hr />]]></content><author><name></name></author><category term="ICFP" /><category term="programming contest" /><category term="Erlang" /><summary type="html"><![CDATA[In July, I participated together with a friend of mine, Achilles Benetopoulos, in the ICFP programming contest. Our team name was “no need for a type system” because we implemented our solution in Erlang (which has a type system, though not a static one) and at the time (after programming for “a couple” of hours straight) it seemed like a very entertaining name. On the lightning round we managed to rank on the 10th place (which was surprisingly higher than our expectations).]]></summary></entry><entry><title type="html">Solving Problems using Monads 1 - Reachability</title><link href="https://angelhof.github.io/2017/10/20/solvingProblemsWithMonads1reachability.html" rel="alternate" type="text/html" title="Solving Problems using Monads 1 - Reachability" /><published>2017-10-20T00:00:00+00:00</published><updated>2017-10-20T00:00:00+00:00</updated><id>https://angelhof.github.io/2017/10/20/solvingProblemsWithMonads1reachability</id><content type="html" xml:base="https://angelhof.github.io/2017/10/20/solvingProblemsWithMonads1reachability.html"><![CDATA[<p>This is part of a series of posts on solving simple programming problems using Monads. The intention of this series is to help me (and you) learn more about Monads by solving interesting (but simple) programming problems.</p>

<p>Searching for a simple problem to start the series with, I stumbled upon <a href="https://www.codewars.com/kata/53223653a191940f2b000877">this kata</a>. It contains a reachability problem for a pair of nodes in a graph.</p>

<h2 id="problem-definition">Problem Definition</h2>

<p>Given a pair of nodes \((s, e)\) and a list of all edges of a unidirectional graph \(G\), decide whether node \(e\) is reachable from node \(s\). Any linear graph traversal algorithm can be used to solve this problem in a simple and relatively efficient manner.</p>

<h2 id="solving-the-problem-without-the-use-of-monads">Solving the problem without the use of Monads</h2>

<p>We will first import the Data.Set in order to keep visited nodes in an efficient data structure.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.Set</span> <span class="k">as</span> <span class="n">S</span>
</code></pre></div></div>

<p>The only type definitions that we need are:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">type</span> <span class="kt">Node</span> <span class="o">=</span> <span class="kt">Char</span>
<span class="kr">type</span> <span class="kt">Arc</span>  <span class="o">=</span> <span class="p">(</span><span class="kt">Node</span><span class="p">,</span> <span class="kt">Node</span><span class="p">)</span>
</code></pre></div></div>

<p>The main function of our problem is:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">solveGraph</span> <span class="o">::</span> <span class="kt">Node</span> <span class="o">-&gt;</span> <span class="kt">Node</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Arc</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="n">solveGraph</span> <span class="n">s</span> <span class="n">e</span> <span class="n">arcs</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">member</span> <span class="n">e</span> <span class="n">visited</span>
  <span class="kr">where</span>
    <span class="n">visited</span> <span class="o">=</span> <span class="n">visit</span> <span class="n">arcs</span> <span class="p">[</span><span class="n">s</span><span class="p">]</span> <span class="o">$</span> <span class="kt">S</span><span class="o">.</span><span class="n">singleton</span> <span class="n">s</span>
</code></pre></div></div>

<p>The function <code class="language-plaintext highlighter-rouge">solveGraph</code> visits all nodes that are reachable from \(s\) and checks if \(e\) is one of them. So now we have to define a function <code class="language-plaintext highlighter-rouge">visit :: [Arc] -&gt; [Node] -&gt; S.Set Node -&gt; S.Set Node</code> that actually performs a Breadth First Search (or any other kind of traversal) on \(G\) starting from \(s\).</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">visit</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Arc</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Node</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">S</span><span class="o">.</span><span class="kt">Set</span> <span class="kt">Node</span> <span class="o">-&gt;</span>  <span class="kt">S</span><span class="o">.</span><span class="kt">Set</span> <span class="kt">Node</span>
<span class="n">visit</span> <span class="kr">_</span> <span class="kt">[]</span> <span class="n">visited</span> <span class="o">=</span> <span class="n">visited</span>
<span class="n">visit</span> <span class="n">arcs</span> <span class="p">(</span><span class="n">n</span><span class="o">:</span><span class="n">ns</span><span class="p">)</span> <span class="n">visited</span> <span class="o">=</span> <span class="n">visit</span> <span class="n">arcs</span> <span class="p">(</span><span class="n">ns</span> <span class="o">++</span> <span class="n">newOpen</span><span class="p">)</span> <span class="n">newVisited</span> 
  <span class="kr">where</span>
    <span class="n">es</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">fromList</span> <span class="o">$</span> <span class="n">expandOnce</span> <span class="n">arcs</span> <span class="n">n</span>
    <span class="n">newVisited</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">union</span> <span class="n">visited</span> <span class="n">es</span>
    <span class="n">newOpen</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">toList</span> <span class="o">$</span> <span class="kt">S</span><span class="o">.</span><span class="n">difference</span> <span class="n">es</span> <span class="n">visited</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">visit</code> performs the classic BFS algorithm. What is now left is to define <code class="language-plaintext highlighter-rouge">expandOnce</code>.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">expandOnce</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Arc</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Node</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Node</span><span class="p">]</span>
<span class="n">expandOnce</span> <span class="n">arcs</span> <span class="n">s</span> <span class="o">=</span> <span class="p">[</span><span class="n">b</span> <span class="o">|</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">)</span> <span class="o">&lt;-</span> <span class="n">arcs</span><span class="p">,</span> <span class="n">a</span> <span class="o">==</span> <span class="n">s</span><span class="p">]</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">expandOnce</code> given a node returns all the nodes that are one edge away from it.</p>

<p>This program works as can be seen below from the interpreter output:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Prelude</span><span class="o">&gt;</span> <span class="o">:</span><span class="n">l</span> <span class="n">main</span><span class="o">.</span><span class="n">hs</span>
<span class="p">[</span><span class="mi">1</span> <span class="kr">of</span> <span class="mi">1</span><span class="p">]</span> <span class="kt">Compiling</span> <span class="kt">Graph</span>            <span class="p">(</span> <span class="n">main</span><span class="o">.</span><span class="n">hs</span><span class="p">,</span> <span class="n">interpreted</span> <span class="p">)</span>
<span class="kt">Ok</span><span class="p">,</span> <span class="n">modules</span> <span class="n">loaded</span><span class="o">:</span> <span class="kt">Graph</span><span class="o">.</span>
<span class="o">*</span><span class="kt">Graph</span><span class="o">&gt;</span> <span class="kr">let</span> <span class="n">arcs</span> <span class="o">=</span> <span class="p">[(</span><span class="sc">'a'</span><span class="p">,</span><span class="sc">'b'</span><span class="p">),(</span><span class="sc">'b'</span><span class="p">,</span><span class="sc">'c'</span><span class="p">),(</span><span class="sc">'c'</span><span class="p">,</span><span class="sc">'a'</span><span class="p">),(</span><span class="sc">'c'</span><span class="p">,</span><span class="sc">'d'</span><span class="p">),(</span><span class="sc">'e'</span><span class="p">,</span><span class="sc">'a'</span><span class="p">)]</span>
<span class="o">*</span><span class="kt">Graph</span><span class="o">&gt;</span> <span class="n">solveGraph</span> <span class="sc">'a'</span> <span class="sc">'d'</span> <span class="n">arcs</span>
<span class="kt">True</span>
<span class="o">*</span><span class="kt">Graph</span><span class="o">&gt;</span> <span class="n">solveGraph</span> <span class="sc">'a'</span> <span class="sc">'e'</span> <span class="n">arcs</span>
<span class="kt">False</span>
</code></pre></div></div>

<h2 id="incorporating-monads-in-the-solution">Incorporating Monads in the Solution</h2>

<p>This solution is short and solves the problem, but it does not satisfy the initial requirement to use Monads. It is easy to notice that <code class="language-plaintext highlighter-rouge">visit</code> passes around the <code class="language-plaintext highlighter-rouge">visited :: Set Node</code> resembling the State monad.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">newtype</span> <span class="kt">State</span> <span class="n">s</span> <span class="n">a</span> <span class="o">=</span> <span class="kt">State</span> <span class="p">{</span> <span class="n">runState</span> <span class="o">::</span> <span class="n">s</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">s</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>

<p>The above <code class="language-plaintext highlighter-rouge">newtype</code> is provided in <code class="language-plaintext highlighter-rouge">Control.Monad.State</code> and we could easily think of <code class="language-plaintext highlighter-rouge">visited</code> as the state. So in order to not pass this around we could reimplement <code class="language-plaintext highlighter-rouge">solveGraph</code> and <code class="language-plaintext highlighter-rouge">visit</code> as follows.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">solveGraph</span> <span class="o">::</span> <span class="kt">Node</span> <span class="o">-&gt;</span> <span class="kt">Node</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Arc</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="n">solveGraph</span> <span class="n">s</span> <span class="n">e</span> <span class="n">arcs</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">member</span> <span class="n">e</span> <span class="n">visited</span>
  <span class="kr">where</span>
    <span class="n">visited</span> <span class="o">=</span> <span class="n">snd</span> <span class="o">$</span> <span class="n">runState</span> <span class="p">(</span><span class="n">visit</span> <span class="n">arcs</span> <span class="p">[</span><span class="n">s</span><span class="p">])</span> <span class="o">$</span> <span class="kt">S</span><span class="o">.</span><span class="n">singleton</span> <span class="n">s</span>

<span class="n">visit</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Arc</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Node</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">State</span> <span class="p">(</span><span class="kt">S</span><span class="o">.</span><span class="kt">Set</span> <span class="kt">Node</span><span class="p">)</span> <span class="p">[</span><span class="kt">Node</span><span class="p">]</span>
<span class="n">visit</span> <span class="kr">_</span> <span class="kt">[]</span> <span class="o">=</span> <span class="n">return</span> <span class="kt">[]</span>
<span class="n">visit</span> <span class="n">arcs</span> <span class="p">(</span><span class="n">n</span><span class="o">:</span><span class="n">ns</span><span class="p">)</span> <span class="o">=</span> <span class="kr">do</span>
  <span class="n">visited</span> <span class="o">&lt;-</span> <span class="n">get</span>
  <span class="kr">let</span> <span class="n">es</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">fromList</span> <span class="o">$</span> <span class="n">expandOnce</span> <span class="n">arcs</span> <span class="n">n</span>
  <span class="kr">let</span> <span class="n">newVisited</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">union</span> <span class="n">visited</span> <span class="n">es</span>
  <span class="kr">let</span> <span class="n">newBoundary</span> <span class="o">=</span> <span class="kt">S</span><span class="o">.</span><span class="n">toList</span> <span class="o">$</span> <span class="kt">S</span><span class="o">.</span><span class="n">difference</span> <span class="n">es</span> <span class="n">visited</span>
  <span class="n">put</span> <span class="n">newVisited</span>
  <span class="n">visit</span> <span class="n">arcs</span> <span class="o">$</span> <span class="n">ns</span> <span class="o">++</span> <span class="n">newBoundary</span>
</code></pre></div></div>

<p>This implementation is by no means shorter than the other one, however it is now clear that the Set of visited nodes is used as state. If the program was much longer and more complicated, not having to pass the state around as an argument would significantly improve the code’s readability.</p>

<h2 id="conclusion">Conclusion</h2>

<p>We tried to solve a simple programming problem using the help of Monads and we did it. However the resulting code is not more elegant than the original one and so we did not manage to showcase the greatness of the <code class="language-plaintext highlighter-rouge">State</code> Monad. In the next part of the series I hope that I can find a problem whose solution is greatly improved when using Monads.</p>

<p>Here is a <a href="https://github.com/angelhof/codewars-solutions/blob/master/graphExistsPath/main.hs">link</a> for the complete code in this post.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This is part of a series of posts on solving simple programming problems using Monads. The intention of this series is to help me (and you) learn more about Monads by solving interesting (but simple) programming problems.]]></summary></entry><entry><title type="html">Thinking back on Google Summer of Code 2017</title><link href="https://angelhof.github.io/2017/10/18/gsoc2017.html" rel="alternate" type="text/html" title="Thinking back on Google Summer of Code 2017" /><published>2017-10-18T00:00:00+00:00</published><updated>2017-10-18T00:00:00+00:00</updated><id>https://angelhof.github.io/2017/10/18/gsoc2017</id><content type="html" xml:base="https://angelhof.github.io/2017/10/18/gsoc2017.html"><![CDATA[<p>It has been one and a half month since GSoC 2017 ended and I would like to note how amazing the whole experience was. I learned a lot about the specific subjects that I dealt with (ACME protocol, X.509 certificates), as well as generally about software engineering and cooperating with other people. As enough time has passed from the completion of the project I think that I can now epitomize what I really kept from the whole experience.</p>

<h3 id="plan-ahead">Plan Ahead</h3>

<p>Always create an initial design before diving in the implementation. It seems difficult to do it at first, but when you get used to it, it really facilitates implementation. However it is also very important to change the design and not stay stuck to it when you realise it has flaws (more often than not it will have). Ideally you will be transitioning between a design and an implementation phase in a continuous feedback loop.</p>

<h3 id="prototype">Prototype</h3>

<p>It is very important to experiment with the implementation and create a first (or even a second, third …) prototype. This is an essential part of the procedure because by building the prototype you have the time to explore all the capabilities and flaws of the design while also getting a better grasp of the task at hand. Creating a prototype also helps to better understand what are the exact requirements in order to finish the goal correctly and efficiently. Building a prototype might sound like wasted time when you have to complete a huge project. However the certainty and experience you get from creating it far outweighs the “wasted” time.</p>

<h3 id="diy">DIY</h3>

<p>The only way to really understand something is by doing. Before beginning the project I had no idea about what I should implement. I did not know anything about the project I would extend, nor about the protocol that I would implement support for. I started out by reading documentation but I was still lost. Only when I began implementing and building I started having a deep understanding of what I had to do. So don’t be afraid if you don’t know about a specific subject beforehand. Start implementing and gradually everything will fall in place :)</p>

<h3 id="embrace-criticism">Embrace Criticism</h3>

<p>By contributing to an open source project you publish your work in public allowing everyone to scrutinize it. There are two ways to handle this criticism; either being defensive by not accepting it or being open by trying to use it to improve yourself. I believe that the most important value that GSoC taught me is to accept and even crave for criticism, as it brings you in contact with other points of view that you might have missed.</p>

<p>Note: My <a href="https://github.com/processone/ejabberd/pull/1959">GSoC submission</a> was completed thanks to my mentor <a href="https://github.com/zinid">Evgeny Khramtsov</a>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[It has been one and a half month since GSoC 2017 ended and I would like to note how amazing the whole experience was. I learned a lot about the specific subjects that I dealt with (ACME protocol, X.509 certificates), as well as generally about software engineering and cooperating with other people. As enough time has passed from the completion of the project I think that I can now epitomize what I really kept from the whole experience.]]></summary></entry></feed>