Cuneiform es un lenguaje de flujo de trabajo de código abierto para el análisis de datos científicos a gran escala. [1] [2] Es un lenguaje de programación funcional de tipado estático que promueve la computación paralela . Cuenta con una interfaz de función externa versátil que permite a los usuarios integrar software de muchos lenguajes de programación externos. A nivel organizacional, Cuneiform proporciona facilidades como ramificación condicional y recursión general, lo que lo hace Turing-completo . En esto, Cuneiform es el intento de cerrar la brecha entre los sistemas de flujo de trabajo científicos como Taverna , KNIME o Galaxy y los modelos de programación de análisis de datos a gran escala como MapReduce o Pig Latin al tiempo que ofrece la generalidad de un lenguaje de programación funcional.
Cuneiform se implementa en Erlang distribuido . Si se ejecuta en modo distribuido, controla un sistema de archivos distribuido compatible con POSIX como Gluster o Ceph (o una integración FUSE de algún otro sistema de archivos, por ejemplo, HDFS ). Alternativamente, los scripts de Cuneiform se pueden ejecutar sobre HTCondor o Hadoop . [3] [4] [5] [6]
Cuneiform está influenciado por el trabajo de Peter Kelly, quien propone la programación funcional como modelo para la ejecución del flujo de trabajo científico. [7] [8] En esto, Cuneiform se distingue de los lenguajes de flujo de trabajo relacionados basados en la programación de flujo de datos como Swift . [9]
Integración de software externo
Las herramientas y bibliotecas externas (por ejemplo, bibliotecas R o Python ) se integran a través de una interfaz de función externa . En esto se parece, por ejemplo, a KNIME , que permite el uso de software externo a través de nodos snippet, o a Taverna , que ofrece servicios BeanShell para integrar software Java . Al definir una tarea en un lenguaje extranjero, es posible utilizar la API de una herramienta o biblioteca externa. De esta manera, las herramientas se pueden integrar directamente sin la necesidad de escribir un contenedor o reimplementar la herramienta. [10]
Los lenguajes de programación extranjeros admitidos actualmente son:
Se planea añadir soporte para idiomas extranjeros para AWK y gnuplot .
Sistema de tipos
Cuneiform proporciona un sistema de tipos simple y con comprobación estática. [11] Si bien Cuneiform proporciona listas como tipos de datos compuestos , omite los accesores de listas tradicionales (cabeza y cola) para evitar la posibilidad de errores de ejecución que podrían surgir al acceder a la lista vacía. En cambio, se accede a las listas de una manera de todo o nada simplemente mapeándolas o plegándolas. Además, Cuneiform omite (a nivel organizativo) las operaciones aritméticas que excluyen la posibilidad de división por cero. La omisión de cualquier operación parcialmente definida permite garantizar que los errores de ejecución puedan surgir exclusivamente en código externo.
Tipos de datos base
Como tipos de datos básicos, Cuneiform ofrece valores booleanos, cadenas y archivos. En este caso, los archivos se utilizan para intercambiar datos en formato arbitrario entre funciones externas.
Registros y coincidencia de patrones
Cuneiform proporciona registros (estructuras) como tipos de datos compuestos. El ejemplo siguiente muestra la definición de una variable rque es un registro con dos campos a1y a2, siendo el primero una cadena y el segundo un valor booleano.
sea r : < a1 : Str , a2 : Bool > =
< a1 = "mi cadena" , a2 = verdadero >;
Se puede acceder a los registros mediante proyección o mediante comparación de patrones . El ejemplo siguiente extrae los dos campos a1y a2del registro r.
sea a1 : Str = ( r | a1 );
sea < a2 = a2 : Bool > = r ;
Listas y procesamiento de listas
Además, Cuneiform ofrece listas como tipos de datos compuestos. El ejemplo siguiente muestra la definición de una variable xscomo una lista de archivos con tres elementos.
sea xs : [ Archivo ] = [ 'a.txt' , 'b.txt' , 'c.txt' : Archivo ];
Las listas se pueden procesar con los operadores for y fold. En este caso, se pueden asignar al operador for varias listas para que las utilice elemento por elemento (similar a lo que ocurre enfor/list Racket , Common mapcarLisp o Erlang zipwith) .
El siguiente ejemplo muestra cómo realizar un mapeo sobre una sola lista, siendo el resultado una lista de archivos.
para x <- xs hacer proceso - uno ( arg1 = x ) : Fin del archivo ;
El siguiente ejemplo muestra cómo comprimir dos listas y el resultado también es una lista de archivos.
para x <- xs , y <- ys hacer proceso - dos ( arg1 = x , arg2 = y ) : Fin del archivo ;
Por último, las listas se pueden agregar mediante el operador de plegado. El siguiente ejemplo resume los elementos de una lista.
pliegue acc = 0, x <- xs hacer
suma( a = acc, b = x )
fin;
Ejecución paralela
Cuneiform es un lenguaje puramente funcional, es decir, no admite referencias mutables . En consecuencia, puede utilizar la independencia de subtérminos para dividir un programa en porciones paralelizables. El planificador de Cuneiform distribuye estas porciones a los nodos de trabajo. Además, Cuneiform utiliza una estrategia de evaluación de llamada por nombre para calcular valores solo si contribuyen al resultado del cálculo. Finalmente, las aplicaciones de funciones externas se memorizan para acelerar los cálculos que contienen resultados derivados previamente.
Por ejemplo, el siguiente programa cuneiforme permite que las aplicaciones de fy gse ejecuten en paralelo mientras que hes dependiente y puede iniciarse solo cuando ambos fy ghayan finalizado.
deje que la salida de f sea: Archivo = f(); deje que la salida de g: Archivo = g(); h( f = salida-de-f, g = salida-de-g );
El siguiente programa cuneiforme crea tres aplicaciones paralelas de la función fal mapearla fsobre una lista de tres elementos:
sea xs : [Archivo] = ['a.txt', 'b.txt', 'c.txt' : Archivo]; para x <- xs hacer f(x = x) : Archivo fin;
De manera similar, las aplicaciones de fy gson independientes en la construcción del registro ry, por lo tanto, pueden ejecutarse en paralelo:
sea r : < a : Archivo , b : Archivo > = & lt ; a = f (), b = g () & gt ;;
Ejemplos
Un guión de hola mundo:
def saludo ( persona : Str ) -> < salida : Str > en Bash * { salida = "Hola $persona" } *
( saludar ( persona = "mundo" ) | salir );
Este script define una tarea greeten Bash que antepone "Hello "a su argumento de cadena person. La función produce un registro con un solo campo de cadena out. Al aplicar greet, vincular el argumento persona la cadena "world"produce el registro <out = "Hello world">. Al proyectar este registro a su campo outse evalúa la cadena "Hello world".
Las herramientas de línea de comandos se pueden integrar definiendo una tarea en Bash :
def samtoolsSort ( bam : Archivo ) - > < sorted : Archivo > en Bash * { sorted = sorted.bam samtools sort - m 2 G $bam - o $ sorted } *
En este ejemplo se define una tarea samtoolsSortque llama a la herramienta SAMtools , que consume un archivo de entrada en formato BAM y produce un archivo de salida ordenado, también en formato BAM.
Historial de versiones
En abril de 2016, el lenguaje de implementación de Cuneiform cambió de Java a Erlang y, en febrero de 2018, su principal plataforma de ejecución distribuida pasó de Hadoop a Erlang distribuido. Además, entre 2015 y 2018, HTCondor se mantuvo como plataforma de ejecución alternativa.
La sintaxis superficial del sistema cuneiforme fue revisada dos veces, como se refleja en el número de versión principal.
Versión 1
En su primer borrador publicado en mayo de 2014, Cuneiform estaba estrechamente relacionado con Make en el sentido de que construía un gráfico de dependencia de datos estáticos que el intérprete recorría durante la ejecución. La principal diferencia con las versiones posteriores era la falta de condicionales, recursión o comprobación de tipos estática. Los archivos se distinguían de las cadenas yuxtaponiendo valores de cadena entre comillas simples con una tilde ~. La expresión de consulta del script se introducía con la targetpalabra clave. Bash era el lenguaje extranjero predeterminado. La aplicación de funciones tenía que realizarse utilizando un applyformulario que tomaba taskcomo primer argumento la palabra clave. Un año después, esta sintaxis superficial fue reemplazada por una versión simplificada pero similar.
El siguiente script de ejemplo descarga un genoma de referencia desde un servidor FTP.
declarar download-ref-genome;
deftask descargar-fa( fa : ~ruta ~id ) *{
wget $ruta/$id.fa.gz
cierre de pistola $id.fa.gz
mv $id.fa $fa
}*
ref-genome-path = ~'ftp://hgdownload.cse.ucsc.edu/goldenPath/hg19/chromosomes';
ref-genoma-id = ~'chr22';
ref-genoma = aplicar(
tarea: descargar-fa
ruta: ruta-del-genoma-de-referencia
id: ref-genoma-id
);
genoma de referencia objetivo;
Versión 2

El segundo borrador de la sintaxis de superficie de Cuneiform, publicado por primera vez en marzo de 2015, se mantuvo en uso durante tres años, después de la transición de Java a Erlang como lenguaje de implementación de Cuneiform. La evaluación difiere de los enfoques anteriores en que el intérprete reduce una expresión de consulta en lugar de recorrer un gráfico estático. Durante el tiempo en que la sintaxis de superficie se mantuvo en uso, el intérprete se formalizó y simplificó, lo que dio como resultado una primera especificación de la semántica de Cuneiform. La sintaxis incluía condicionales. Sin embargo, los booleanos se codificaban como listas, reciclando la lista vacía como booleana falsa y la lista no vacía como booleana verdadera. La recursión se agregó más tarde como un subproducto de la formalización. Sin embargo, la verificación de tipos estáticos se introdujo solo en la versión 3.
El siguiente script descomprime un archivo comprimido y lo divide en particiones de tamaño uniforme.
deftask unzip( <out(Archivo)> : zip(Archivo) ) en bash *{
descomprimir -d dir $zip
salida=`ls dir | awk '{imprimir "dir/" $0}'`
}*
deftask split( <out( Archivo )> : archivo( Archivo ) ) en bash *{
dividir -l 1024 $archivo txt
salida=txt*
}*
sotu = "sotu/estadodelaunión1790-2014.txt.zip";
fileLst = split( archivo: descomprimir( zip: sotu ) );
archivoLst;
Versión 3
La versión actual de la sintaxis de superficie de Cuneiform, en comparación con los borradores anteriores, es un intento de cerrar la brecha con los lenguajes de programación funcional convencionales. Presenta un sistema de tipos simple, comprobado estáticamente e introduce registros además de listas como un segundo tipo de estructura de datos compuesta. Los booleanos son un tipo de datos base independiente.
El siguiente script descomprime un archivo generando una lista de archivos.
def untar(tar: Archivo) -> <fileLst: [Archivo]>
en bash *{
alquitrán xf $tar
archivoLst=`tar tf $tar`
}*
deje que hg38Tar : Archivo =
'hg38/hg38.tar';
let <archivoLst = rápido: [Archivo]> =
descomprimir(tar = hg38Tar);
faLst;
Referencias
- ^ "Joergen7/Cuneiforme". GitHub . 14 de octubre de 2021.
- ^ Brandt, Jörgen; Bux, Marc N.; Leser, Ulf (2015). "Cuneiforme: un lenguaje funcional para el análisis de datos científicos a gran escala" (PDF) . Actas de los talleres de la EDBT/ICDT . 1330 : 17– 26.
- ^ "Análisis de datos escalable en varios idiomas en Beam: la experiencia cuneiforme de Jörgen Brandt". Erlang Central . Archivado desde el original el 2 de octubre de 2016. Consultado el 28 de octubre de 2016 .
- ^ Bux, Marc; Brandt, Jörgen; Lipka, Carsten; Hakimzadeh, Kamal; Dowling, Jim; Leser, Ulf (2015). "SAASFEE: motor de ejecución de flujo de trabajo científico escalable" (PDF) . Actas de la Fundación VLDB . 8 (12): 1892– 1895. doi :10.14778/2824032.2824094.
- ^ Bessani, Alysson; Brandt, Jörgen; Bux, Marc; Cogo, Vinicius; Dimitrova, Lora; Dowling, Jim; Gholami, Ali; Hakimzadeh, Kamal; Hummel, Michael; Ismail, Mahmoud; Laure, Erwin; Leser, Ulf; Litton, Jan-Eric; Martinez, Roxanna; Niazi, Salman; Reichel, Jane; Zimmermann, Karin (2015). "Biobankcloud: una plataforma para el almacenamiento, el intercambio y el procesamiento seguros de grandes conjuntos de datos biomédicos" (PDF) . Primer taller internacional sobre gestión y análisis de datos para la medicina y la atención sanitaria (DMAH 2015) .
- ^ "Análisis de datos escalable en varios idiomas en Beam: la experiencia cuneiforme". Erlang-factory.com . Consultado el 28 de octubre de 2016 .
- ^ Kelly, Peter M.; Coddington, Paul D.; Wendelborn, Andrew L. (2009). "El cálculo lambda como modelo de flujo de trabajo". Concurrencia y computación: práctica y experiencia . 21 (16): 1999– 2017. doi :10.1002/cpe.1448. S2CID 10833434.
- ^ Barseghian, Derik; Altintas, Ilkay; Jones, Matthew B.; Crawl, Daniel; Potter, Nathan; Gallagher, James; Cornillon, Peter; Schildhauer, Mark; Borer, Elizabeth T.; Seabloom, Eric W. (2010). "Flujos de trabajo y extensiones del sistema de flujo de trabajo científico Kepler para respaldar el acceso y análisis de datos de sensores ambientales" (PDF) . Informática ecológica . 5 (1): 42– 50. doi :10.1016/j.ecoinf.2009.08.008. S2CID 16392118.
- ^ Di Tommaso, Paolo; Chatzou, María; Floden, Evan W; Barja, Pablo Prieto; Palombo, Emilio; Notredame, Cedric (2017). "Nextflow permite flujos de trabajo computacionales reproducibles". Biotecnología de la Naturaleza . 35 (4): 316– 319. doi :10.1038/nbt.3820. PMID 28398311. S2CID 9690740.
- ^ "Implementación de un lenguaje de flujo de trabajo funcional en Erlang" (PDF) . Consultado el 28 de octubre de 2016 .
- ^ Brandt, Jörgen; Reisig, Wolfgang; Leser, Ulf (2017). "Semántica computacional del lenguaje de flujo de trabajo científico funcional Cuneiform". Revista de programación funcional . 27 . doi :10.1017/S0956796817000119. S2CID 6128299.