# Abstract

Software produced for research, published and otherwise, suffers from a number of common problems that make it difficult or impossible to run outside the original institution, or even off the primary developer's computer. We present ten simple rules to make such software robust enough to be run by anyone, anywhere, and thereby delight your users and collaborators.

# Author Summary

Many researchers have found out the hard way that there's a world of difference between "works for me on my machine" and "works for other people on theirs". Many common challenges can be avoided by following a few simple rules; doing so not only improves reproducibility, but can accelerate research.

# Introduction

Scientific software is typically developed and used by a single person, usually a graduate student or postdoc . It may produce the intended results in their hands, but what happens when someone else wants to run it? Everyone with a few years of experience feels a bit nervous when told to use another person's code to analyze their data: it will often be undocumented, work in unexpected ways (if it works at all), rely on nonexistent paths or resources, be tuned for a single dataset, or simply be an older version than was used in published papers. The potential new user is then faced with two unpalatable options: hack the existing code to make it work, or start over.

Being unable to replicate results is so common that one publication refers to it as "a rite of passage" . The root cause of this problem is that most research software is essentially a prototype, and therefore is not *robust*. The lack of robustness in published, distributed software leads to duplicated efforts with little practical benefit, which slows the pace of research . Bioinformatics software repositories catalogue dozens to hundreds of tools that perform similar tasks: for example, in 2016 the Bioinformatics Links Directory included 84 different multiple sequence aligners, 141 tools to analyze transcript expression, and 182 pathway and interaction resources. Some of these tools are legitimate efforts to improve the state-of-the-art, but often they are difficult to install and run , and are effectively abandoned after publication .

This problem is not unique to bioinformatics, or even to computing . Best practices in software engineering specifically aim to increase software robustness. However, most bioinformaticians learn what they know about software development on the job or otherwise informally . Existing training programs and initiatives rarely have the time to cover software engineering in depth, especially since the field is so broad and developing so rapidly . In addition, making software robust is not directly rewarded in science, and funding is difficult to come by . Some proposed solutions to this problem include restructuring educational programs, hiring dedicated software engineers , partnering with private sector or grassroots organizations , or using specific technical tools like containerization or cloud computing . Each of these requires time and, in some cases, institutional change.

The good news is, you don't need to be a professionally-trained programmer to write robust software. In fact, some of the best, most reliable pieces of software in many scientific communities are written by researchers  who have adopted strong software engineering approaches, have high standards of reproducibility, use good testing practices, and foster strong user bases through constantly evolving, clearly documented, useful, and useable software. In the bioinformatics community, Bioconductor and Galaxy follow this path . Not all scientific software needs to be robust , but if you publish a paper about your software, it should, at minimum, satisfy these rules.

So what *is* "robust" software?

We implied above that it is software that works for people other than the original author, on machines other than its creator's. More specifically, we mean that:

-   it can be installed on more than one computer with relative ease,

-   it works consistently as advertised, and

-   it can be integrated with other tools.

Our rules are generic and can be applied to all languages, libraries, packages, documentation styles, and operating systems for both closed-source and open-source software. They are also necessary steps toward making computational research replicable and reproducible: after all, if your tools and libraries cannot be run by others, they cannot be used to verify your results or as a stepping stone for future work .

# Rule 1: Use version control.

Version control is essential to sustainable software development . In particular, developers will struggle to understand what they have actually built, what it actually does, and what they have actually released without some mechanical way to keep track of changes. They should therefore **put everything created manually into version control as soon as it is created**, including programs, original field observations, and the source files for papers. Files that can be regenerated at need, such as the binaries for compiled programs or intermediate files generated during data analysis, should not be versioned; instead, it is often more sensible to use an archiving system for them, and store the metadata describing their contents in version control instead .

If you are new to version control, it is simplest to treat it as "a better Dropbox[^1]" (or, if you are of a certain age, a better FTP), and to use it simply to synchronize files between multiple developers and machines . Once you are comfortable working that way, you should **use a feature branch workflow**: designate one parallel copy (or "branch") of the repository as the master, and create a new branch from it each time you want to fix a bug or add a new feature. This allows work on independent changes to proceed in isolation; once the work has been completed and tested, it can be merged into the master branch for release.

# Rule 2: Document your code and usage

How to write high quality documentation has been described elsewhere  and so here we only cover two minimal types: the README and usage. The README is usually available even before the software is installed, exists to get a new user started, and points them towards more help. Usage is a terse, informative command-line help message that guides the user in the correct use of the software.

Numerous guidelines exist on how to **write a good README file** . At a minimum, your README should:

The program should also **print usage information** when launching from the command line. Usage provides the first line of help for both new and experienced users. Terseness is important: usage that extends for multiple screens is difficult to read or refer to on the fly.

Almost all command-line applications use a combination of POSIX  and GNU  standards for usage. More standard command-line behaviours are detailed in . Your software's usage should:

1.  *Describe the syntax for running the program*, including the name of the program, the relative location of optional and required flags, other arguments, and values for execution.

2.  *Give a short description* to remind users of the software's primary function.

3.  *List the most commonly used arguments*, a description of each, and the default values.

4.  *State where to find more information*.

Usage should be printed to standard output so that it can be combined with other bash utilities like `grep`, and it should finish with an appropiate exit code.

Documentation beyond the README and usage is up to the developer's discretion. We think it is very important for developers to document their work, but our experience is that people are unlikely do it during normal development However, it is worth noting that software that is widely used and contributed to has and enforces the need for good documentation .

# Rule 3: Make common operations easy to control.

Being able to change parameters on the fly to determine if and how they change the results is important as your software gains more users, as it facilitates exploratory analysis and parameter sweeping. Programs should therefore **allow the most commonly changed parameters to be configured from the command line**.

Users will want to change some values more often than others. Since parameters are software-specific, the appropriate 'tunable' ones cannot be detailed here, but a short list includes input and reference files and directories, output files and directories, filtering parameters, random number generation seeds, and alternatives such as compressing results, use a variant algorithm, or verbose output.

**Check that all input values are in a reasonable range at startup**. Few things are as annoying as having a program announce after running for two hours that it isn't going to save its results because the requested directory doesn't exist.

To make programs even easier to use, **choose reasonable defaults where they exist** and **set no defaults at all when there aren't any reasonable ones**. You can set reasonable default values as long as any command line arguments override those values.

Changeable values should *never* be hard-coded: if users have to edit your software in order to run it, you have done something wrong. Changeable but infrequently-changed values should therefore be stored in configuration files. These can be in a standard location, e.g. `.packagerc` in the user's home directory, or provided on the command line as an additional argument. Configuration files are often created during installation to set up such things as server names, network drives, and other defaults for your lab or institution.

# Rule 4: Version your releases.

Software evolves over time, with developers adding or removing features as need dictates. Making official releases stamps a particular set of features with a project-specific identifier so that version can be retrieved for later use. For example, if a paper is published, the software should be released at the same time so that the results can be reproduced.

Most software has a version number composed of a decimal number that increments as new versions are released. There are many different ways to construct and interpret this number, but most importantly for us, a particular software version run with the same parameters should give identical results no matter when it's run. Results include both correct output as well as any errors. **Increment your version number every time you release your software to other people**.

Semantic versioning[^2] is one of the most common types of versioning for open-source software. Version numbers take the form of *MAJOR.MINOR\[.PATCH\]*, e.g., 0.2.6. Changes in the major version number herald significant changes in the software that are not backwards compatible, such as changing or removing features or altering the primary functions of the software. Increasing the minor version represents incremental improvements in the software, like adding new features. Following the minor version number can be an arbitrary number of project-specific identifiers, including patches, builds and qualifiers. Common qualifiers include `alpha`, `beta`, and `SNAPSHOT`, for applications that are not yet stable or released, and `-RC` for release candidates prior to an official release.

**The version of your software should be easily available by supplying `--version` or `-v` on the command line**. This command should print the software name and version number, and it should also be **included in all of the program's output**, particularly debugging traces. If someone needs help, it's important that they be able to tell whoever's helping them which version of the software they're using.

While new releases may make a program better in general, they can simultaneously create work for someone who integrated the old version into their own workflow a year or two ago, and won't see any benefits from upgrading. A program's authors should therefore **ensure that old released versions continue to be available.** A number of mechanisms exist for controlled release that range from as simple as adding an appropriate commit message or tag to version control , to official releases alongside code on Bitbucket or GitHub, to depositing into a repository like apt, yum, homebrew, CPAN, etc. Choose the method that best suits the number and expertise of users you anticipate.

# Rule 5: Reuse software (within reason)

In the spirit of code reuse and interoperability, developers often want to reuse software written by others. With a few lines, a call is made out to another library or program and the results are incorporated into the primary script. Using popular projects reduces the amount of code that needs to be maintained and leverages the work done by the other software.

Unfortunately, reusing software (whether software libraries or separate executables) introduces dependencies, which can bring their own special pain. The interface between two software packages can be a source of considerable frustration: all too often, support requests descend into debugging errors produced by the other project due to incompatible libraries, versions, or operating systems . Even introducing libraries in the same programming language can rely on software installed in the environment, and the problem becomes much more difficult when relying on executables, or even on web services.

Despite these problems, software developers in research should re-use existing software provided a few guidelines are adhered to.

First, **make sure that you really need the auxiliary program**. If you are executing GNU sort instead of figuring out how to sort lists in Python, it may not be worth the pain of integration. Reuse software that offers some measurable improvement to your project.

Second, if launching an executable, **ensure the appropriate software and version is available**. Either allow the user to configure the exact path to the package,

distribute the program with the dependent software, or download it during installation using your package manager. If the executable requires internet access, check for that early in execution.

Third, **ensure that reused software is robust**. Relying on erratic third party libraries or software is a recipe for tears. Prefer software that follows good software development practices, is open for support questions, and is available from a stable location or repository using your package manager.

Exercise caution especially when transitioning across languages or using separate executables, as they tend to be especially sensitive to operating systems, environments, and locales.

# Rule 6: Rely on build tools and package managers for installation.

To compile code, deploy applications, and automate other tasks, programmers routinely use build tools like Make, Rake, Maven, Ant or MS Build. These tools can also be used to manage runtime environments, i.e., to check that the right versions of required packages are installed and install or upgrade them if they are not. As mentioned in Rule 5, a package manager can mitigate some of the difficulties in software reuse.

The same tools can and should be used to manage runtime environments on users' machines as well. Accordingly, developers should **document all dependencies in a machine-readable form**. Package managers like apt and yum are available on most Unix-like systems, and application package managers exist for specific languages like Python (pip), Java (Maven/Gradle), and Ruby (RubyGems). These package managers can be used together with the build utility to ensure that dependencies are available at compile/run time.

For example, it is common for Python projects to include a file called `requirements.txt` that lists the names of required libraries, along with version ranges:

    requests>=2.0
    pygithub>=1.26,<=1.27
    python-social-auth>=0.2.19,<0.3

This file can be read by the pip package manager, which can check that the required software is available and install it if it is not. Whatever is used, developers should *always* install dependencies using their dependency description, especially on their personal machines, so that they're sure it works.

Conversely, developers should **avoid depending on scripts and tools which are not available as packages**. In many cases, a program's author may not realize that some tool was built locally, and doesn't exist elsewhere. At present, the only sure way to discover such unknown dependencies is to install on a system administered by someone else and see what breaks. As use of virtualization containers becomes more widespread, software installation can also be tested on a virtual machine or container system like Docker.

# Rule 7: Do not require root or other special privileges to install or run.

Root (also known as "superuser" or "admin") is a special account on a computer that has (among other things) the power to modify or delete system files and user accounts. Conversely, files and directories owned by root usually cannot be modifed by normal users.

Installing or running a program with root privileges is often convenient, since doing so automatically bypasses all those pesky safety checks that might otherwise get in the user's way. However, those checks are there for a reason: scientific software packages may not intentionally be malware, but one small bug or over-eager file-matching expression can certainly make them behave as if they were. Outside of very unusual circumstances, **packages should not require root privileges to set up or use**.

Another reason for this rule is that users may want to try out a new package before installing it system-wide on a cluster. Requiring root privileges will frustrate such efforts, and thereby reduce uptake of the package. Requiring, as Apache Tomcat does, that software be installed under its own user account—i.e., that `packagename` be made a user, and all of the package's software be installed in that pseudo-user's space—is similarly limiting, and makes side-by-side installation of multiple versions of the package more difficult.

Developers should therefore **allow packages to be installed in an arbitrary location**, e.g., under a user's home directory in `~/packagename`, or in directories with standard names like `bin`, `lib`, and `man` under a chosen directory. If the first option is chosen, the user may need to modify her search path to include the package's executables and libraries, but this can (more or less) be automated, and is much less risky than setting things up as root.

Testing the ability to install software has traditionally been regarded as difficult, since it necessarily alters the machine on which the test is conducted. Lightweight virtualization containers like Docker make this much easier as well, or simply **ask another person to try and build your software before releasing it**.

# Rule 8: Eliminate hard-coded paths.

It's easy to write software that reads input from a file called `mydata.csv`, but also very limiting. If a colleague asks you to process her data, you must either overwrite your data file (which is risky) or edit your code to read `otherdata.csv` (which is also risky, because there's every likelihood you'll forget to change the filename back, or will change three uses of the filename but not a fourth).

Hard-coding file paths in a program also makes the software harder to run in other environments. If your package is installed on a cluster, for example, the user's data will almost certainly *not* be in the same directory as the software, and the folder `C:\users\yourname\` will probably not even exist.

For these reasons, users should be able to **set the names and locations of input and output files as command-line parameters**. This rule applies to reference data sets as well as the user's own data: if a user wants to try a new gene identification algorithm using a different set of genes as a training set, she should not have to edit the software to do so. A corollary to this rule is **do not require users to navigate to a particular directory to do their work**, since "where I have to be" is just another hard-coded path.

In order to save typing, it is often convenient to allow users to specify an input or output *directory*, and then require that there be files with particular names in that directory. This practice, which is sometimes called "convention over configuration", is used by many software frameworks, such as WordPress and Ruby on Rails, and often strikes a good balance between adaptability and consistency.

# Rule 9: Include a small test set that can be run to ensure the software is actually working.

Every package should come with a set of tests for users to run after installation. Its purpose is not only to check that the software is working correctly (although that is extremely helpful), but also to ensure that it works at all. This test script can also serve as a working example of how to run the software.

In order to be useful, **make the tests easy to find and run**. Many build systems will also run unit tests if provided them at compile time. For users, or if the build system is not amenable to testing, provide a working script in the project's root directory named `runtests.sh` or something equally obvious. This lets new users build their analysis from a working script. For example, with its distribution, HISAT2 includes a full set of very small files, and a 'Getting Started with HISAT2' section in its manual that leads you through the entire data lifecycle .

Equally, **make the test script's output easy to interpret**. Screens full of correlation coefficients do not qualify: instead, the script's output should be simple to understand for non-experts, such as one line per test, with the test's name and its pass/fail status, followed by a single summary line saying how many tests were run and how many passed or failed. If many or all tests fail because of missing dependencies, that fact should be displayed once, clearly, rather than once per test, so that users have a clear idea of what they need to fix and how much work it's likely to take.

Research has shown that the ease with which people can start making contributions is a strong predictor of whether they will or not . By making it simpler for outsiders to contribute, a test suite of any kind also makes it more likely that they will, and software with collaborators stands a better chance of surviving in the busy field of scientific software.

# Rule 10: Produce identical results when given identical inputs.

The usage message tells users what the program could do. It is equally important for the program to tell users what it actually did. Accordingly, when the program starts, it should **echo all parameters and software versions to standard out or a log file alongside the results**. This feature supports greater reproducibility because any result can be replicated with only the previous output files as reference.

Given a set of parameters and a dataset, **a particular version of a program should produce the same results every time it is run** to aid testing, debugging, and reproducibility. Even minor changes to code can cause minor changes in output because of floating-point issues, which means that getting exactly the same output for the same input and parameters probably won't work during development, but it should still be a goal for people who have deployed a specific version.

Many applications rely on randomized algorithms to improve performance or runtimes. As a consequence, results can change between runs, even when provided with the same data and parameters. By its nature, this randomness renders strict reproducibility and therefore debugging more difficult. If even the small test set (#9) produces different results for each run, new users may not be able to tell whether the software is working properly. When comparing results between versions or after changing parameters, even small differences can confuse or muddy the comparison. And especially when producing results for publications, grants or diagnoses, any analysis should be absolutely reproducible.

Given the size of biological data, it is unreasonable to suggest that random algorithms be removed. However, most programs use a pseudo-random number generator, which uses a starting seed and an equation to approximate random numbers. Setting the seed to a consistent value can remove randomness between runs. **Allow the user to optionally provide the random seed as an input parameter**, thus rendering the program deterministic for those cases where it matters. If the seed is set internally (e.g., using clock time), echo it to the output for re-use later. If setting the seed is not possible, **make sure the acceptable tolerance is known and detailed in documentation and in the tests**.

# Conclusion

There has been extended discussion over the past few years of the sustainability of research software, but this question is meaningless in isolation: any piece of software can be sustained if its users are willing to put in enough effort. The real equation is the ratio between the skill and effort available, and the ease with which software can be installed, understood, used, maintained, and extended. Following the ten rules we outline here reduce the denominator, and thereby enable researchers to build on each other's work more easily.

That said, not *every* coding effort needs to be engineered to last. Code that is used once to answer a specific question related to a specific dataset doesn't require comprehensive documentation or flexible configuration, and the only sensible way to test it may well be to run it on the dataset in question. Exploratory analysis is an iterative process that is developed quick and revised often . However, if a script is dusted off and run three or four times for slightly different purposes, is crucial to a publication or a lab, or being passed on to someone else, it may be time to make your software more robust.

[^1]: `http://dropbox.com`

[^2]: `http://semver.org/`


|     |
|:---:|

Imitation of Life: **Advanced system for native Artificial Evolution**

**by Sperl Thomas**
`email@example.com`
`July 2011`

**Abstract:**

A model for artificial evolution in native x86 Windows systems has been developed at the end of 2010. In this text, further improvements and additional analogies to natural microbiologic processes are presented. Several experiments indicate the capability of the system - and raise the question of possible countermeasures.

# Introduction

Artificial Evolution has become a successful playground for evolutional experiments, when Tom Ray released the Tierra system.. Tierra is a virtual system with self-replicating programs which simulate mutations in form of copying errors. The artificial creatures struggle for the limited resources (such as CPU time and memory space), thus the systems fullfills the three criteria for evolution: replication, mutation, selection.
In order to achieve high robustness against mutations, Ray introduced techniques such as non-direct addressing and seperation of arguments and operations.
Many interesting insights to evolution have been found with Tierra (such as evolution of multi-cellularity or parallel computing) and similar systems such as avida (evolution under high mutation rate and emergence of complex features).
Iliopoulos, Adami and Ször have discussed the consequences for computer security of implementing darwinian principles into native system, in 2008. They concluded *that a truly undetectable virus might be more feasible than previously imagined*, and that it is currently unknown whether there would be a defence against such organisms.
In 2010, I have created the first (to my knowledge) implementation of an artificial evolution system for a native operation system (Microsoft Windows XP+ 32bit), using several parallels to the natural biosynthesis process. A short comparison between usual x86 code and the new artificial evolution concept shows that the new concept is actually more robust against mutations.
The presented proof-of-concept organism as well as the underlaying metalanguage have been analysed in detail by Peter Ferrie in 2011.
The idea of this article is to continue this research...

# Artificial Biosynthesis

The main idea is to use a similar concept to natural biosynthesis: The codons of the mRNA are translated into amino acids using tRNA molecules, these amino acid chains form the proteins - the actually functional part of the cell.
In the artificial analogon, the whole information of the organism is saved in a chain of codons. Each codon consists of 8bits, thus there are 256 different codons. In the translator (similar to tRNA in the ribosom), the codons will be mapped to an x86 instruction (similar to an amino acid) - a chain of x86 instructions form the protein, the functional part of the organism:

|                |             |
|:--------------:|:-----------:|
| **Artificial** | **Natural** |
|      bit       | nucleobase  |
|      byte      |    codon    |
|  instruction   | amino acid  |
|    function    |   protein   |
|   translator   |    tRNA     |

In the natural system, the information is saved in codons, which consists of 3 nucleobases - as there exists 4 different nucleobases (**A**denine, **G**uanine, **T**hymine, **C**ytosine), a codon can have $4^3$ = 64 different representations. Each codon codes one out of 20 amino acids, thus there is a redundancy in the mapping process, which is used to increase the robustness of the code. This redundancy is also used in the artificial biosynthesis as there are less than $2^8$=256 base functions of the meta language.

## Meta-Language

The idea is to create a compact, *complete* instruction set with seperation of arguments and operations, and with non-direct addressing.
The language provides seven registers with specific properties:

-   **RegA, RegB, RegD** - general purpose registers (correspond to EAX, EBX, EDX)

-   **BC1** - operation register (correspond to EBX): the first argument of every operation, and source or destination for other instructions

-   **BC2** - argument register (correspond to ECX): the second argument of every operation

-   **BA1** - write address register (correspond to EDI): holds the address for write instructions

-   **BA2** - jump address register (correspond to ESI): holds the address for jump instructions

The seperation of arguments and operations is realized by using BC1 (and BC2) as standard arguments, and filling them independently of the operation.
The language provides PIC (*position-independent code*). Every address is relative to the instruction pointer thus is independent of the position.

|  |  |  |
|:---|:---|:---|
| **nopREAL** |  | nop |
| **nopsA** | BC1 = RegA | mov ebx, eax |
| **nopsB** | BC1 = RegB | mov ebx, ebp |
| **nopsD** | BC1 = RegD | mov ebx, edx |
| **nopdA** | RegA = BC1 | mov eax, ebx |
| **nopdB** | RegB = BC1 | mov ebp, ebx |
| **nopdD** | RegD = BC1 | mov edx, ebx |
| **save** | BC2 = BC1 | mov ecx, ebx |
| **addsaved** | BC1 + = BC2 | add ebx, ecx |
| **subsaved** | BC1 - = BC2 | sub ebx, ecx |
| **saveWrtOff** | BA1 = BC1 | mov edi, ebx |
| **saveJmpOff** | BA2 = BC1 | mov esi, ebx |
| **writeByte** | byte\[BA1\] = (BC1 & 0xFF) | mov byte\[edi\], bl |
| **writeDWord** | dword\[BA1\] = BC1 | mov dword\[edi\], ebx |
| **getDO** | BC1 = DataOffset | mov ebx, DataOffset |
| **getdata** | BC1 = dword\[BC1\] | mov ebx, dword\[ebx\] |
| **getEIP** | BC1 = InstructionPointer | call gEIP; gEIP: pop ebx |
| **push** | push BC1 | push ebx |
| **pop** | pop BC1 | pop ebx |
| **pushall** | pushad | pushad |
| **popall** | popad | popad |
| **zer0** | BC1 = 0 | mov ebx, 0x0 |
| **add0001** | BC1 + = 0x1 | add ebx, 0x1 |
| **add0004** | BC1 + = 0x4 | add ebx, 0x4 |
| **add0010** | BC1 + = 0x10 | add ebx, 0x10 |
| **add0040** | BC1 + = 0x40 | add ebx, 0x40 |
| **add0100** | BC1 + = 0x100 | add ebx, 0x100 |
| **add0400** | BC1 + = 0x400 | add ebx, 0x400 |
| **add1000** | BC1 + = 0x1000 | add ebx, 0x1000 |
| **add4000** | BC1 + = 0x4000 | add ebx, 0x4000 |
| **sub0001** | BC1 - = 0x1 | sub ebx, 0x1 |
| **shl** | BC1 $<<$ (BC2 & 0xFF) | shl ebx, cl |
| **shr** | BC1 $>>$ (BC2 & 0xFF) | shr ebx, cl |
| **xor** | BC1 $\wedge$ = BC2 | xor ebx, ecx |
| **and** | BC1 & = BC2 | and ebx, ecx |
| **mul** | (RegD: RegA) = RegA \* BC1 | mul ebx |
| **div** | (RegD, RegA) = RegA / BC1 | div ebx |
| **JnzUp** |  | jz over; jmp esi; over: |
| **JnzDown** |  | jnz down; times 32: nop; down: |
| **call** |  | stdcall ebx |
| **CallAPILoadLibrary** |  | stdcall dword\[LoadLibrary\] |

### Mutable API calls

To use the APIs provided by the OS, an organism can use the `call` instruction:

|                       |
|:---------------------:|
| ![image](APIcall.PNG) |

One possibility is to save the API addresses directly within the organism body. However, this has two disadvantages: Firstly, the addresses may change for each new version of Windows. Secondly, the probability that a different and valid API address appears out of one single bitflip is very low (a rough estimate: a memory address of 32-bit gives $2^{32}$ possible addresses - say there are 5000 valid API addresses. The probability to reach one of them is $P = \frac{5000}{2^{32}} \approx 10^{-6}$).
A different method is to save a short hash of the desired API name in the organism. Then load the DLL, scan the export section for API names and create hashes for each API. If the hashes match, save the address of the API. This approach is independent of the OS version, and it's very mutable. It is possible to use hash as short as 12bit for each API. Let's say there are 1000 APIs in a DLL file. The probability to access on of these APIs within one single bitflip is given by $P = \frac{1000}{2^{12}} \approx 0.25$. In average, every 4th bitflip in the API hash leads to a different hash corresponding to a valid API.

### Example: ROL instruction

The x86 instruction ROL (*Rotate Left*) is not directly provided by the instruction set. However, it can be written using just instructions of the set.
Let's say, one would like to write `rol RegA, c`, where c is some integer - then the usual assembler instructions looks like this:
![image](ROL_asm.PNG)
Translating this into the ArtEvol MetaLanguage is very easy:
![image](ROL_AEML.PNG)
The 2nd argument (`c` in this case) is saved in the BC2 register, then the first argument is loaded into the BC1 register and the operation is performed. As ROL requires three operations, the result of the first one is temporarily saved at the stack; the same procedure is performed again, and in the end, the results are combined.
The `addnumber` is a macro which returns the right combination of `addNNNN` instructions.

## Translator

In order to convert the metalanguage instructions to native x86 instructions, a tiny translator is used:

|                          |
|:------------------------:|
| ![image](Translator.PNG) |

## Replication, Mutation and Selection

To achieve Evolution, a system requires Reproduction, Mutations and Selection.
In the **reproduction** stage, the organism creates a living copy of itself. In the artificial system of a computer, this can be done in the same way as computer viruses and computer worms do - interfere with special file formats or network protocols such that the copy will be executed in a different habitat (other computer, other file, ...). However, this requires alot of previous knowledge about the system, thus is not the simplest starting point for evolution. The most trivial way of reproduction is to copy the own file in the current directory and run it - which is actually the way how it is done in the experiments.
The interference between reproduction and **mutations** leads to non-identical replica of the organism. In the biological system, mutations happens due to disruptive effect such as cosmic X-rays. This leads to point mutations (exchange of one single codon) or more difficult chromosome abnormality. The natural mutation probability in a computer system (such as mistakes in the copy process) is neglectable, thus the organism has to carry its own mutation engines. This is explained in detail in the next chapter.
Natural **selection** is a process in which a certain trait becomes more or less common in the population, depending on its effect on the fitness of the organism. This process appears when the organism compete, struggle for limited values (such as energy), or are exposed to natural enemies. In the artificial system of a computer, anti virus programs could be responsible for natural selection (and by that unwillingly initiate a faster evolutionary process at all). A different selective pressure comes from attentive user, who would stop any suspicious behaviour.

# Mutations

## Point mutation - bit flip

Point mutations change single nucleobases in the DNA. These mutations can be categorized into silent (when the affected codon maps to the same amino acid due to the redundance in the alphabet), missense (when the affected codon maps to a different amino acid); or nonsense (when the affected codon maps to the STOP codon).
A native analogon to that concept would be the change of single bits in the organism - called *Bitflip*. These mutations have the same categories as their biological companion.
The mutation rate (mutations per base per generation) in biological organisms varies from $10^{-4}$ for very small (some kilo bases) to $10^{-8}-10^{-9}$ for humans (some giga bases). Finding an adequate mutation rate for artificial organisms is not trivial, as too small values lead to mainly unmutated offspring, thus no evolution; whereas too big mutation rates lead to extinction of the population. To get the best, one could test organisms with different mutation rate, and take the critical value.

|  |  |
|:--:|:--:|
| ![image](MutaRate07001.PNG) | ![image](MutaRate09001.PNG) |
| **Figure 3.1**: Five populations with a | **Figure 3.2**: Five populations with a |
| mutation rate of 1 / 7.001 | mutation rate of 1 / 9.001 |

|  |  |
|:--:|:--:|
| ![image](MutaRate11003.PNG) | ![image](MutaRate13499.PNG) |
| **Figure 3.3**: Five populations with a | **Figure 3.4**: Five populations with a |
| mutation rate of 1 / 11.003 | mutation rate of 1 / 13.499 |

The test-organism - which can create three offspring - has a size of 20.480 bytes, the critical mutation rate is between $\frac{1}{9.001}$ and $\frac{1}{11.003}$; the probability that at least one bit is changed is between 84.5% and 89.7%.

## Chromosomal inversion - byte eXCHanGe

A different kind of mutation happens when a segment of a chromosome is reversed. This happens when a segment breaks off and is rearranged in the wrong way.
A similar method is used in this project: two consecutive d-words (that means, 4 codons each) are exchanged. This mutation is not as dangerous as it may look like at first; the codon streams forming a functional part are very big, thus are not too sensitive on such local translocations.

|  |  |
|:--:|:--:|
| ![image](MutaRate26501_xchg.PNG) | ![image](MutaRate27011_xchg.PNG) |
| **Figure 3.5**: Five populations with a | **Figure 3.6**: Five populations with a |
| mutation rate of 1 / 26.501 | mutation rate of 1 / 27.011 |

|  |  |
|:--:|:--:|
| ![image](MutaRate27253_xchg.PNG) | ![image](MutaRate27509_xchg.PNG) |
| **Figure 3.7**: Five populations with a | **Figure 3.8**: Five populations with a |
| mutation rate of 1 / 27.253 | mutation rate of 1 / 27.509 |

These graphs show that the critical mutation rate is much sharper than for Bitflips - this is what you would expect as Byte Exchange has much stronger effects.

## Deletion, insertion, translocation

Deletion is a mutation in which a part of the DNA is missing; insertion is the inverse process, where an additional sequence of DNA is included into the genom. Translocation is a combination of these mutations: A part of the DNA breaks off and is included at a different place.
A similar method can be realized in artificial organisms in the computer system within one simple algorithm. Three random values are calculated (the place of the mutation $P$, the size of the inserted NOP block $S_i$ and the size of the translocated block $S_b$). Then at P, a block with the size of $S_b$ will be translocated $S_i$ bytes, the new created block at P will be filled with NOPs.

![image](deletion.png)

**Figure 3.9**: Deletion process in the organism. Grey are NOPs, red and orange are functional parts. Red is not moved, orange is the translocated sequence. The second red part is smaller after deletion, as it has lost some of its code.

![image](insertion.png)

**Figure 3.10**: Insertion process in the organism. Grey are NOPs, red and orange are functional parts. Red is not moved, orange is the translocated sequence. The whole orange part is translocated, thus there is nothing deleted - just an insertion of NOPs.

## Horizontal gene transfer

Horizontal gene transfer is a process in which an organism incorporates genetic material from another organism without being the offspring of that organism. In biological systems, this is a controlled (not by random mutations) method to receive beneficial functions such as antibiotic resistance. Photosynthesis is an important process which has been developed with horizontal gene transfer from different bacteria.
In the artificial system, an organism could try to interact with other organism and exchange valid code, and therefore perform a symbiotic *conjugation*. However, this would require a specific protocol for communication (such as F-plasmids in bacteria) - which is not developed so far.
Nevertheless the artificial organism could try to gain new information from other files, just by opening them and copy some parts of their code. In the case that the other file is written in the same language, the organism has the chance of getting new functions.

## \*Polymorphism: neutral codon variation

In the artificial organisms, the alphabet has $2^8 = 256$ entries which map to 45 or less instructions, thus there is a big redundancy - that means several codons map to the same amino acid.
The organism can scan thru its alphabet, detect equal amino acids, then scan its codon-stream and exchange the codons which point to the same amino acid.
There are a few advantages to use this technique in artificial organism: Firstly, codons which point to isolated amino acids (these who can not be transformed by a single bitflip to another amino acid of the same kind) can be de-isolated, thereby increase the robustness of the overall code. Secondly, such *macro mutations* are of high importance to bypass natural enemies (such as antivirus software), thus increase fitness. And thirdly, mutations in the polymorphism-engine itself or variations of the START or STOP codon could lead to unpredictable results.

# Further improvements

## Start- and Stop codons: Splicing

Natural genetic code contains alot of non-functional garbage, which can be old unused DNA or (malformed) duplicates of actual functional code. In human DNA, approximately 95% of the DNA is garbage. These non-coding parts are called *Introns*, the functional parts are *Exons*. Before translation of mRNA into proteins, the introns are cutted out in a process called *Splicing* - this is done by taking usage of two special codons - the *START* and the *STOP* codon. Each functional part starts with a START-codon and ends with a STOP-codon - the parts between a STOP codon and the next START codon (which is actually the intron) will be removed.
The advantage of such introns is that within the unused code new DNA sequences could be developed which are (in very rare cases) actually functional - and have different behaviour. Together with the additional possibility for altering its code, this would be a special gain for artificial organisms.

|                        |
|:----------------------:|
| ![image](splicing.png) |

**Figure 4.1**: Splicing functionality in a artificial organism

Implementing a very small splicing algorithm into the translator can be achieved in the following way:

-   All codons in the form of **1??1.???1** (32 codons) point to a NOP amino acid

-   At the translation, whenever there is a STOP codon, AL=0x91 (1001.0001); whenever there is a START codon, AL=0x0

-   Each codon will be OR'ed with AL

-   In the end, each codon after a STOP mark will be redirected to a NOP amino acid - until there is a START codon

|                                 |
|:-------------------------------:|
| ![image](Translator_splice.PNG) |

## Optimization of the instruction set

The original instruction set had 43 different entries. Ofria, Adami and Collier found out, that a smaller instruction set leads to higher robustness under mutations, thus higher fitness.
In their experiments, the reason is that small instruction set requires bigger realisations of function, thus the risk of a lethal mutation is spread over a larger area. In our realisation, a second advantage appeares. A small instruction set leads to a more redundant alphabet, therefore allows more codons to point to the same amino acid. In the end, there is a bigger probability that a single BitFlip changes the codon such that it still points to the original amino acid.
Peter Ferrie was able to create an optimized instruction set with just 18 entries, by replacing an instruction with a combination of other instructions. One simple example is `zer0` $\to$ `save`+`xor`.
For implementing these optimizations, one has to take care of changed Registers and Flags. This can be done by using Stack instruction (`pushall` and `popall`) or even implement a new *pseudo*register (in the .data section) with some special properties.
Unfortunately, these implementations lead to an excessive useage of *evolutionary dangerous* instructions, which are instructions that lead to the programs crash if they are replaced by other instructions.
I consider dangerous instructions as everything that interacts with the stack (`push`, `pop`, `pushall`, `popall`, `CallAPILoadLibrary`), that influences the code flow (`JnzDown`, `JnzUp`, `call`, `saveJmpOff`), and that interacts with the memory (`saveWrtOff`, `writeByte`, `writeDWord`, `getdata`). One can create two further categories: *semi-harmless*, which are all instructions that change the values of `RegA`, `RegB`, `RegD` and `BC2`, and *harmless* instructions just change the `BC1` register.

|                                          |                                |
|:----------------------------------------:|:------------------------------:|
|        ![image](OptInstrALL.PNG)         |   ![image](OptInstrzer0.PNG)   |
| **Figure 4.2**: Original instruction set | **Figure 4.3**: `zer0` removed |

|                                    |                                   |
|:----------------------------------:|:---------------------------------:|
|   ![image](OptInstrsubsaved.PNG)   |   ![image](OptInstraddNNNN.PNG)   |
| **Figure 4.4**: `subsaved` removed | **Figure 4.5**: `addNNNN` removed |

|                                    |                                   |
|:----------------------------------:|:---------------------------------:|
|   ![image](OptInstraddsaved.PNG)   |   ![image](OptInstradd0001.PNG)   |
| **Figure 4.6**: `addsaved` removed | **Figure 4.7**: `add0001` removed |

The x-axis shows the different instructions (with the same order as written in chapter 2), the y-axis gives the (normalized) appearence of the instruction in an organism. It is interesting to see how dangerous instruction density increases, especially after removing the `addsaved` instruction.

## Optimization of the alphabet

To achieve the optimal robustness, evolution has lead to a special order of codon mapping. As an example, the amino acid *Proline* can be coded via `CCU`, `CCC`, `CCA` and `CCG`. That means, whenever a mutation changes the third nucleobase, still a codon remains that codes Prolin (in biological systems, mutations happens most often at the third nucleobase).
For artificial organisms, one has 256 slots for about 45 instructions, and furthermore different pairs of exchanged codons have different probability to cause mistakes (exchanging `add0001` with `add0004` may cause less problems than exchanging `add0001` with `pushall`).
This is a nonlinear problem, and solutions by hand take long and are of low quality. However, one can reformulate the problem - with the help of a bit of physics:
Let's imagine the codons as objects in a special space, such that each two codons with one bit difference are neighbors (the geometry of this space is an 8 dimensional cube with codons on the corners). For example, the codon `0000.0000` and `0010.0000` are neighbors. Each codon interacts with its neighbors, thus has an interaction energy (which depends on the types of the codons).
We can define V(A,B) as the interaction energy of codon A and B. One possible definition would be

|  |  |
|:---|:---|
| V(A,B) = 0 | if A = B |
| V(A,B) = 0.5 | if A and B are `addNNNN` or `sub0001` |
| V(A,B) = 0.66 | if A and B are harmless instructions |
| V(A,B) = 0.75 | if A and B are harmless or semi-harmless instructions |
| V(A,B) = 1 | else (if A or B is a dangerous instruction or a START or STOP codon) |

Now we can define the total energy of the system as: $$\begin{aligned}
E_{\textnormal{total}}=\sum_{i=1}^{256} \sum_{j=1}^8 V(\textnormal{codon}_i, \textnormal{codon}_j)\nonumber
\end{aligned}$$ The total energy of a maximum random system would be 2.048, the mimumum total energy of a system with just one single instruction would be zero.
Finally, we can reformulate "find an optimal alphabet" into "find the minimal interaction energy of the system". There are several ways to find a minimum energy, one is the Metropolis-Algorithm; we use a slightly modified one.
First, we fill the 256 entries with instructions of random order, and calculate the total energy of the system. Then we exchange a few instructions randomly, and calculate the new total energy. If the new energy is smaller than the old one, we keep the new system, otherwise we continue with the old one. To find a (local) minimum, one can repeat that method.

![image](energy.PNG)

**Figure 4.8**: Energy minimization of five systems. x-axis is iteration number (in 1000s), y-axis is energy.

The figure shows that the alphabet reaches a good local minimum after a few 100.000 iterations. It also shows that systems with different starting order find local minimal of approximately the same energy.

# Experiments

An experiment measures the fitness of the organisms by letting them struggle for limited resources (such as CPU time and memory). To control the experiment, several *guard files* ran in the background, to close endless-loop files, multiple instances of the same file, unmutated files by a certain probability. A more detailed explanation can be found in .

## Hamming distance

In the first experiment, we analyse the *long time* behaviour of a population, that can just perform BitFlips and Byte XCHG.
The Hamming distance (difference in the bit-code) with respect to the original ancestor is calculated every 3 minutes.

![image](hamming.PNG)

**Figure 5.1**: Evolution of Hamming distance over 12 hours. x-axis: Hamming distance, y-axis: time (in minutes)
It is very surprising and interesting, that after about 7.5 hours a mutation has occured with the effect that some organisms can largely bypass the no-cloning guard. A deeper analysis of that event would require reverse engineering of mutated code, which is a non-trivial task - and therefore hasn't been done yet.
However, this event is an indication that artificial organisms can bypass control instances very fast.
A second experiment has been performed, with an organism which contains about 90% introns (similar to natural organisms).

![image](hamming_intron.PNG)

**Figure 5.2**: Evolution of Hamming distance for an organism with a high amount of introns over 6 hours. x-axis: Hamming distance, y-axis: time (in minutes)
As one would expect, the mutation rate is much higher and very constant, the standard derivation of the hamming distance spreads continuously.

## Effect of alphabet "energy"

As explained in chapter 4.3, each alphabet has a specific "energy". To see whether the used definition of the interaction actually give a good result for robust alphabets, an evolutionary experiment has been performed.

|  |  |
|:--:|:--:|
| ![image](AlphabetLvsRnd1.PNG) | ![image](AlphabetLvsRnd2.PNG) |
| **Figure 5.3**: Energy 1152.1 (blue) vs. | **Figure 5.4**: Energy 1152.1 (blue) vs. |
| 1662.9 $\pm$ 11.2 (red) | 1386.8 $\pm$ 3.2 (red) |

Figure 5.3 and 5.4 show that lower energy has a better fitness, thus the presented interaction definition is at least roughly a good approximation for an optimal alphabet.

## Effect of Start- and Stop-codons

One experiment has been performed with a number of 100.000 random codons within a STOP and a START codon - a big intron. Several organisms have converted a part of the intron into an exon (by introducing a START codon) without any negative effect - all converted functional parts were neutral mutations.
There were two very surprising results: The biggest converted part had 33 codons, and still worked without problem. A different converted exon even managed to perform an API call without crashing.

![image](intronToexon.PNG)

Such huge blocks of non-lethal codons and API calls could be very valuable for artificial organisms, which are chased by behaviour scanners and API call tracers.

# Outlook

There are several features in the natural protein biosynthesis (or in microbiology in general) which could be used in this artificial concept:
**Alternative Splicing:** Before translating the codons into amino acids, the splicing process cuts out introns. In natural systems, there can be an alternative splicing process, which can create the final codon sequence in many alternative ways. For example, exons can be combined in different ways, some exons could be cutted out, introns can be coded, and other methods. This process is influenced by regulatory elements (such as proteins). An analogy to this process would increase the variability of the organisms drastically.
**Protein Folding:** After translation of codons into amino acids, a process called *folding* gives a specific 3D structure to the amino acid chain - this structure is mainly responsible for the proteins chemical properties. A similar further layer of translation may have advantages for the organisms too.
**Protocol for Horizontal Gene Transfer:** To exchange valuable information as antibiotic resistance, bacteria have developed processes called *conjugation*. To develope a process like that for exchanging useful information within the population would be a big advantage.
There are some further techniques which would increase the variability of the organisms, such as increasing the functional code size. The difficulties come from restrictions of the PE format, which requires more than one field to be mutated in the same manner. This is a very unlikely process - finding a solution to that problem would open many new possibilities for the organisms.
The experiments have shown a very promising behaviour of the different mutation techniques. Especially the START- and STOP-codon experiment, where organisms performed an additional API call, indicates that even macro mutations are realistic within this concept - and behaviour changing mutations are not always lethal.
The question of how antivirus programs can detect organisms using this technique is open. The organism's non-lethal configuration is infinite, and due to the fact that darwinian evolution is not predictable, algorithmic approaches are probably unusable and limited. Behaviour scanners are likely (due to effects that has been shown with Splicing) not practicable, too. Statistical approaches may work for a low number of generations quite good, but will most likely fail for big difference to the ancestor (at high generations or when several macro mutations happened), as well..
... as a conclusion, one can see:
**The artificial organisms took the redpill - and enjoy their new freedom now...**


# Abstract

Scale-free networks, in which the distribution of the degrees obeys a power-law, are ubiquitous in the study of complex systems. One basic network property that relates to the structure of the links found is the degree assortativity, which is a measure of the correlation between the degrees of the nodes at the end of the links. Degree correlations are known to affect both the structure of a network and the dynamics of the processes supported thereon, including the resilience to damage, the spread of information and epidemics, and the efficiency of defence mechanisms. Nonetheless, while many studies focus on undirected scale-free networks, the interactions in real-world systems often have a directionality. Here, we investigate the dependence of the degree correlations on the power-law exponents in directed scale-free networks. To perform our study, we consider the problem of building directed networks with a prescribed degree distribution, providing a method for proper generation of power-law-distributed directed degree sequences. Applying this new method, we perform extensive numerical simulations, generating ensembles of directed scale-free networks with exponents between 2 and 3, and measuring ensemble averages of the Pearson correlation coefficients. Our results show that scale-free networks are on average uncorrelated across directed links for three of the four possible degree-degree correlations, namely in-degree to in-degree, in-degree to out-degree, and out-degree to out-degree. However, they exhibit anticorrelation between the number of outgoing connections and the number of incoming ones. The findings are consistent with an entropic origin for the observed disassortativity in biological and technological networks.

# Introduction

The use of networks is fundamental to model the structure and the dynamics of a vast number of systems found throughout the natural and engineered worlds. Their main appeal lies in allowing the reduction of a complex system to a discrete set of elements, the nodes, that interact across links. Then, one can study the structural properties of a network and infer results on the behaviour of the system thus modelled . The simplest global structural attribute of a network is its degree distribution $P\left(k\right)$, which expresses the probability of having a node with $k$ links. A particularly important case is that of scale-free networks, in which the degree distribution obeys a power-law $P\left(k\right)\sim k^{-\gamma}$ . Scale-free networks have been observed in citation distributions , Internet and WWW topology , biological systems , technological, economic and social systems , and transport processes , and therefore they have been the subject of a considerable body of research. A generalization of the simple network model can be introduced by defining a directionality for the links. Directed networks are more suited to represent systems in which the interaction between elements is not necessarily symmetric, such as food webs or gene regulatory networks . In this case, the connectivity of a node is no longer represented by a single scalar, as each node has a number of incoming connections (its in-degree $k^-$) and a number of outgoing connections (its out-degree $k^+$). A related quantity is the degree assortativity, often called simply assortativity, which measures the tendency of a node to be connected to nodes of similar degree. Assortativity is effectively a measure of the correlations amongst node degrees. As such, it is known to have substantial effects on the dynamical processes taking place on a network. For instance, assortative networks are more resistant to fragmentation in case of attack, while disassortative networks are less prone to cascading failures . Degree correlations also play an important role in mathematical epidemiology, as they directly affect the dynamics of epidemic spreading, as well as the efficiency of defence mechanisms . Numerous studies have shown that social networks are typically assortative, while biological and technological networks are disassortative, with links preferentially between nodes of high and low degree. In the case of directed networks, one can actually consider four different degree assortativities across links, as one can model the dependence of either in-degree or out-degree of a node on either in-degree or out-degree of its neighbours . Here, we study how a scale-free structure affects assortativity in directed networks. In particular, we show that directed scale-free networks exhibit no in–in, out–out and in–out correlations, but are anticorrelated in the out–in assortativity.

# Methods

To study the preferred correlation structure induced by scale-freeness in directed networks, we performed extensive numerics, generating statistical ensembles of networks with power-law distributed in-degrees and out-degrees. The generation of directed networks with given degree distributions involves two distinct phases. First, extract two sequences of integer numbers that follow the distributions, and assign these to the nodes as directed half-links, or "stubs". Taken in pairs, these numbers form a so-called bi-degree sequence, and correspond to the in-degree and the out-degree of each node. Then, sample the bi-degree sequence creating network realizations without self-edges or multiple edges. A suitable method to perform this second step is the algorithm discussed in Ref. , which allows an efficient uniform sampling of the realizations of a bi-degree sequence. However, not every sequence of integer pairs can be realized by a simple directed graph. Thus, before being able to apply the sampling algorithm, we need to develop a procedure to properly create bi-degree sequences that admit realizations, which are referred to as graphical. To do so, we start from the Fulkerson theorem , which states the necessary and sufficient graphicality conditions for bi-degree sequences:

**Theorem 1**.  * A sequence of non-negative integer pairs $\mathcal{D}=\left\lbrace\left(k_1^-,k_1^+\right), \left(k_2^-,k_2^+\right), \dotsc, \left(k_N^-,k_N^+\right)\right\rbrace$ with $k_1^-\geqslant k_2^-\geqslant\dotsb\geqslant k_N^-$ is graphical if and only if $$\begin{gathered}
 k_1^- \leqslant N-1,\quad \max_{1\leqslant i\leqslant N}k_i^+\leqslant N-1\label{simple}\:,\\
 \sum_{i=1}^N k_i^- = \sum_{i=1}^N k_i^+\label{handsh}\:,\\
 \sum_{i=1}^x k_i^- \leqslant \sum_{i=1}^x \min\left\lbrace x-1,k_i^+\right\rbrace + \sum_{i=x+1}^N \min\left\lbrace x, k_i^+\right\rbrace\:.\label{interesting}
\end{gathered}$$*

Theorem  can be used to efficiently verify the graphicality of an extracted sequence using the particularly fast implementation described in . The theorem can be intuitively understood by looking at the three conditions. Condition  simply ensures that no node has a number of incoming or outgoing stubs that exceeds the number of other nodes. This is clearly a necessary condition, since each node can connect to or receive connections from at most all the remaining nodes. To understand Condition , notice that the left-hand side is just the number of incoming stubs in the set consisting of the first $x$ nodes. Then, consider how to maximize this number. To start with, take each of the first $x$ nodes and connect them to all the others in the set. However, note that each node $i$ can only have as many outgoing connections as its out-degree $k_i^+$. Thus, if the out-degree of node $i$ is large enough, it can be connected to all the remaining $x-1$ nodes; otherwise, it can only be connected to $k_i^+$ amongst the remaining $x-1$. The first term in the sum on the right-hand side accounts for these connections. The second term in the sum has the same meaning. However, the sum is now taken over the nodes that are *not* within the first $x$. Thus, each can connect to at most $x$, rather than $x-1$, other nodes. Finally, Condition  mandates the total number of incoming stubs equal the total number of outgoing stubs. This introduces an important constraint in the generation of graphical bi-degree sequences. To see why, write the number of incoming and outgoing stubs as $$\sum_{i=1}^N k_i^{\pm} = N\left\langle k^{\pm}\right\rangle\:.$$ In general, the power-law exponents for in-degrees and out-degrees in directed scale-free networks can be different . Then, if the out-degrees scale as ${k^+}^{\gamma^+}$ and the in-degrees as ${k^-}^{\gamma^-}$, for $N\gg1$, it is $\left\langle k^{\pm}\right\rangle \approx \frac{\gamma^\pm-1}{\gamma^\pm-2}$. Thus, one cannot expect the sums of in-degrees and out-degrees to be equal, if the respective power-law exponents are different. In fact, in the region of interest $2<\gamma\leqslant 3$, the variance of power-law distributions is unbounded, since $\left\langle {k^\pm}^2\right\rangle \approx \frac{\gamma^\pm-1}{\gamma^\pm-3}$. Thus, in this range of exponents, one should not expect Condition  to be satisfied even when choosing the same exponent for in-degrees and out-degrees.

To guarantee that Condition  is satisfied, and avoid the trivial non-graphicality of the generated bi-degree sequence, one cannot extract independently the sequences of in-degrees and out-degrees. Rather, one should be extracted without further constraints, and the other should be conditioned to have the same sum as the former. Without loss of generality, assume that $\gamma^+>\gamma^-$. Then, it is $\left\langle k^-\right\rangle>\left\langle k^+\right\rangle$. Thus, freely extracting the out-degrees requires, on average, to lower the mean in-degree with respect to its unconstrained value. This effectively introduces an upper cutoff excluding all the degrees above a certain threshold $N_U$. For a large network, the normalization constant of the in-degree distribution with an upper cutoff is $$Z_{N_U}\equiv\int_1^{N_U} {k^-}^{-\gamma^-}\ \mathrm{dk^-} = \frac{N_U^{1-\gamma^-}\left(N_U^{\gamma^- -1}-1\right)}{\gamma^- -1}\:.$$ Thus, the mean in-degree is $$\label{meanin}
 \left\langle k^-\right\rangle_{N_U} = \int_1^{N_U} \frac{{k^-}^{-\gamma^-+1}}{Z_{N_U}}\ \mathrm{dk^+} = \frac{\gamma^- -1}{\gamma^- -2} N_U \frac{N_U^{\gamma^- -2}-1}{N_U^{\gamma^- -1}-1}\:.$$ Equating Eq.  with the expression for the unconstrained mean out-degree yields $$\label{nu}
 N_U \frac{N_U^{\gamma^- -2}-1}{N_U^{\gamma^- -1}-1} = \frac{\gamma^- -2}{\gamma^- -1} \frac{\gamma^+ -1}{\gamma^+ -2}\:.$$ The solution to Eq. , plotted in Fig. , show that for almost all the choices of $\gamma^-$ and $\gamma^+$, the upper cutoff would eliminate the vast majority of the tail of the degree distribution. As the defining characteristic of scale-free networks is a power-law tail, this indicates that the choice of conditioning the in-degree distribution on the sum of the out-degrees is not suitable for sequence generation.

The other possibility is extracting the in-degrees in an unconstrained way, and conditioning the out-degrees on their sum. This time, the cutoff introduced is a lower cutoff $N_L$ on the out-degree distribution. For $N\gg1$, the out-degree normalization constant with lower cutoff is $$Z_{N_L}\equiv\int_{N_L}^\infty {k^+}^{-\gamma^+}\ \mathrm{dk^+} = \frac{N_L^{1-\gamma^+}}{\gamma^+ -1}\:.$$ Then, the mean out-degree is $$\left\langle k^+\right\rangle_{N_L} = \int_{N_L}^\infty \frac{{k^+}^{-\gamma^++1}}{Z_{N_L}}\ \mathrm{dk^+} = \frac{\gamma^+ -1}{\gamma^+ -2} N_L\:.$$ As the two mean degrees have to be equal, it is $$N_L = \frac{\gamma^- -1}{\gamma^- -2} \frac{\gamma^+ -2}{\gamma^+ -1}\:.$$ Note that, defining the excess exponent $E\equiv \gamma^+ -\gamma^-$, the equation above can be rewritten as $$N_L = \frac{\gamma^- -1}{\gamma^- -2}\left(1-\frac{1}{\gamma^- -1+E}\right)\:.$$ This form explicitly shows that $N_L$ is 1 when the exponents are equal and $E=0$, and it increases monotonically with $E$, towards an asymptotic value of $\left(\gamma^- -1\right)/\left(\gamma^- -2\right)$. As illustrated in Fig. , such cutoff is very mild. Thus, this approach leaves the tail of the distribution entirely untouched. Moreover, for more than half of the region of interest, the whole out-degree distribution has no effective cutoff at all.

Notice that defining a proper method for the generation of power-law distributed directed degree sequences is essential for the accuracy of research outcomes. In fact, approximate techniques have uncontrolled errors and produce results that depend on the details of the approximation made .

# Results and discussion

At the light of the considerations expressed in the previous section, we generated ensembles of bi-degree sequences of random power-law distributed integers with exponents between 2 and 3, conditioning the sequence with the greater exponent on the sum of the sequence with the lower one. Then we tested the sequences for graphicality, and sampled the graphical ones using the direct construction algorithm detailed in Ref. . For each sample, we measured the assortativities using the Pearson coefficients $$r^{\alpha\beta} = \frac{\sum_{i,j}A_{i,j}\Big(k_i^\alpha-\left\langle k^\alpha\right\rangle_L\Big)\left(k_j^\beta-\left\langle k^\beta\right\rangle_L\right)}{\sqrt{\sum_ik_i^\alpha\Big(k_i^\alpha-\left\langle k^\alpha\right\rangle_L\Big)^2}\sqrt{\sum_jk_j^\beta\left(k_j^\beta-\left\langle k^\beta\right\rangle_L\right)^2}}\:,$$ where the averages are taken over all directed links, the elements of the adjacency matrix of the network $A_{i,j}$ are 1 if there is a link from node $i$ to node $j$, and $\alpha$ and $\beta$ can be $-$ or $+$, indicating in-degrees or out-degrees, respectively . We stress that the sampling method used is a degree-based graph construction algorithm . Algorithms in this class can access the entire space of the realizations of a graphical sequence. They work by building the sample graphs via the systematic placement of links, guaranteeing that the graphicality of the sequence is maintained after each step. At every moment, the combinatorially exact probabilities of placing each allowed link are completely determined. Thus, these methods efficiently allow uniform graph sampling, without introducing biases due to a particular choice of generative model or construction algorithm, which can result in overrepresentation or inaccessibility of part of the realization space.

The results indicate the absence of any dependence of the in-in, in-out and out-out coefficients on the choice of power-law exponents. In fact, these three coefficients all vanish within the uncertainties throughout the region studied. Conversely, the out-in coefficient is always negative (Fig. ), indicating disassortative correlation between the out-degree of the node at the beginning of a link and the in-degree of the node at its end. Figure  illustrates this pattern of dependence by plotting the average in-degree $\left\langle k^-\right\rangle_n$ of the neighbours of nodes with a given out-degree $k^+$, for an ensemble of networks with $\gamma^+=\gamma^-=2.01$ and $N=1000$. The values of $\left\langle k^-\right\rangle_n$ decrease quickly and monotonically with $k^+$, confirming the strong disassortative nature of the networks. Our results show substantial similarities between the correlation structure of directed and undirected scale-free networks. Indeed, it is a well-known fact that random undirected scale-free networks are disassortative . Thus, to explain our findings, we use the entropic treatment described in Ref. , extending it to directed networks. To do so, write the information entropy of a given network as $$\label{entropy}
 S = -{\sum}_{i,j}\left[\varepsilon_{i,j}\log\varepsilon_{i,j}+\left(1-\varepsilon_{i,j}\right)\log\left(1-\varepsilon_{i,j}\right)\right]\:,$$ where $\varepsilon_{i,j}$ is the expectation value for the $\left(i,j\right)$ element of the adjacency matrix. To derive an expression for $\varepsilon_{i,j}$ in the case of a given bi-degree sequence, note that it has to satisfy two conditions, namely $$\sum_{j=1}^N \varepsilon_{i,j} = k_i^+$$ and $$\sum_{j=1}^N k_j^-\varepsilon_{i,j} = k_i^+\left\langle k^-\right\rangle_n\left(k_i^+\right)\:.$$ A form that satisfies these conditions is $$\label{closed}
 \varepsilon_{i,j} = \frac{1}{N}\left\lbrace\frac{k_i^+ k_j^-}{\left\langle k\right\rangle} + \int\left(\frac{\sigma_2}{\sigma_{\beta+2}}\delta\left(\nu-\left(\beta+1\right)\right)-\delta\left(\nu-1\right)\right)\left[\frac{\left(k_i^+ k_j^-\right)^\nu}{\left\langle {k^-}^\nu\right\rangle} - {k_i^+}^\nu - {k_j^-}^\nu + \left\langle {k^-}^\nu\right\rangle\right]\mathrm{d\nu}\right\rbrace\:,$$ where $\beta$ is a free parameter. In principle, the factor $\left(\frac{\sigma_2}{\sigma_{\beta+2}}\delta\left(\nu-\left(\beta+1\right)\right)-\delta\left(\nu-1\right)\right)$ in Eq.  can be replaced by any arbitrary function of $\nu$. However, the choice made, in which $\sigma_t\equiv\left\langle {k^-}^t\right\rangle - \left\langle k\right\rangle\left\langle {k^-}^{t-1}\right\rangle$, allows to reproduce the observed dependence of $\left\langle k^-\right\rangle_n$ on $k^+$. Then, computing the integral in Eq. , it is $$\label{epsexp}
 \varepsilon_{i,j} = \frac{1}{N}\left\lbrace\frac{\left\langle {k^-}^2\right\rangle - \left\langle k\right\rangle^2}{\left\langle {k^-}^{\beta+2}\right\rangle - \left\langle k\right\rangle\left\langle {k^-}^{\beta+1}\right\rangle} \left[ \frac{\left(k_i^+ k_j^-\right)^{\beta+1}}{\left\langle {k^-}^{\beta+1}\right\rangle} - {k_i^+}^{\beta+1} - {k_j^-}^{\beta+1} + \left\langle {k^-}^{\beta+1}\right\rangle\right] + k_i^+ + k_j^- - \left\langle k\right\rangle\right\rbrace\:.$$ Using Eqs.  and , we can find the choice of $\beta$ that maximizes $S$, and compute the Pearson coefficient $r^\ast$ corresponding to the maximum entropy network for any given power-law exponents. Notice that in the equations above, we make no distinction between $\left\langle k^+\right\rangle$ and $\left\langle k^-\right\rangle$, as they must be equal to ensure graphicality of the bi-degree sequence. Also, we restrict the parameter search to the values that yield networks without self-edges or multiple edges. To carry out the calculation, we use the degree-maximizing sequence as representative of the scale-free networks for each value of $\gamma$ . Figure  displays a comparison between $r^{+-}$ as measured by simulations and $r^\ast$, in the case of $\gamma^+=\gamma^-$. The two sets of results are substantially in agreement, save for higher values of $\gamma$, where $r^\ast<r^{+-}$. This can be explained by considering that the degree-maximizing sequences used to compute $r^\ast$ feature more high-degree nodes than would be found on average, thus decreasing the assortativity of their realizations.

In summary, we showed that directed scale-free networks are naturally uncorrelated when considering in-in, in-out and out-out correlations. Thus, when looking across a directed link, the in-degree of the originating node has no influence on the in-degree or the out-degree of the target node. Similarly, the out-degrees are not affected by the out-degrees of the neighbours. However, the out-in correlation coefficient is found to be negative throughout the region studied. This indicates that the natural state of directed scale-free networks is one in which nodes of low degree prefer to link to nodes of high degree, and vice versa. The origin of this preference is entropic, as the coefficients found are in good agreement with those corresponding to the maximum information entropy. Thus, the observation of a disassortative directed scale-free network is not sufficient to infer the existence of extra growth mechanisms beyond those responsible for its degree distribution. These results suggest that the disassortative correlations observed in many real-world systems, such as biological and technological networks, do not necessarily arise because of design or evolutionary pressure. In fact, the absence of such drivers, and the resulting randomness, would lead to the observation of the anticorrelated state as the most probable one. Notice that this does not exclude the presence of evolutionary mechanisms, which may certainly be the cause of an observed disassortative network topology in some specific cases. However, their action would have to promote the maximum-entropy state, thus making their presence undetectable from the degree distribution and correlations alone.

# Acknowledgments

The authors would like to thank Alex Arenas for fruitful discussions. OW acknowledges support by the URSS (Undergraduate Research Support Scheme) of the University of Warwick. CIDG acknowledges support by EINS, Network of Excellence in Internet Science, via the European Commission's FP7 under Communications Networks, Content and Technologies, grant No. 288021.

# Figures