Skip to content
Snippets Groups Projects
Commit 8340aa00 authored by Michael Blaschek's avatar Michael Blaschek :bicyclist:
Browse files

added a mojo recipe

parent d795e253
No related branches found
No related tags found
No related merge requests found
*.sif
models/WRF/sandbox.wrf.dev
models/WRF/run
*/.ipynb_checkpoints/*
SFILES := $(wildcard Singularity.*)
BUILD_FLAGS := ${BUILD_FLAGS}
APP_FLAGS := ${APP_FLAGS}
%.sif:%
sudo singularity build $@ $<
sudo singularity $(APP_FLAGS) build $(BUILD_FLAGS) $@ $<
$(addsuffix .sif, $(SFILES)): $(SFILES)
......@@ -11,4 +13,4 @@ list:
@LC_ALL=C $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'
clean:
rm -f *.sif
\ No newline at end of file
rm -f *.sif
# Development Notes
Building a apptainer
```sh
# just run the build script
./build.sh [recipe]
# build an example
./build.sh Singularity.almalinux.base
# run the final container
./Singularity.almalinux.base.sif
```
## run scripts
```sh title='Example run script to source environment vars'
%runscript
for script in /.singularity.d/env/*.sh; do
if [ -f "$script" ]; then
. "$script"
fi
done
if [ $# -eq 0 ]; then
exec /bin/bash --norc "$@"
fi
exec "$@"
```
\ No newline at end of file
Bootstrap: docker
From: ubuntu:20.04
%labels
maintainer IT-IMGW <it.img-wien@univie.ac.at>
%files
./runscript /.singularity.d/runscript
./run-help /.singularity.d/runscript.help
./src/mojokernel.py /modular/pkg/packages.modular.com_mojo/jupyter/kernel/mojokernel2.py
%post
export DEFAULT_TZ=Vienna/Europe
export DEBIAN_FRONTEND='noninteractive'
apt-get update \
&& apt-get install -y \
tzdata \
vim \
curl \
wget \
g++ \
make \
file \
git \
python3-venv \
apt-transport-https \
libedit2
rm -rf /var/lib/apt/lists/*
mkdir -p /modular
export MODULAR_HOME="/modular"
export PATH="$MODULAR_HOME/pkg/packages.modular.com_mojo/bin:$PATH"
curl https://get.modular.com | sh -
apt-get install -y modular
modular --help
modular auth $MOJO_AUTH
modular install mojo
# Cleanup
apt-get -y autoremove --purge
apt-get -y clean
# add a environment script for initalizing
cat > /.singularity.d/env/99-mojo.sh<<EOF
mkdir -p \$HOME/.modular
cp -u /modular/modular.cfg \$HOME/.modular/
if [ ! -d \$HOME/.modular/pkg ]; then
ln -s /modular/pkg \$HOME/.modular/pkg
fi
EOF
# create a jupyter kernel that can be used.
mkdir -p /modular/pkg/packages.modular.com_mojo/venv/share/jupyter/kernels/mojo-jupyter-kernel
cp /root/.local/share/jupyter/kernels/mojo-jupyter-kernel/* /modular/pkg/packages.modular.com_mojo/venv/share/jupyter/kernels/mojo-jupyter-kernel/
cat > /modular/pkg/packages.modular.com_mojo/venv/share/jupyter/kernels/mojo-jupyter-kernel/kernel.json <<EOF
{
"display_name": "Mojo",
"argv": [
"python3",
"/modular/pkg/packages.modular.com_mojo/jupyter/kernel/mojokernel2.py",
"-f",
"{connection_file}"
],
"language": "mojo",
"codemirror_mode": "mojo",
"language_info": {
"name": "mojo",
"mimetype": "text/x-mojo",
"file_extension": ".mojo",
"codemirror_mode": {
"name": "mojo"
}
}
}
EOF
# fix environment for additional packages
sed -i 's/false/true/' /modular/pkg/packages.modular.com_mojo/venv/pyvenv.cfg
# command prompt name
CNAME=u20.mojo
# does not work goes into /.singularity.d/env/91-environment.sh
echo "export PS1=\"[IMGW-$CNAME]\w\$ \"" >> /.singularity.d/env/99-zz-custom-env.sh
# add some labels
echo "libc $(ldd --version | head -n1 | cut -d' ' -f4)" >> "$SINGULARITY_LABELS"
echo "linux $(cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2)" >> "$SINGULARITY_LABELS"
%environment
export LC_ALL=C
export LANG=C.UTF-8
export LIBRARY=/opt/conda/lib:/usr/lib64:/lib64:/lib
export INCLUDE=/opt/conda/include:/usr/include
export MODULAR_HOME="$HOME/.modular"
export PATH="$MODULAR_HOME/pkg/packages.modular.com_mojo/bin:$MODULAR_HOME/pkg/packages.modular.com_mojo/venv/bin:/opt/conda/bin:$PATH"
export SHELL=/bin/bash
# DOCKERFILE:
# FROM ubuntu:20.04
# ENV DEBIAN_FRONTEND='noninteractive'
# RUN apt-get update \
# && apt-get install -y curl g++ make file python3-venv apt-transport-https libedit2\
# && mkdir -p /modular
# ENV LC_ALL=C
# ENV LANG=C.UTF-8
# ENV LIBRARY=/usr/lib64:/lib64:/lib
# ENV INCLUDE=/usr/include
# ENV MODULAR_HOME="/modular"
# ENV PATH="$MODULAR_HOME/pkg/packages.modular.com_mojo/bin:$PATH"
# RUN curl https://get.modular.com | sh - \
# && apt-get install -y modular
# RUN modular auth $MOJO_AUTH \
# && modular install mojo
# # Cleanup
# RUN apt-get -y autoremove --purge\
# && apt-get -y clean
# ===----------------------------------------------------------------------=== #
# Copyright (c) 2023, Modular Inc. All rights reserved.
#
# Licensed under the Apache License v2.0 with LLVM Exceptions:
# https://llvm.org/LICENSE.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===----------------------------------------------------------------------=== #
import benchmark
from complex import ComplexSIMD, ComplexFloat64
from math import iota
from python import Python
from runtime.llcl import num_cores
from algorithm import parallelize, vectorize
from tensor import Tensor
from utils.index import Index
from python import Python
alias float_type = DType.float64
alias simd_width = 2 * simdwidthof[float_type]()
alias width = 960
alias height = 960
alias MAX_ITERS = 200
alias min_x = -2.0
alias max_x = 0.6
alias min_y = -1.5
alias max_y = 1.5
fn mandelbrot_kernel_SIMD[
simd_width: Int
](c: ComplexSIMD[float_type, simd_width]) -> SIMD[float_type, simd_width]:
"""A vectorized implementation of the inner mandelbrot computation."""
let cx = c.re
let cy = c.im
var x = SIMD[float_type, simd_width](0)
var y = SIMD[float_type, simd_width](0)
var y2 = SIMD[float_type, simd_width](0)
var iters = SIMD[float_type, simd_width](0)
var t: SIMD[DType.bool, simd_width] = True
for i in range(MAX_ITERS):
if not t.reduce_or():
break
y2 = y * y
y = x.fma(y + y, cy)
t = x.fma(x, y2) <= 4
x = x.fma(x, cx - y2)
iters = t.select(iters + 1, iters)
return iters
fn main() raises:
let p = Python.import_module("numpy")
let b = Python.import_module("numpy")
let t = Tensor[float_type](height, width)
@parameter
fn worker(row: Int):
let scale_x = (max_x - min_x) / width
let scale_y = (max_y - min_y) / height
@parameter
fn compute_vector[simd_width: Int](col: Int):
"""Each time we operate on a `simd_width` vector of pixels."""
let cx = min_x + (col + iota[float_type, simd_width]()) * scale_x
let cy = min_y + row * scale_y
let c = ComplexSIMD[float_type, simd_width](cx, cy)
t.data().simd_store[simd_width](
row * width + col, mandelbrot_kernel_SIMD[simd_width](c)
)
# Vectorize the call to compute_vector where call gets a chunk of pixels.
vectorize[simd_width, compute_vector](width)
@parameter
fn bench[simd_width: Int]():
for row in range(height):
worker(row)
let vectorized = benchmark.run[bench[simd_width]]().mean()
print("Number of threads:", num_cores())
print("Vectorized:", vectorized, "s")
# Parallelized
@parameter
fn bench_parallel[simd_width: Int]():
parallelize[worker](height, height)
let parallelized = benchmark.run[bench_parallel[simd_width]]().mean()
print("Parallelized:", parallelized, "s")
print("Parallel speedup:", vectorized / parallelized)
_ = t # Make sure tensor isn't destroyed before benchmark is finished
#!/usr/bin/python
# ===----------------------------------------------------------------------=== #
#
# This file is Modular Inc proprietary.
#
# ===----------------------------------------------------------------------=== #
#
# This file contains an implementation of a Jupyter kernel for Mojo. It
# communicates to Mojo using the MojoJupyter API library.
#
# ===----------------------------------------------------------------------=== #
import argparse
import ctypes
import json
import os
import shutil
import time
import traceback
from configparser import ConfigParser
from enum import IntEnum
from pathlib import Path
from typing import Any, Dict, Optional
from ipykernel.kernelapp import IPKernelApp
from ipykernel.kernelbase import Kernel
# ===----------------------------------------------------------------------=== #
# OutputProcessor
# ===----------------------------------------------------------------------=== #
# Special start and end output markers that denote a display message.
display_start = "%%%%%%%DISPLAY_START"
display_end = "%%%%%%%DISPLAY_END"
class ExecutionFinishedState(IntEnum):
"""
Copied from MojoJupyter/Kernel.cpp - models the possible states of a kernel execution.
"""
NotFinished = 0
FinishedSuccess = 1
FinishedError = 2
class OutputProcessor:
"""
Process the output coming from stdout/stderr to find special markers that
indicate specific messages need to be sent.
"""
def __init__(self, kernel: Kernel):
# The kernel object that we are processing output for.
self.kernel = kernel
# The current contents of a pending display message.
self.pending_display_message = None
def send_message(self, name: str, text: str) -> None:
"""
Send a stream message to the client. `name` should be stderr or stderr.
"""
stream_content = {
"name": name,
"text": text,
}
self.kernel.log.info(stream_content)
self.kernel.send_response(
self.kernel.iopub_socket, "stream", stream_content
)
# Create the output callback function. This is called by the MojoJupyter
# library to send output back to the Jupyter client.
def process_output(self, name: bytes, msg: bytes) -> None:
"""Process output from the Mojo kernel."""
msgstr = msg.decode()
def send_stream(text: str) -> None:
self.send_message(name.decode(), text)
# Short-circuit stderr right away - we don't report images or anything
# through stderr, and we should always report it immediately.
if name.decode() == "stderr":
send_stream(msgstr)
return
# If we don't have a pending display message, and we don't have a
# display start marker, then just send the output as a normal stream message.
display_marker_loc = msgstr.find(display_start)
if self.pending_display_message is None and display_marker_loc == -1:
send_stream(msgstr)
return
while len(msgstr) > 0:
# Buffer the display message, sending the correct bits to the stdout.
if self.pending_display_message is None:
# We need this for the case where we've just come back around from sending a display message.
if display_marker_loc == -1:
send_stream(msgstr)
msgstr = ""
continue
# Send the beginning of the message to the stream output.
if display_marker_loc > 0:
send_stream(msgstr[:display_marker_loc])
# Meanwhile, set up for the display message.
self.pending_display_message = ""
msgstr = msgstr[display_marker_loc + len(display_start) :]
continue
# endif self.pending_display_message is None
# Try to find the end marker. If we did find it, add it to the
# pending display message and send it out.
display_marker_loc = msgstr.find(display_end)
if display_marker_loc != -1:
self.pending_display_message += msgstr[:display_marker_loc]
self._send_display_message()
# Reset display_marker_loc to a potential display_start index
# and reset msgstr so things get sent properly.
msgstr = msgstr[display_marker_loc + len(display_end) :]
display_marker_loc = msgstr.find(display_start)
continue
# endif display_marker_loc != -1
self.pending_display_message += msgstr
# If we just completed the display end message, send it out.
display_marker_loc = self.pending_display_message.find(display_end)
if display_marker_loc == -1:
msgstr = ""
continue
self.pending_display_message = self.pending_display_message[
:display_marker_loc
]
# Discard the display end message.
msgstr = self.pending_display_message[
display_marker_loc + len(display_end) :
].rstrip("\r\n")
self._send_display_message()
# Finally, flush the stream from the message.
send_stream(msgstr)
# endwhile len(msgstr) > 0
def _send_display_message(self):
"""Send the current pending display message to the client."""
display_message = json.loads(self.pending_display_message)
self.kernel.send_response(
self.kernel.iopub_socket,
"display_data",
{
"data": display_message[0],
"metadata": display_message[1],
},
)
self.pending_display_message = None
# ===----------------------------------------------------------------------=== #
# MojoKernel
# ===----------------------------------------------------------------------=== #
class MojoKernel(Kernel):
"""A Jupyter kernel for Mojo."""
def __init__(self, **kwargs):
"""Initialize the Mojo kernel.
This loads the MojoJupyter library and starts a kernel repl session.
"""
# Kernel Metadata.
self.implementation = "MojoKernel"
self.implementation_version = "0.1"
self.language = "mojo"
self.language_version = "0.1"
self.language_info = {
"name": "mojo",
"mimetype": "text/x-mojo",
"file_extension": ".mojo",
"codemirror_mode": {"name": "mojo"},
}
self.banner = ""
self.auto_gen_cell_id_count = 0
super(MojoKernel, self).__init__(**kwargs)
# Load the MojoJupyter library, and initialize the result types of the
# functions we use.
self.lib_mojo_jupyter: ctypes.CDLL = self.load_mojo_lib()
self.lib_mojo_jupyter.initMojoKernel.restype = ctypes.c_void_p
self.lib_mojo_jupyter.checkMojoExecutionFinished.restype = ctypes.c_int
# The type of the output callback function. It takes a name and a
# message.
self.output_callback_type: ctypes.CFUNCTYPE = ctypes.CFUNCTYPE(
None,
ctypes.c_char_p,
ctypes.c_char_p,
)
self.output_processor = OutputProcessor(self)
self.output_callback = self.output_callback_type(
lambda name, msg: self.output_processor.process_output(name, msg)
)
self.mojo_kernel: ctypes.c_void_p = (
self.lib_mojo_jupyter.initMojoKernel(
self.output_callback,
ctypes.c_char_p(self.mojoReplExe.encode("utf-8")),
ctypes.c_char_p(None), # lldbInitFile
)
)
if not self.mojo_kernel:
raise RuntimeError("Unable to initialize Mojo kernel.")
def _send_internal_error_message(self):
self.output_processor.send_message(
"stderr",
(
"Internal Mojo Kernel Error\n\nThe Jupyter Notebook encountered"
" an internal error and was unable to evaluate the provided"
" expression. Please report this issue.\n\nMore information"
" about this error can be found in the server error log."
),
)
def __del__(self):
"""Destroy the Mojo kernel."""
self.lib_mojo_jupyter.destroyMojoKernel(self.mojo_kernel)
def load_mojo_lib(self) -> ctypes.CDLL:
"""Load the libMojoJupyter library.
On success, this initializes `mojoReplExe` returns the loaded library.
"""
# Grab the mojo repl executable from the config.
config = ConfigParser()
config.read(Path(os.environ["MODULAR_HOME"]) / "modular.cfg")
mojoReplExePath = Path(
config.get("mojo", "repl_entry_point").rstrip(";")
)
if not mojoReplExePath.exists():
raise RuntimeError(
"Unable to locate `mojo-repl-entry-point` executable."
)
self.mojoReplExe = str(mojoReplExePath)
# Make sure the lib directory is in the path.
libDir = mojoReplExePath.parent
os.environ["PATH"] += os.pathsep + str(libDir)
# Load the MojoJupyter library. This library provides the internal
# implementation of the kernel.
mojoJupyterPath = Path(config.get("mojo", "jupyter_path").rstrip(";"))
if not mojoJupyterPath.exists():
raise RuntimeError("Unable to locate `MojoJupyter` library.")
return ctypes.cdll.LoadLibrary(str(mojoJupyterPath))
def do_execute(
self,
code: str,
silent: bool = False,
store_history: bool = True,
user_expressions: Optional[Dict[str, Any]] = None,
allow_stdin: bool = False,
*,
cell_id: Optional[str] = None,
):
"""Execute a code cell."""
# TODO: Better propagate errors from the kernel execution, process
# provided arguments, etc.
# Wait for the currently running execution to finish.
def wait_for_execution() -> ExecutionFinishedState:
# Wait for the execution to finish.
while True:
# Sleep for a bit to avoid busy spinning while waiting for the
# execution to finish.
time.sleep(0.05)
# Poll the kernel to see if the execution has finished.
result: int = self.lib_mojo_jupyter.checkMojoExecutionFinished(
ctypes.c_void_p(self.mojo_kernel),
)
if result != ExecutionFinishedState.NotFinished:
return ExecutionFinishedState(result)
try:
# jupyter on the cli doesn't provide a cell id, so we need to
# autogenerate one.
if cell_id is None:
cell_id = f"__autogen_cell_id_{self.auto_gen_cell_id_count}"
self.auto_gen_cell_id_count += 1
# Start execution of the expression.
finish_state = self.lib_mojo_jupyter.startMojoExecution(
ctypes.c_void_p(self.mojo_kernel),
ctypes.c_char_p(cell_id.encode("utf-8")),
ctypes.c_char_p(code.encode("utf-8")),
ctypes.c_int(store_history),
)
if finish_state == ExecutionFinishedState.NotFinished:
finish_state = wait_for_execution()
if finish_state == ExecutionFinishedState.FinishedError:
return {"status": "error"}
return {
"status": "ok",
"execution_count": self.execution_count,
"payload": [],
"user_expressions": {},
}
except KeyboardInterrupt:
# Interrupt the current kernel execution.
self.lib_mojo_jupyter.interruptMojoExecution(
ctypes.c_void_p(self.mojo_kernel)
)
wait_for_execution()
# TODO: When Mojo actually has debug info again, we should emit the
# current stack frame here.
return {
"status": "error",
"execution_count": self.execution_count,
}
except:
traceback.print_exc()
self._send_internal_error_message()
return {
"status": "error",
"execution_count": self.execution_count,
}
def do_complete(self, code: str, cursor_pos: int):
"""Find code completions for the given code and cursor position."""
# The type of the completion function, it takes a completion label.
completion_callback_type: ctypes.CFUNCTYPE = ctypes.CFUNCTYPE(
None, ctypes.c_char_p
)
# Build the callback handler used to process completion results.
results = []
completion_callback = completion_callback_type(
lambda result: results.append(result.decode())
)
self.lib_mojo_jupyter.checkMojoCodeComplete(
ctypes.c_void_p(self.mojo_kernel),
ctypes.c_char_p(code.encode()),
ctypes.c_int(cursor_pos),
completion_callback,
)
return {
"matches": results,
"cursor_end": cursor_pos,
"cursor_start": cursor_pos,
"metadata": {},
"status": "ok",
}
if __name__ == "__main__":
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
"--modular-home",
required=False,
help="The value of the env var MODULAR_HOME.",
default=None
)
args, jupyter_args = parser.parse_known_args()
if os.environ.get('MODULAR_HOME') is None:
if args.modular_home is None:
raise Exception("MODULAR_HOME required")
os.environ["MODULAR_HOME"] = args.modular_home
# We pass the kernel name as a command-line arg, since Jupyter gives those
# highest priority (in particular overriding any system-wide config).
IPKernelApp.launch_instance(
argv=jupyter_args + ["--IPKernelApp.kernel_class=__main__.MojoKernel"]
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment