-- Discrete Markov Chain Sample Example
using io
def main()
n = 2000
niter = 500
M = get_stocastic(n,n) -- initialize the matrix
v = get_distribution(n) -- initialize the vector of distribution
for i = 1 : niter
v = mat_mulvec(M, v) -- compute on the device
end
println(stdout, v)
end
Neblina is a numerical programming language that features a concise/intuitive syntax and an interpreter that transparently runs operations in parallel, exempting the user from the complexities and labor-intensiveness commonly associated with explicit parallel programming.
By relying on OpenCL as the default back-end, Neblina can readily take advantage of multi- and many-core massively parallel processors in a portable way.
Increasing productivity in parallel and high-performance computing is a very active research area. We can mention the project named High Productivity Computing Systems (HPCS) of DARPA, whose one of its goals is to develop a new generation of high-productivity computing. In pursuing this purpose, the project gave rise to the following programming languages: X10, Fortress and Chepel. However, in these languages the user needs to know about parallelism (either task or data parallelism) and only Chepel supports GPU architectures. The common approach to increase the productivity in parallel CPU programing is the use of compile directives and annotations directly into existing sequential codes. Accelerator, Nested and Copperhead use data parallelism model to facilitate the GPU development. Our language differs from these previous approaches since Neblina does not require any user knowledge about parallelism. The most similar approaches to ours are SaC (Single Assignment C), Chesnut and Matlab Jacket. However, these languages have important differences regarding our work. One of the distinctions is that SaC, Chesnut and Jacket generate a CUDA code from a high-level functional language. In this case, only NVIDIA platforms can be used; differently, our language can exploit any OpenCL-supported parallel device.
Download
Neblina is a free open source software and can be used for academic, commercial and other purposes without restrictions.
Neblina is a language focused on establishing a parallel computing layer that requires minimal knowledge of the user about parallel programming. For those who are programming in Neblina, the operations are seen as sequential instructions regardless of the processing architecture or vendor. The Neblina's interpreter sends the data to the parallel processing unit (either a CPU or an accelerator) in a transparent way through the OpenCL parallel API. Another goal is to provide an open environment for straightforward programming of heterogeneous systems focusing on productivity. Users can also extend the language by creating new parallel functions; particularly, operations involving arrays (matrices and vectors), which normally demand great computational effort.
The Neblina programming language aims to be simple and easy to manipulate, but at same time powerful and efficient. Its basic structure is similar to traditional programming languages, such as C, Java and Python. It has dynamic typing that allows greater flexibility in programming (like Python and Lua). The variables do not have types, they have values. We chose not to use models of languages such as LISP or Prolog, as this could discourage non-programmers or professionals from different areas to use Neblina. We aimed at having a simple yet familiar language. The Neblina language is Turing-complete and supports most of the conventional statements (conditional structure, loop iteration and so on). Each statement has an implicit begin and the end of scope is marked with the reserved word end. Our goal was to come up with a language capable to compactly describe algorithms and concepts, being intelligible and pleasant to code. All Neblina programs have a main function (such as C) that indicates the entry point of the program, that is, where the execution starts.
See the automatic generated GNU Bison Automaton Report for a detailed view of Neblina syntax. Below are some Neblina codes to introduce you its syntax:
def main()
n = 4 -- an integer
V = float[3] -- vector of float point variable with 3 elements
M = int[n,n] -- integer matrix 4x4
end
Example of a for-loop and if statement:
using io
def main()
for i = 0 : 10
if (i % 2) == 0:
println( i ) -- print only even numbers from 0 to 10
end
end
end
The same example now using while statement:
using io
def main()
i = 0
while i <= 10:
if (i % 2) == 0:
println( i ) -- print only even numbers from 0 to 10
end
i = i + 1
end
end
Defining functions and getting the returns:
def foo()
return 1, 2, 3
end
def main()
a, b, c = foo()
a, b = b, a -- swap values
end
Neblina Build-in Functions
Here you can find a list of Neblina built-in functions and their respective packages. Functions in the standard package (std) do not need to be explicitly included as these are native ones.
Function
Description
Example
Package
open(filename,mode)
like C/C++ fopen. filename can be a path to file and mode can be read "r", write "w". Return a file descriptor.
f = open("file.txt", "r")
io
close(fdesc)
close a file descriptor
close( f ) -- opened before
io
print([fdesc], str)
print str to file described by fdesc. If fdesc is omitted then it prints to stdout.
print( f, "str" )
io
println([fdesc], str)
The same as print but it breaks the line.
println( "Hello World!" )
io
read([fdesc], var)
Read a variable from file and return the value read.
a = read(f, a)
io
fmtfloat(nplaces)
Set a number of decimal places to float variables
fmtfloat( 2 ); print( 1.123 ) -- will print 1.12
io
sin(value)
compute the sine of a value
r = sin(3.14)
math
cos(value)
compute the cosine of a value
r = cos(2.73)
math
tan(value)
compute the tangent of a value
r = tan(10.73)
math
sinh(value)
compute the hyperbolic sine of a value
r = sinh(3.14)
math
cosh(value)
compute the hyperbolic cosine of a value
r = cosh(2.73)
math
tanh(value)
compute the hyperbolic tangent of a value
r = tanh(10.73)
math
asin(value)
compute the arc sine of a value
r = asin(3.14)
math
acos(value)
compute the arc cosine of a value
r = acos(2.73)
math
atan(value)
compute the arc tangent of a value
r = atan(10.73)
math
deg(value)
convert radians to degrees
r = deg(4.93)
math
rad(value)
convert degrees to radians
r = rad(2.2)
math
log(value[, base])
compute the logarithm of a value given a base
r = log(4,2.71)
math
floor(value)
rounding to floor
r = floor(1.9) -- r is 1
math
ceil(value)
rounding to ceil
r = ceil(1.1) -- r is 2
math
round(value)
rounding to nearest int
r = round(1.8) -- r is 2
math
sqrt(value)
compute the square root of a value
r = sqrt(64)
math
exp(value)
compute e^value
r = exp(4.83)
math
abs(value)
compute the absolute of a value
r = abs(-83.23)
math
seed(value)
initialize pseudo random sequence
seed(738491023)
math
random([init, end])
generate a pseudo random number in the specified range
r = random(0,10)
math
sec()
Returns the number of seconds since 1970-01-01 00:00:00 +0000 (UTC)
r = sec()
time
milli()
Returns the number of milliseconds since 1970-01-01 00:00:00 +0000 (UTC)
r = mili()
time
micro()
Returns the number of microseconds since 1970-01-01 00:00:00 +0000 (UTC)
r = sec()
time
type(v)
return a type as a string of a value
s = type("LNCC")
std
toint(v)
Convert v to int
r=tofloat(1.9203) -- return 1
std
tofloat(v)
Convert v to float
r=tofloat("1.849")
std
tostr(v)
Convert v to string
r=tostr(0x89)
std
c(re,im)
Create a complex number with real part re and complex part re
c = c(1,3.3)
std
real(c)
Return as a float the real part of a complex number
r=real(c)
std
imag(c)
Return as a float the complex part of a complex number
r=imag(c)
std
conj(c)
Return a complex conjugate of a complex number
c=conj(c(1,2))
std
upper(s)
Return the upper case string from s
s=upper("lncc")
std
lower(s)
Return the lower case string from s
s=lower("LNCC")
std
len(s)
Return the size of a string
r=len("LNCC")
std
at(s,v)
Return the char at position v of string s
p=at("LNCC",2) -- return "N"
std
vec_add(v1,v2)
Return the sum of vectors v1+v2
v=vec_add(v1,v2)
std
vec_sub(v1,v2)
Return the subtraction of vectors v1-v2
v=vec_sub(v1,v2)
std
vec_sum(v1)
Return the sum of values in v1
r=vec_sum(v1)
std
vec_dot(v1,v2)
Return the inner product of v1 by v3
r=vec_dot(v1,v2)
std
vec_norm(v1)
Return the L^2 norm of vector v1
r=vec_norm(v1)
std
vec_mulsc(v1,r)
Return the product of v1 by a float r
v=vec_mulsc(v1,r)
std
mat_add(m1,m2)
Return the sum of matrices m1+m2
m=mat_add(m1,m2)
std
mat_sub(m1,m2)
Return the subtraction of matrices m1-m2
m=mat_sub(m1,m2)
std
mat_mul(m1,m2)
Return the multiplication of matrices m1*m2
m=mat_mul(m1,m2)
std
mat_mulvec(m1,v1)
Return the multiplication of a matrix by a vector
v = mat_mulvec(m1,v1)
std
mat_mulsc(m1,r)
Return the product of m1 by a float r
v=mat_mulsc(m1,r)
std
mat_transp(m1)
Return the transpose matrix
m = mat_transp(m1)
std
nrows(m)
Return the number of rows of matrix m
r=nrows(m)
std
ncols(m)
Return the number of columns of matrix m
r=ncols(m)
std
C Integration
The Neblina programming language allows one to add functions written natively in C language and use them. In order add functions written in C it is necessary to first create a package, which is accomplished through helper Neblina functions. Below are listed the Neblina's primitive types:
typedef enum {
T_STRING, // basic string
T_INT, // integer
T_FLOAT, // float point
T_COMPLEX, // complex numbers
T_ADDR, // address
// (for internal byte code use)
T_NDEF, // to inicialize some variable
// (for internal byte code use)
T_CFUNC, // pointer to a C function
// (for internal byte code use)
T_VECTOR, // native neblina vector
T_MATRIX, // native neblina matrix
T_FILE, // file handler
T_ANY // arbitrary type
} data_type;
The Neblina's interpreter provides a way to extend the runtime by supporting the addition of arbitrary sequential or parallel functions.
For instance, the implementation of the function 'neblina_open', which opens a file given its name and the opening mode, has the following C signature:
void ** neblina_open (void ** input, int * status);
The 'input' array holds the input arguments of this function (file name and opening mode).
The 'status' is an output variable that stores the status of the execution, such as success or failure.
The function 'neblina_open' should be able to verify the integrity of the input arguments, for instance, check whether the input file actually exists.
Next, a package called 'io', which will hold this function, is created.
Within that, the types of input and output values are defined, and the C function 'neblina_open' is registered in the package under the alias 'open', which is how it will be called in Neblina:
package_t * getPackage() {
package_t * pkg = package_init( 1, // number of functions in package
"io" ); // name of package
data_type input_type[] = { T_STRING, T_STRING };
data_type output_type[] = { T_FILE };
pkg->functions[0] =
package_new_func("open", // name of function that will be used
2, // number of input parameters
1, // number of output values
input_type, // type of input values
output_type, // type of output values
neblina_open );// callback function that will be called
return pkg;
}
Become a Neblina developer
You can help the Neblina Interpreter become a better software. Please contact pcslara@lncc.br for further information. The source code of Neblina is in the public domain.