Intro
Zeszyt interaktywny to aplikacja przeglądarkowa do tworzenia dokumentów łączących kod na żywo z tekstem narracyjnym, równaniami i wizualizacjami.
Jupyter jest to
otwarto-źródłowe narzędzie do tworzenia interaktywnych "zeszytów", w których można z poziomu przeglądarki wpisywać kod źródłowy z możliwością jego uruchomienia z funkcjami min. rysowania obrazów, wykresów, widgetów z poziomu przeglądarki, czy wręcz map. Są dostępne frontendy
Jupyter console i
Qt Console for Jupyter, które teoretycznie powinny działać z każdym kernelem.
Karnele C++ dla Jupyter'a
Narzędzie to aby móc wykonywać kod w danym języku programowania musi mieć zainstalowane odpowiednie środowisko uruchomieniowe, tzw. kernel. Wraz z Jupyterem jest instalowany domyślny kernel
IPython obsługujący język Python. Na szczęście Jupyter nie jest ograniczony jedynie do tegoż języka programowania i powstało wiele
kerneli do różnych języków programowania, a jeśli jakiegoś języka to osoby ambitne mogą dodać własny kernel. Portal ten jest poświęcony C++, dlatego skupię się na kernelach tego języka, których na moment pisania artykułu, istnieją trzy:
Wszystkie wymienione powyżej kernele bazują na
CLingu, który jest interpreterem opartym na C++. W wyborze pomaga wpis na
oficjalnej stronie standardu C++ z 2017 roku, gdzie został wymieniony
artykuł opisujący użycie kernela
xeus-cling wraz z innymi przykładami. Przekonujące jest też to, że xeus-cling można sobie
przetestować w przeglądarce jeszcze bez jakiejkolwiek instalacji. A już najlepszym argumentem za wyborem tego kernela jest
dostępna dokumentacja i kilka artykułów dlatego też wybieram właśnie jego.
Instalacja i pierwsze uruchomienie
Wersje Jupytera
Na
oficjalnej stronie projektu Jupyter jest kilka wariantów instalacji (wszystkie open-source i wszystkie można zainstalować):
Na start wybierzemy zeszyty jedno osobowe, w oparciu o opisane na
StackOverflow różnice między Jupyter Notebook a JupyterLab skupię się na nowszym JupyterLab. Decyzja o wyborze wariantu nie jest decyzją permanentną, bo wg
informacji z artykułu wiemy, że na jednym komputerze można mieć zainstalowany zarówno
JupyterLab jak i
Jupyter Notebook, a nawet JupyterHub. Dodam, że przed instalacją można sobie
przetestować w przeglądarce wraz z wsparciem dla C++
Jupyter Notebook lub
JupyterLab. A tworząc zeszyt dla C++ mamy możliwość wyboru różnych standardów C++.
Instalacja JupyterLab wraz z kernelem xeus-cling
Wg
instrukcji można go zainstalować przy pomocy wielu narzędzi, m.in.:
pip
,
conda
,
pipenv
, lub skorzystać z gotowych obrazów dockera.
Z drugiej strony aby zainstalować kernel
xeus-cling obsługujący C++, podany jest sposób przy użyciu narzędzia
miniconda (ale nie
anaconda, która jest odradzana ze względu na gabaryty i konflikt między pakietami), dlatego też z listy sposobów instalacji wybiorę
conda
.
Po wejściu na stronę
dokumentacji microcondy znajdujemy odpowiedni plik dla naszego systemu. Ja testowałem na
instalacje na linuxie, dlatego też dokonałem pobrania pliku i instalacji w następujący sposób:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
ale dla innych systemów sposób instalacji będzie inny.
Wiele z później opisanych dodatków ma zalecany sposób instalacji przez narzędzie
mamba, które jest wg ichniejszej dokumentacji "lepszą alternatywą dla conda", która pierwotnie była nakładką na condę. Dlatego też można od razu zainstalować mambę:
conda install mamba -n base -c conda-forge
a następnie będziemy mogli używać
mamba
w zastępstwie do
conda
do instalacji pakietów. Dodam jedynie, że aktywacja środowiska odbywa się w dalszym ciągu przez condę)
Mając już zainstalowanego ww. managera pakietów wystarczy zawołać jedną komendę, ale wpierw zalecają uruchomienie środowiska izolowanego:
conda create -n cling
conda activate cling
Następnie w tym izolowanym środowisku wpisać:
conda install xeus-cling jupyterlab -c conda-forge
Obecnie ww. kernel,
wg instrukcji jest wspierany wyłącznie dla systemu Linux. Jak ktoś takiego nie posiada pozostaje mu spróbowanie poniższych kroków w ramach dockera.
Uruchamianie jupytera
Wykonawszy powyższe kroki mamy już od razu jupytera z wsparciem do C++, możemy go uruchomić komendą:
jupyter-lab
jeśli instalowaliśmy go w środowisku izolowanym powinniśmy wpierw wpisać:
conda activate cling
.
po wpisaniu tej komendy uruchamia się nam przeglądarka (
http://localhost:8888/lab) i od razu możemy działać zarówno w języku Python, jak i C++ w różnych wersjach, na ten moment: C++11, C++14 i C++17, mamy też dostępny terminal z poziomu przeglądarki i przeglądarkę plików.
Czyli zobaczymy coś takiego:
Czy to bezpieczne?
Jak szybko zauważymy narzędzie ma dostęp do plików, może je edytować, ale dostęp ma jedynie do plików katalogu w którym je uruchomimy, oraz do podkatalogów. Dlatego niepowołany użytkownik nam nie dostanie się do innych plików. Kolejnym zabezpieczeniem jest fakt, że po uruchomieniu Jupytera jest otwierana przeglądarka z odpowiednim tokenem, więc chociażbyśmy weszli na adres jupytera z innego komputera, lub przeglądarki, to bez tokena nie uda się nam zalogować. Mamy też możliwość wygenerowania hasła, którego będziemy mogli używać zamiast tokena.
Jupiter po polsku?
Do Jupytera dostępne są
przygotowane paczki językowe, niestety na ten moment języka polskiego nie ma. Jakby był to instalowali byśmy go zapewne komendą:
conda install -c conda-forge jupyterlab-language-pack-pl-PL
może się podejmiesz, drogi Czytelniku?
Pisanie programów C++ w jupyterze - podstawy
Wpierw należało by otworzyć nowy zeszyt (ang. "notebook"), tworząc go mamy możliwość wybrania standardu C++, ja będę przedstawiał przykłady używając C++17. Jak już utworzymy nowy zeszyt z menu, zobaczymy, że ma on rozszerzenie
.ipynb
. Pojawi się nam też pierwsza komórka. Kod programu wprowadza się w tzw. "komórkach", chociaż osoby o naturze hardcora mogą też pisać czystym tekstem, co jest odradzane. Komórki z kodem utrzymują kontekst między sobą w ramach całego zeszytu, dlatego wartość raz prowadzona do zmiennej dostępna jest w innych komórkach. Dla każdej komórki mamy do wyboru kod, lub język
Markdown (w ramach komórek typu Markdown możemy tworzyć wzory przy pomocy
LaTeX).
Oto przykładowy zeszyt w Jupyterze zawierający zaimplementowane komórki tekstowe Markdown, kodu, oraz wzór matematyczny wpisany w języku Latex. Rozwinięto listę rodzajów typów komórek.
W przykładzie wykorzystano następujące typu komórek:
1) Poniższy tekst sformatowany wpisano do pierwszej komórki Markdown:
# Nauka Jupytera
Nie jest to takie **trudne**.
## Nauka w C++
Poniżej przykładowy kod:
2) kod źródłowy C++ wykonano wpisując "czystą" treść programiku w C++ do bloku CODE
3) przykładowe równanie matematyczne opisano w języku LaTeX w komórce typu Markdown:
$$c = \sqrt{ a^{2}+b^{2}}$$
Z istotnych rzeczy - komórki dodajemy znakiem plusa, widocznym na powyższym obrazku na górze z prawej. W komórce możemy pisać tak długo dopóki nie przeskoczymy do innej komórki (co może być kliknięciem myszą, bo ENTER domyślnie nie przeskakuje). Wykonać komórkę, czyli zamienić tekst na kod w przypadku języka markdown lub wykonać kod źródłowy robimy przez kombinacje klawiszy: SHIFT + ENTER (alternatywnie przycisk PLAY nad komórkami). Typ komórki między markdown a code możemy zmienić albo z menu nad zeszytem, które na obrazku jest rozwinięte, albo mając zaznaczoną komórkę (zaznaczamy klikając myszką na lewo od komórki) przyciskami:
Y - kod, oraz
M - markdown. Z bardzo przydatnych rzeczy polecam komendę: CTRL + SHIFT + C, które aktywuje paletę komend, na której pojawiają się komendy wyszukiwane tekstowo, wraz ze skrótami. Jak ktoś ma słabą pamięć lub inne skróty może sobie paletę komend wyklikać z menu:
View -> Activate Command Palette. Do rozpoczęcia używania zeszytów wystarczy nam jeszcze poznanie
dokumentacji języka Markdown (zaimplementowano go m.in. Github, Gitlab, StackOverflow, Telegram). Jeśli ktoś chce korzystać ze wzorów odsyłam do materiałów poświęconym
wyrażeniom matematycznym w LaTeX (język używany powszechniew pracach naukowych z silnym wsparciem zapisu wyrażeń matematycznych). Poza powyższym zachęcam aby sobie jeszcze przeklikać interfejs, aby lepiej zaprzyjaźnić się ze środowiskiem. Jak już ktoś opanuje Jupytera, polecam dostępne skróty z menu kontekstowego
Settings -> Advanced settings editor -> Keyboard Shortcuts.
Pisanie w jupyterze w C++
Po pierwsze odbywa się interpretacja języka C++, a nie kompilacja, co prawda jest on zrobiony na bazie Clanga i LLVM, no ale zawsze jest to bardziej jak skrypt. Dlatego też nie piszemy funkcji
int main()
, tylko wszystko piszemy w "globalnym mainie", chociaż możemy tworzyć funkcje/przestrzenie nazw, a nawet używać szablonów, czy zewnętrznych bibliotek. Ważną rzeczą jest fakt, że nie cały kod w zeszycie się uruchamia na żądanie, jedynie aktywna komórka/komórki, której/których uruchomienia zażądamy. Kolejnym istotnym szczegółem jest kontekst wszystkich wykonanych kodów, trzymany między komórkami. Wyczyścić go można przyciskiem na górze "Restart the kernel".
Pisząc w C++ normalnie błędy kompilacji są wykrywane, co widać na poniższym obrazku:
Na wieść o istnieniu interpretera C++ u doświadczonych programistów pojawia się podejrzenie, że ktoś sobie z nas robi żarty. Doświadczenie całkiem nas nie myli, gdyż czasami nie działają pewne fukcjonalności, które powinny działać. Często również komunikat o błędzie nie jest intuicyjny. Przykładowo w momencie pisania tego artykułu natrafiłem na problemy z
std::endl
, które na moje oko nie powinny wystąpić. Na błędy czasami pomaga podział komórki (jak mamy dwie funkcje w jednej komórce to mając błąd
input_line_28:12:1: error: function definition is not allowed here
możemy go zwalczyć przez podział komórki i wykonanie ich oddzielnie niż jednej. Czasami też pomaga restart kerneli, jednakże wtedy tracimy kontekst z wykonania poprzednich komórek.
Dobrą wiadomością jest z kolei fakt, że większość rzeczy działa, w tym szablony, dlatego jest to dobre narzędzie do prowadzenia szkoleń czy nauki C++.
Wypisywanie obiektów
Jest to jedna z zalet interaktywnych zeszytów, dostępna nie tylko w Pythonie. Jeśli obiekt jest typu pierwotnego lub sekwencją (czyli coś co ma metody
begin()
o
end()
) składającą się z obiektów pierwotnych to można w łatwy sposób wyświetlić zawartość przez (o zgrozo!) pominięcie średnika w ostatniej instrukcji w komórce np.:
#include <vector>
#include <iostream>
using namespace std;
std::vector < int > v { 10, 4, 23, 4, 5 };
v
Spowoduje wypisanie:
{ 10, 4, 23, 4, 5 }
Co ciekawe, powyższy mechanizm działa tylko na ostatnią instrukcję w komórce, gdy zrobimy to na instrukcji nieostatniej to pojawi się znany nam wszystkim błąd:
#include <vector>
#include <iostream>
using namespace std;
std::vector < int > v { 10, 4, 23, 4, 5 };
v
int i = 44;
i
i dla powyższego kodu ujrzymy komunikat:
input_line_24:4:2: error: expected ';' after expression
v
^
;
Interpreter Error:
Z kolei jeśli chcieli byśmy skorzystać z "tradycyjnego" mechanizmu wyświetlania funkcją powinniśmy użyć funkcji
xcpp::display()
, która jest dostępna po włączeniu:
#include <xcpp/xdisplay.hpp>
, czego zastosowanie widać w poniższym kodzie:
#include <vector>
#include <iostream>
#include <xcpp/xdisplay.hpp>
using namespace std;
std::vector < int > v { 10, 4, 23, 4, 5 };
xcpp::display( v );
int i = 44;
xcpp::display( i );
I wydruk:
{ 10, 4, 23, 4, 5 }
44
Biblioteki zewnętrzne
Jakiż to zysk byłby z pisania w C++ na Jupyterze gdyby się nie dało użyć zewnętrznych bibliotek. Jest to możliwe w
xeus-cling
dzięki następującym instrukcjom:
#pragma cling add_include_path("sciezka/do/naglowkow")
#pragma cling add_library_path("sciezka/do/libow")
#pragma cling load("sciezka/do/bibliotek/dynamicznych")
Przykładowo użyjmy biblioteki
ranges-v3, a poniżej przykład
sumowania liczb (pobrany + dostosowany z repozytorium renges-v3):
#pragma cling add_include_path("/home/agh/Pulpit/range-v3/include/")
#include <iostream>
#include <vector>
#include <range/v3/numeric/accumulate.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/take.hpp>
#include <range/v3/view/transform.hpp>
using std::cout;
using namespace ranges;
int sum = accumulate( views::ints( 1, unreachable ) | views::transform([ ]( int i ) {
return i * i;
} ) | views::take( 10 ),
0 );
cout << "sum: " << sum << '\n';
W odpowiedzi wypisywane jest:
sum: 385
Dla bibliotek muszą być podane wszystkie ścieżki do includów, jak i do bibliotek (
przykład ze stacka,
przykład z OpenCV). Zasygnalizowany błąd może być jeszcze mniej intuicyjny niż te, które sygnalizuje kompilator C++ (jak widać da się przebić poziom niejasności kompilatora).
Kolejną moją obserwacją jest, że po dodaniu ścieżki do nowej biblioteki wykonanie kodu może nie zadziałać od razu (includując
gtesta zdarzyło mi się, że musiałem 2 sekundy poczekać), dlatego w razie błędu koniecznym może okazać się ponowne wykonanie komórek używających danej biblioteki.
Po co się męczyć - instalacja bibliotek przez conda
Instalujemy
jupyter
przez
conda
, więc możemy też użyć tegoż mechanizmu do instalacji bibliotek, o ile takowe są dostępne w ramach tegoż managera pakietów. Przykładowo dla biblioteki
boost
, czy
poco
możemy zawołać:
conda install boost -c conda-forge
conda install poco -c conda-forge
Wtedy nie musimy podawać ścieżek do nagłówków, jednakże do bibliotek skompilowanych już tak. Przykładowo poniżej zmodyfikowany kod z artykułu o
obsłudze plików ZIP z poziomu C++:
#pragma cling load("libPocoZip.so")
#include <iostream>
#include <fstream>
#include <Poco/Zip/Compress.h>
const char * outputZipFileName = "paczka.zip";
std::ofstream outputZipArchive( outputZipFileName, std::ios::binary );
Poco::Zip::Compress compressor( outputZipArchive, true );
const char * fileToZip = "plikKtoryMusiIstniec.txt";
compressor.addFile( fileToZip, fileToZip );
compressor.close();
outputZipArchive.close();
std::cout << "Archiwum wygenerowano: " << fileToZip << '\n';
Jak wszystko dobrze zrobimy to archiwum ZIP faktycznie powstaje. Co prawda w momencie pisania tego artykułu trzeba było jeszcze na Linuxie zastosować
pewien hack aby podmienić wersję GLIBCXX, ale z innymi bibliotekami raczej nie będzie tego problemu (chociaż mogą pojawić się inne wyzwania).
Udogodnienia xeus
Dokumentacja on-line z poziomy Jupytera
Często potrzebujemy sprawdzić coś w dokumentacji C++, w tym celu przykładowo wchodzimy na stronę
https://en.cppreference.com/w/ i wyklikujemy lub po prostu wpisuje szukaną frazę w ulubioną wyszukiwarkę, zaawansowani użytkownicy linuxa często używają wtedy manuala. Używając opisywanego kernela dla C++ mamy łatwy sposób do wyświetnenia dokumentacji. Wystarczy wpisać w interaktywnym notatniku tytułu poszukiwanego zagadnienia poprzedzonego zanakiem zapytania. Np. na poniższym obrazku pokazano fragment dokumentacji dla polecenia
?std::string
. Istotnym jest fakt, że dokumentacja ta jest w pełni interaktywna, więc możemy ją przewijać i klikać, a nawet korzystać z wyszukiwarki.
Można podejrzeć możliwe tagi dla standardowej dokumentacji w pliku
${CLING_PREFIX}/share/xeus-cling/tagfiles/cppreference-doxygen-web.tag
, tagi takie można wygenerować przy pomocy doxygena. Aby to zrobić wystarczy ze źródeł html użyć komendy
doxytag. Należy też pamiętać o tym, aby wygenerować plik z linkiem do tagów w katalogu:
${CLING_PREFIX}/etc/xeus-cling/tags.d/
, wg wzoru:
{
"url": "Base URL for the documentation",
"tagfile": "Name of the doxygen tagfile"
}
co dla standardowej biblioteki wygląta tak:
cat ${CLING_PREFIX}/etc/xeus-cling/tags.d/stl.json
{
"url": "https://en.cppreference.com/w/",
"tagfile": "cppreference-doxygen-web.tag"
}
Oprócz doxygena Jupyter wspiera również dokumentacje narzędzia
breathe lub
sphinx, szczegóły w
dokumentacji xeus.
Zawartość multimedialna
Zasadniczo jest to jeden z głównych powodów, dlatego używanie Jupytera jest takie przyjemne, bo do zeszytu można poza kodem, opisem HTML i wzorami wrzucać też multimedia generowane z poziomu języków programowania, w tym również C++.
Zasadniczo wg
dokumentacji xeus-cling można wyświetlać dowolny obraz, w tym celu zainspirowany
dokumentacją przygotowałem funkcję:
#include <string>
#include <fstream>
#include <xtl/xbase64.hpp>
#include <nlohmann/json.hpp>
#include <xcpp/xdisplay.hpp>
namespace im
{
struct image
{
inline image( const std::string & filename )
{
std::ifstream fin( filename, std::ios::binary );
m_buffer << fin.rdbuf();
}
std::stringstream m_buffer;
};
nl::json mime_bundle_repr( const image & i )
{
auto bundle = nlohmann::json::object();
bundle[ "image/png" ] = xtl::base64encode( i.m_buffer.str() );
return bundle;
}
}
void displayPngImage( const std::string & filename )
{
im::image i( filename );
xcpp::display( i );
}
Która po uruchomieniu zgodnie z poniższymi poleceniami dane następujący rezultat:
Widgety Xwidgets
Jest to biblioteka widgetów. Powstała specjalnie do użytku z poziomu Jupytera, bazuje na pythonicznym odpowiedniku
ipywidgets. Istotnym szczegołem jest fakt, że widgety z Xwidgets są kopiowane przez wartość, co zobaczymy na poniższych przykładach.
Instalacja
Instalujemy podobnie, przez manager pakietów
conda
, jednakże poza samym xwidgers musimy jeszcze zainstalować ipywidgets:
conda install ipywidgets -c conda-forge
conda install xwidgets -c conda-forge
Po instalacji uruchamiamy jupytera i działamy.
Suwaki
Abu użyć suwaka należy załączyć nagłówek:
#include <xwidgets/xslider.hpp>
, a obiekt suwaka tworzymy, a następnie wyświetlamy w następujący sposób:
xw::slider < double > slider1;
slider1.display();
Dla suwaków możemy ustawić też wartości minimalną, maksymalną, wartość początkową, czy nawet orientacje ("vertical" lub "horizontal"), możemy to zrobić w momencie tworzenia, jak i po utworzeniu. Przykłady suwaków widać na poniższym rysunku:
Jak widać wartości z suwaków można pobrać, oraz suwaki są kopiowane przez wartość.
Przyciski
Można również tworzyć przyciski, w tym celu potrzebujemy użyć obiektu
xw::button
dostępnego po załączeniu
#include <xwidgets/xbutton.hpp>
. Do przycisku możemy podpiąć funkcję zwrotną/callbacka. Przyciski również można zmieniać i ustawiać, ciekawą możliwością jest możliwość używania predefiniowanych ikon ze strony
https://fontawesome.com/v4.7/icons/, możliwości te są zawarte w poniższym obrazku:
Kod dla powyższego (do kopiowania):
#include <iostream>
#include <xwidgets/xbutton.hpp>
void f1()
{
std::cout << "Kliknieto:" << __FUNCTION__ << std::endl;
}
xw::button button1;
button1.description = "Kliknij mnie";
button1.tooltip = "Jak mnie klikniesz to pojawi sie komunikat";
button1.icon = "chevron-circle-down"; button1.button_style = "info"; button1.on_click( f1 );
xw::button button2 = xw::button::initialize().button_style( "success" ).icon( "snowflake-o" ).description( "Niech pada" ).finalize();
button1.display();
button2.display();
Obserwator i validator dla widgetow
Do widgetów z xwidgets można dołączyć funckjonalność
biblioteki xproperty o obserwatora i validatowa treści. Przykład tego jest widoczny na poniższym obrazku:
a oto jeszcze kod:
#include <iostream>
#include <xwidgets/xnumeral.hpp>
xw::numeral < int > number;
number.min = - 100;
number.description = "Liczba naturalna";
number.display();
XVALIDATE( number, value,[ ]( const auto &, double proposal ) {
std::cout << "Validator: propozycja: " << proposal << std::endl;
if( proposal < 0 )
{
throw std::runtime_error( "Tylko liczby dodatnie!" );
}
return proposal;
} );
XOBSERVE( number, value,[ ]( const auto & s ) {
std::cout << "Zmieniono wartosc na: " << s.value << std::endl;
} );
Inne widgety
Jest ich bardzo dużo, wiele z nich znamy z HTMLa, przegląd interaktywny wszystkich obecnie wspieranych widgetów można sobie zobaczyć wchodząc przez
Readme Repozytorium Xwidgets na Bindera. Na zakończenie tematu widgetów pozwolę sobie na zademonstrowanie jeszcze widgetów audio-video:
#include "xwidgets/ximage.hpp"
#include "xwidgets/xvideo.hpp"
#include "xwidgets/xaudio.hpp"
auto im = xw::image_from_file( "marie.png" ).finalize();
auto vid1 = xw::video_from_file( "Big.Buck.Bunny.mp4" ).finalize();
auto vid2 = xw::video_from_url( "https://webrtc.github.io/samples/src/video/chrome.webm" ).finalize();
auto au = xw::audio_from_file( "Big.Buck.Bunny.mp3" ).finalize();
im.display();
vid1.display();
vid2.display();
au.display();
Poza widgetami zwyczajnymi są też widgety komponujące inne widgety, typu karty. Są też np. funkcje do wyboru koloru.
Dalsze możliwości Jupytera - specjalne komendy
Jest jeszcze wiele ciekawych bibliotek, dedykowanych pod C++sowy kernel Jupytera, ale wpierw wróćmy do możliwości xeus-cling, jakie dają
magiczne komendy.
Komendy te zaczynają się od
%
dla linii, lub
%%
dla całej komórki. Przykłady ich użycia są krótkie i treściwe w
dokumentacji, dlatego pozwolę sobie jedynie na ich opisanie.
Udogodnieniem jest możliwość wywoływania komend powłoki z poziomu Jupytera m.in.
ls
,
ls
,
find
. W tym celu linię należy zacząć od
!
(wykrzyknika) np.:
!./programWykonywalny
xplot - biblioteka do generowania wykresów
Biblioteka
xplot jest nakładką na pythoniczną bibliotekę
bqplot-2D, jednakże niestety jest ona będącą we wczesnej fazie rozwoju. Umożliwia ona rysowanie i wyświetlanie wykresów w ramach zeszytu Jupyter.
Jej pełne możliwości można zobaczyć wchodząc przez
Readme Repozytorium xplot na Bindera, który zawiera wszystkie przykłady zrealizowane z poziomu C++, natomiast kilka interesujących przykładów (zaciągniętych z repozytorium) znajduje się na poniższych obrazkach:
Aby ją zainstalować wg
dokumentacji potrzeba:
conda install xplot -c QuantStack -c conda-forge
conda install widgetsnbextension -c conda-forge
conda install bqplot>=0.11.4,<0.12 -c conda-forge
jupyter labextension install @jupyter-widgets/jupyterlab-manager
jupyter labextension install bqplot@^0.4.3
xtensor (rzekomy odpowiednik NumPy) - numeryczna analiza w oparciu o tablice wielo-wymiarowe
W
dokumentacji można sobie przejrzeć możliwości przez wejście na link do Bindera. Z tamtej strony został pobrany poniższy obrazek, demonstrujący przykład działania biblioteki:
Osoby znający NumPy odnajdą się w porównaniu
NumPy to xtensor cheatsheet.
Instalacja biblioteki xtensor
Wg
dokumentacji wystarczy jedynie:
conda install -c conda-forge xtensor
Biblioteka ta zawiera też podbibliotekę do manipulacji różnymi formatami
xtensor-io
xtensor-python - mapowanie pythona z poziomu C++
xtensor-python jest to biblioteka, która umożliwia wykorzystywanie tablic z NumPy z poziomu C++, szczegóły w
dokumentacji.
Instalacja xtensor-python
mamba install -c conda-forge xtensor-python
xvega - biblioteka sterowana JSONem do tworzenia wykresów
O tej bibliotece jest poświęcony cały
artykuł przeglądowy, dlatego nie będę jej opisywał. Osoby zainspirowane artykułem mogą przeskoczyć do
dokumentacji lub do
kodu.
xleaflet - biblioteka do interaktywnych map
xleaflet jest to backend do pythonicznej biblioteki
ipyleaflet, który umożliwia wizualizacje map z poziomu jupytera.
Możliwe jest m.in.:
Instalacja tej biblioteki wymaga więcej komend, wg
dokumentacji należy zastosować poniższe komendy (są to komendy dla jupyter-lab, dla notebooka trzeba by zastosować inne)
mamba install xeus-cling xleaflet -c conda-forge
jupyter labextension install @jupyter-widgets/jupyterlab-manager
jupyter labextension install jupyter-leaflet
Brzmi ciekawie? Zachęcam wpierw aby na
github projektu przejrzeć przykłady, można też kliknąć na
binderze uruchamiającym xleaflet.
Przykładowo dla poniższego kodu otrzymujemy interaktywną mapę skierowaną na Kraków:
#include <xleaflet/xmap.hpp>
#include <xleaflet/xfullscreen_control.hpp>
auto map = xlf::map::initialize()
.center( { 50.0614300, 19.9365800 } ) .zoom( 10 )
.finalize();
map.add_control( xlf::fullscreen_control() );
map.display();
Jako, że na temat używania map z poziomu Jupytera przy użyciu C++ powstał
artykuł dlatego też pozwolę sobie pominąć dalszy opis.
xframe - kolejna biblioteka do numerycznych analiz wielowymiarowych tablic
xframe to biblioteka do numerycznych analiz dla wielowymiarowych tablic, zainspirowana przez
pandas i
xarray.
xwebrtc - biblioteka do obsługi strumieni multimedialnych
xwebrtc jest backendem do
WebRTC (narzędzia do obsługi multimediów) i odpowiednikiem pythonicznego
ipywebrtc. Biblioteka umożliwia m.in. obsługę kamery, odtwarzanie multimediów, nagrywanie, czy rzekomo prowadzenie rozmów z innymi. Problemem jest fakt, że na
githubie biblioteki jest informacja o wczesnej fazie developmentu, dlatego wiele ciekawych funkcjonalności może nie działać.
Instalacja xwebrtc
Na ten moment w
dokumentacji jest jedynie informacja o instalacji biblioteki dla przestarzałych zeszytów:
mamba install xeus-cling xwebrtc -c conda-forge
wydaje się, że instalacja dla
jupyter-lab
nie jest obecnie wspierana (brak informacji w internecie + nie zadziałało u mnie).
Demonstracja
Jak już uda się nam zainstalować bibliotekę możemy przetestować poniższy kod:
#include "xwebrtc/xcamera_stream.hpp"
auto camera2 = xwebrtc::camera_facing_user( false ).finalize();
camera2
otrzymując w rezultacie obraz z kamery (czyli w moim przypadku poduszki):
Użycie CUDA z poziomu Jupytera
Wg
artykułu o użyciu CUDA w Jupyterze jest to możliwe. Niestety ze względu na brak karty Nvidia'i nie jestem w stanie tego opisać.
Voilà - Gdy nie chcemy instalować serwera jupytera
Jest to narzędzie, które ma z założenia zamieniać zeszyty Jupytera w oddzielne aplikacje webowe. Niestety na moment pisania artykułu to narzędzie nie zadziałało mi z zeszytami z C++. Może jednak za jakiś czas zacznie to działać, wtedy odsyłam do
https://github.com/voila-dashboards/voila.
SoS - wiele kerneli w jednym zeszycie
Narzędzie SoS umożliwia używanie wielu kerneli Jupytera w jednym zeszycie, ponadto umożliwia migracje danych między kernelami.
Niestety obecnie oficjalnie wspierane są jedynie
wybrane kernele, natomiast przez społeczność został już napisany prosty
sos-xeus-cling umożliwiający użycie C++ z poziomu SoSa. Osoby zainteresowane mogą przeczytać
opis mechanizmu działania SoS wraz z przykładami.
Mimo dobrych chęci sekcje te należy potraktować na razie jako ciekawostkę, gdyż nie przeszedł on testów integracji między C++ a Pythonem, powiem więcej - kod z C++ w ogóle mi nie zadziałał przy aktywowaniu kernela
SoS.
jupyter-console - czyli dla zwolenników konsoli
Jest to alternatywa do webowego jupytera, jej używanie wygląda jak na poniższym obrazku:
Aby ją zainstalować wystarczy:
conda install -c conda-forge jupyter-console
jednakże w moim przypadku niestety zadziałało tylko gdy zainstalowałem przy pomocy:
pip install jupyter-console
Należy pamiętać, że do c++ należy wybrać odpowiedni kernel, jeśli tego nie zrobimy będzie użyty domyślny, czyli do pythona. Wybieramy kernel przy użyciu argumentu:
--kernel=
, szczegóły w powyższym obrazku.
Więcej, aczkolwiek niezbyt wiele informacji
w dokumentacji.
Jak widać jupyter-console nie ma możliwości otwierania istniejących zeszytów, jedynie idzie z komendami w locie. Ma natomiast możliwość podpięcia się do uruchomionego kernala (nawet tego z jupyter-lab) i wykonaniu dodatkowych komend na aktualnie działającej sesji.
Współpraca wielu osób nad jednym dokumentem równocześnie
Pierwszą rzeczą jest zainstalowanie dodatku
jupyterlab-link-share, dzięki któremy w menu kontekstowym po prawej stronie pojawi się opcja
Share -> Share Jupyter server link.
Robimy to komendą:
conda install -c conda-forge jupyterlab-link-share
Dzięki temu będziemy mogli łatwo skopiować linka. Rzekomo też dzięki uruchomieniu jupytera z opcją:
jupyter-lab --collaborative=true
wiele osób będzie mogło pracować nad tym samym linkiem. Niestety gdy to testowałem nie zadziałało.
Hasło zamiast tokena
Jeśli w pewnym momencie będziemy chcieli mieć dostęp do jupytera pamiętając jedynie hasło, bez zmiennego tokena to możemy włączyć taką opcję, w tym celu piszemy:
jupyter-lab password
Enter password:
Verify password:
od tej pory uruchomiwszy Jupytera nie jest nam potrzebny token, a jedynie hasło.
Dostęp zdalny
Zabezpieczeniem jest też iż można go uruchomić jedynie z localhosta, aby to zmienić należy uruchomić z flagą:
jupyter-lab --ServerApp.allow_remote_access=true
Połączenie z Jupyter-lab przez HTTPS
Jest to dobrze opisane w
oddzielnym artykule. Jako, że zeszyty Jupytera często uruchamiane są lokalnie pozwolę sobie nie opisywać jak włączyć szyfrowanie.
Alternatywa do PowerPoint - pokaz slajdów z Jupytera
Jest to możliwe, oraz wygląda naprawdę dobrze. Co prawda wygodnie wspierane jest na razie tylko przez starszą wersję Jupyter-notebook, gdzie zmieniamy typ komórek na slajdy, następnie zapisujemy i uruchamiamy w specjalny sposób:
jupyter nbconvert plik_ze_slajdami.ipynb --to slides --post serve
Po zrobieniu tego pojawi się nam link do wejścia. Jak to zrobić jest opisane w
tym artykule.
Jupyter-hub
Jest to projekt umożliwiający obsługę Jupytera przez wielu użytkowników posiadających swoje konta. Na
głównej stronie Jupyter-hub znajdują się dwie wiodące wersje -
JupyterHub przy użyciu Kubernetes i Docker, oraz wersja
TLJH - The Littlest JupyterHub, która jest łatwa do zainstalowania. Wg
ichniejszej dokumentacji jeśli ilość użytkowników będzie do stu to wystarczy wersja prostrza.
Instalacja The Littlest JupyterHub i xeus-cling
Na
pierwszej stronie dokumentacji jest informacja o wsparciu dla systemu Ubuntu 18.04 lub nowszych, mi udało się zainstalować to na Ubuntu 21.04. Na ww. stronie dokumentacji jest wiele tutoriali konfiguracyjnych u wielu popularnych dostawrów usług serwerowych (wtym u polskiego OVH), osoby, które chcą mają też
poradnik jak zainstalować na własnym serwerze. Zasadniczo instalacja sprowadza się do komend:
sudo apt install python3 python3-dev git curl
curl -L https://tljh.jupyter.org/bootstrap.py | sudo -E python3 - --admin user # nazwa user to nazwa użytkownika, można ustawić inną
Po kilku minutach wszystko będzie gotowe i możemy wejść na adres naszego komputera (może to być
localhost
lub adres IP w sieci) i logujemy się jako nasz użytkownik - ja zalogowałem się po raz pierwszy nazwą użytkownika jw. a hasło jakie użyjemy za pierwszym razem takie pozostaje. Po zalogowaniu możemy dodać użytkowników (użytkownik na linię), jak również zarządzać użytkownikami, ich serwerami itp.
Pakiety instalujemy nie tak jak wcześniej, tylko wchodzimy przez jupyterHub tworzymy nowy Terminal i tam wpisujemy komendę np.:
sudo -E conda install xeus-cling -c conda-forge
po wylogowaniu i zalogowaniu pojawią się nam zmiany - czyli w tym przypadku możliwość tworzenia zeszytów w C++.
Ważne jest też, aby instalować poprzedzając przez sudo -E
!Szczegółowo, a zarazem prosto i obrazowo powyższe kroki są opisane na
stronie poświęconej instalacji na naszym serwerze.
Binder
Mając skonfigurowanego Kubernetesa można też skonfigurować na własnym serwerze
Bindera, którego znamy z przykładów. Więcej informacji w
dokumentacji Bindera. Można też sobie wejść na
repozytorium projektu
Repo2Docker - jupyter-notebook niezależny od systemu
Repo2Docker jest narzędziem, które umożliwia utworzenie zeszytu na środowisku izolowanym przez Dockera. Wystarczy podać linka do repozytorium o odpowiedniej strukturze i w rezultacie (po dłuższym czasie) pojawia się nam link do interaktywnego zeszytu.
Aby zainstalować to narzędzie wystarczy:
pip3 install jupyter-repo2docker
Następnie możemy uruchomić narzędzie używając:
jupyter-repo2docker link.do.repozytorium
Podejrzeć sobie przykłady możemy przeglądając repozytoria na stronie
https://github.com/binder-examples. Przykład można uruchomić zawołaniem komendy pobierającej wszystko z Githuba:
jupyter-repo2docker https://github.com/binder-examples/zero-to-binder
lub uruchomić z katalogu lokalnego. Istotnym jest to, że można sobie skonfigurować
zależności przez pliki konfiguracyjne, m.in. można skonfigurować:
Więcej informacji w
dokumentacji.
Bibliografia