August was all about cleaning up details on the Qubic programming language Abra. We finalized many details associated with the syntax of the language and began understanding user experience implementation aspects of the project. The results are promising and cemented our belief that we should be able to show a proof-of-concept Qubic implementation around the end of 2018.

We also started to document the Qubic computational model. The document goes deeper into how an Abra program is translated into an intermediate trit-code representation that we callHandy.The document will also detail how the code will interact with the Qubic Dispatcher, which is the qubic task and event dispatcher on Qubic-enabled nodes.  

Handy encodes the Abra program into a compact trinary representation so that it can easily be packaged into a Qubic transaction and posted on the Tangle. Handy will have done the syntax checking and flagging of any errors in the Abra program before packaging happens, which means that any post-processing tool, like the Qubic Dispatcher, will not have to do that any more.

The Qubic Dispatcher runs on a Qubic-enabled node and will translate the qubic trit-code to its target architecture. The dispatcher will also listen for the events that can trigger the qubics and pass them to those qubics for further processing. The processing of qubic code is tightly interwoven with the dispatcher as the documentation will make clear.

The Qubic computation model document is next in the pipeline. Its two main parts will be about the Abra syntax and about the interaction with the dispatcher.

The other document that should be ready around the same time is the paper on the mathematics behind Qubic. The initial layout is complete and we’re filling in the meat of the sections right now.

Meanwhile, the work on the Abra compiler has been progressing nicely. We are including the latest revisions to the language at the moment, and will finalize the LLVM code generation next. That should allow us to run and test simple, straightforward Abra code. After that the dispatcher will be created and the whole interaction between the code and the dispatcher should allow for much more complex programs to be created and tested.

The FPGA implementation is being researched in parallel so that our design process will always keep that one in mind. Remember that our main focus is on energy-efficient distributed computing for the Internet of Things.

Because of this focus, we have decided to postpone further work on the gateway concept (for moving funds with Qubic) until we have reached the point where we can actually run qubics in their entirety. Such gateways will need a full running system first, to be able to implement and test them.

We have also started looking for additions to the Qubic team because we think the general design is now stable enough. We have identified where people can work on different parts simultaneously, without getting in each other’s way.

All in all it was an exciting month with lots of insights and our enthusiasm for the Qubic project has only grown.

// sample Abra type definitions// define some standard trit lengths we will use in our examples// note that you can define the optimal size for any type's range// to reduce energy requirements accordinglyTrit [1];// -1 to +1Tryte [3];// -13 to +13Short [9];// -9,841 to +9,841Int [27];// -3,812,798,742,493 to +3,812,798,742,493Long [81];// -2.217...e+38 to +2.217...e+38Hash [243];State [729];// convenience type to make code more clear// supposed to always contain a binary boolean value 0 (false) or 1 (true)// should never be null or have the value - (convention not enforced by Abra)Bool [Trit];// here's how to define a named structured trit vector// it consists of the concatenation of all sub-vectors// its size is the sum of all sub-vector sizes// IOTA transaction layoutTransaction {signature [27 * Hash], extradatadigest [Hash], address [Hash], value [Long], issuancetimestamp [Int], timelocklowerbound [Int], timelockupperbound [Int], bundle [Long], trunk [Hash], branch [Hash], tag [Long], attachmenttimestamp [Int], attachmenttimestamplowerbound [Int], attachmenttimestampupperbound [Int], nonce [Long]};// sample Abra look-up tables// these are a look-up tables, or LUTs// a LUT describes for each combination of input trits what the resulting output trits will be// any missing explicitly defined combinations will cause the resulting output to be null// if any input to a LUT is null, the result will be null as well,// so we only need to specify combinations of non-null input values// ************* BINARY OPERATORS *************// LUT logic: binary NOT//return !trit1;not [0 = 1;1 = 0;];// LUT logic: binary AND//return (trit1 & trit2);and [0,0 = 0;0,1 = 0;1,0 = 0;1,1 = 1;];// LUT logic: binary OR//return (trit1 | trit2);or [0,0 = 0;0,1 = 1;1,0 = 1;1,1 = 1;];// LUT logic: binary XOR//return (trit1 ^ trit2);xor [0,0 = 0;0,1 = 1;1,0 = 1;1,1 = 0;];// LUT logic: binary NAND//return !(trit1 & trit2);nand [0,0 = 1;0,1 = 1;1,0 = 1;1,1 = 0;];// LUT logic: binary NOR//return !(trit1 | trit2);nor [0,0 = 1;0,1 = 0;1,0 = 0;1,1 = 0;];// LUT logic: binary XNOR//return !(trit1 ^ trit2);xnor [0,0 = 1;0,1 = 0;1,0 = 0;1,1 = 1;];// ************* TERNARY OPERATORS *************// LUT logic: return the negative value of the input trit//return -trit1;/ note that making an entire trit-vector value negative// can be done by simply negating every trit in the vectorneg [- = 1;0 = 0;1 = -;];// LUT logic: return a boolean indicating whether the two input trits are equal//return (trit1 == trit2);equal [-,- = 1;-,0 = 0;-,1 = 0;0,- = 0;0,0 = 1;0,1 = 0;1,- = 0;1,0 = 0;1,1 = 1;];// LUT logic: return a boolean indicating whether the two input trits are unequal//return (trit1 != trit2);unequal [-,- = 0;-,0 = 1;-,1 = 1;0,- = 1;0,0 = 0;0,1 = 1;1,- = 1;1,0 = 1;1,1 = 0;];// LUT logic: when (boolean) trit1 equals 1 return trit2 else return trit3//return trit1 ? trit2 : trit3;unequal [0,-,- = -;0,-,0 = 0;0,-,1 = 1;0,0,- = -;0,0,0 = 0;0,0,1 = 1;0,1,- = -;0,1,0 = 0;0,1,1 = 1;1,-,- = -;1,-,0 = -;1,-,1 = -;1,0,- = 0;1,0,0 = 0;1,0,1 = 0;1,1,- = 1;1,1,0 = 1;1,1,1 = 1;];// LUT logic: return the 2rd input trit only when 1st input trit equals 1 (true)//return (trit1 == 1) ? trit1 : null;nullOrTrit [1,0 = 0;1,1 = 1;1,- = -;];// sample Abra functions// this is a function that takes a trit vector of State [729],// but we don't need to constantly repeat that size since we// already established the size at the top of the filedigest(state [State]) =// a function returns the value of its last statement,// which in this case is a concatenation (comma operator)// of the input state and the result of the transform() functionstate, transform(state, 81);// note that this function does not need curly braces around its single statement//curly braces are only necessary when grouping multiple statements// construct functions for all predefined types that perform// a similar task to the nullOrTrit LUT for those larger types// note that we have defined each successive type a factor 3 times bigger// which means we can easily define each successive type in terms of the previous one// every nullOrXxx function returns <val> when <t> equals 1 (true), and null otherwise// note that it may seem wasteful to define it this way, but when mapped to FPGA// these can be translated into parallel circuitry that executes them simultaneously.// for other architectures, Abra will allow the Abra functions to be replaced with// a dll function that uses optimal architecture-specific instructions.// we expect a library of common functions to be developed over timenullOrTryte(t [Bool], val [Tryte]) = {// concatenate the 3 separate trits via the LUTnullOrTrit[t, val[0]],nullOrTrit[t, val[1]],nullOrTrit[t, val[2]];};nullOrShort(t [Bool], val [Short]) = {// concatenate the 3 separate trytes via the previous function// note the notation to easily specify a subrange of a trit vector// we will start at N times the size of Tryte and have an open ended range// which specifies to take whatever amount necessary to pass to the underlying parameternullOrTryte(t, val[0 * Tryte..]),nullOrTryte(t, val[1 * Tryte..]),nullOrTryte(t, val[2 * Tryte..]);};nullOrInt(t [Bool], val [Int]) = {// concatenate the 3 separate shorts via the previous functionnullOrShort(t, val[0 * Short..]),nullOrShort(t, val[1 * Short..]),nullOrShort(t, val[2 * Short..]);};nullOrLong(t [Bool], val [Long]) = {// concatenate the 3 separate ints via the previous functionnullOrInt(t, val[0 * Int..]),nullOrInt(t, val[1 * Int..]),nullOrInt(t, val[2 * Int..]);};nullOrHash(t [Bool], val [Hash]) = {// concatenate the 3 separate longs via the previous functionnullOrLong(t, val[0 * Long..]),nullOrLong(t, val[1 * Long..]),nullOrLong(t, val[2 * Long..]);};nullOrState(t [Bool], val [State]) = {// concatenate the 3 separate hashes via the previous functionnullOrHash(t, val[0 * Hash..]),nullOrHash(t, val[1 * Hash..]),nullOrHash(t, val[2 * Hash..]);};// sample old Curl implementation in Abra, just for funtransform(state [State], round [Short]) = {// assume subXxx() is a function that performs subtraction on trit vectors of size X// and also returns a trit vector size XroundMinusOne = sub9(round, 1);// assume equalXxx() is a function that performs comparison on trit vectors of size X// and returns a trit containing a binary boolean value of 0 (false) or 1 (true)roundZero = equal9(roundMinusOne, 0);// calculate the new state for this roundnewState = curlRound(state);// here comes the interesting part, we're going to use a merger operation,// which is an operation that returns the single operand that is non-null// note that a merger will fail when multiple operands are non-null// this line will return newState when roundMinusOne was determined to be zero// and null otherwisestateOut = nullOrState(roundZero, newState);// this line will return newState when roundMinusOne was determined to be non-zero// and null otherwisestateNext = nullOrState(not[roundZero], newState);// this line will only return roundMinusone when it was non-zero and otherwise returns nullroundNext = nullOrShort(not[roundZero], roundMinusOne);// recursively call transform to execute the next round// note that this will only execute if either of stateNext and roundNext are non-null// otherwise it will not execute and a null return value is assumed// so this is essentially a conditional executionstateFinal = transform(stateNext, roundNext);// merge the two branches by returning the one that is non-null// so this is kind of an implied if-then-else, because it will// either return stateOut or stateFinalstateOut \ stateFinal;};// now follows the actual implementation of a single Curl round// LUT logic: curl the 2 input trits onto an output tritcurl [-,- = 1;-,0 = 0;-,1 = -;0,- = 1;0,0 = -;0,1 = 0;1,- = -;1,0 = 1;1,1 = 0;];// a very dumb and straightforward implementation that curls all 729 state tritscurlRound(s [State]) = {// we use the curl LUT for each of the 729 individual pairs of trits,// and concatenate the resulting trits into a 729 trit return valuecurl[s[0], s[364]],curl[s[364], s[728]],curl[s[728], s[363]],// ... snipped 723 more lines like this ...curl[s[366], s[1]],curl[s[1], s[365]],curl[s[365], s[0]];};

Follow us on our official channels for the latest updates:
Discord | Twitter | LinkedIn | Instagram | YouTube |


IOTA Foundation

Official posts from the IOTA Foundation, and migrated posts from old platforms.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.