Skip to content

Commit 2e30980

Browse files
committed
smooth scrolling in guidebook
1 parent 9cc1766 commit 2e30980

2 files changed

Lines changed: 131 additions & 109 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
[![Latest nightly release](https://jitpack.io/v/MKLab-ITI/JGNN.svg)](https://jitpack.io/#MKLab-ITI/JGNN) [![Latest stable release](https://img.shields.io/badge/JAR-1.2.0%20stable-red)](https://github.com/MKLab-ITI/JGNN/releases/latest)
44

5+
*Resource efficient machine learning and graph neural networks in native Java.*
6+
57
Graph Neural Networks (GNNs) are getting more and more popular, for example to
68
make predictions based on relational information, or to perform inference
79
on small datasets. JGNN provides native Java implementations of this machine
@@ -11,7 +13,7 @@ Follow the Jitpack badge for Gradle or Maven integration.
1113
* Cross-platform
1214
* Lightweight
1315
* Optimized: data views, automatic datatypes, SIMD, parallelized batching
14-
* Neuralang scripting language for model definition
16+
* Neuralang scripting language for model definitions
1517

1618
Feel free to contribute in any way, for example through the [issue tracker](https://github.com/MKLab-ITI/JGNN/issues). In addition to bug reports,
1719
requests for features and clarifications are welcome.

docs/index.html

Lines changed: 128 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,15 @@
5959

6060
.sidebar-link.active {
6161
color: #fff;
62-
background-color: #007bff;
62+
background-color: #33bbff;
6363
border-radius: 4px;
6464
}
65+
.sidebar-link.subsection {
66+
color: #777;
67+
}
68+
.sidebar-link.subsection.active {
69+
color: #fff;
70+
}
6571

6672

6773
details {
@@ -129,42 +135,53 @@
129135
}
130136
</style>
131137
<script>
132-
document.addEventListener("DOMContentLoaded", function () {
133-
const sidebarLinks = document.querySelectorAll('.sidebar-link');
134-
135-
function removeActiveClasses() {
136-
sidebarLinks.forEach(link => link.classList.remove('active'));
137-
}
138-
139-
function addActiveClass(link) {
140-
removeActiveClasses();
141-
link.classList.add('active');
142-
}
143-
144-
const options = {
145-
root: null,
146-
rootMargin: '0px',
147-
threshold: 0.2
148-
};
138+
document.addEventListener("DOMContentLoaded", function () {
139+
const sidebarLinks = document.querySelectorAll('.sidebar-link');
140+
const sections = document.querySelectorAll('section');
141+
142+
function removeActiveClasses() {
143+
sidebarLinks.forEach(link => link.classList.remove('active'));
144+
}
145+
146+
function addActiveClass(link) {
147+
removeActiveClasses();
148+
if (link) link.classList.add('active');
149+
}
150+
151+
function getClosestSection() {
152+
let minDistance = Infinity;
153+
let closestSection = null;
154+
const scrollY = window.scrollY;
155+
sections.forEach(section => {
156+
const offset = Math.abs(section.offsetTop - scrollY - 100); // Adjust for fixed navbar offset
157+
if (offset < minDistance) {
158+
minDistance = offset;
159+
closestSection = section;
160+
}
161+
});
162+
return closestSection;
163+
}
164+
165+
function updateActiveSection() {
166+
const section = getClosestSection();
167+
if (section) addActiveClass(document.querySelector(`.sidebar-link[href="#${section.id}"]`));
168+
}
169+
window.addEventListener("scroll", updateActiveSection);
170+
sidebarLinks.forEach(link => {link.addEventListener("click", function (event) {
171+
event.preventDefault();
172+
const targetId = this.getAttribute("href").substring(1);
173+
const targetSection = document.getElementById(targetId);
174+
175+
if (targetSection) {window.scrollTo({
176+
top: targetSection.offsetTop - 80,
177+
behavior: "smooth"
178+
});}
179+
});});
180+
window.addEventListener("load", updateActiveSection);
181+
});
182+
</script>
149183

150-
const observerCallback = (entries, observer) => {
151-
entries.forEach(entry => {
152-
const id = entry.target.getAttribute('id');
153-
const link = document.querySelector(`.sidebar-link[href="#${id}"]`);
154184

155-
if (entry.isIntersecting) {
156-
addActiveClass(link);
157-
}
158-
});
159-
};
160-
161-
const observer = new IntersectionObserver(observerCallback, options);
162-
163-
document.querySelectorAll('section').forEach(section => {
164-
observer.observe(section);
165-
});
166-
});
167-
</script>
168185
</head>
169186

170187
<body>
@@ -174,21 +191,14 @@
174191
<li class="nav-item"> <a class="sidebar-link" href="#setup">1. Setup</a></li>
175192
<li class="nav-item"> <a class="sidebar-link" href="#quickstart">2. Quickstart</a></li>
176193
<li class="nav-item"> <a class="sidebar-link" href="#gnn-builders">3. GNN Builders</a></li>
177-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#modelbuilder"
178-
style="color: #777777;">3.1. ModelBuilder</a></li>
179-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#fastbuilder"
180-
style="color: #777777;">3.2. FastBuilder</a></li>
181-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#neuralang"
182-
style="color: #777777;">3.3. Neuralang</a></li>
183-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#debugging"
184-
style="color: #777777;">3.4. Debugging</a></li>
194+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#modelbuilder">3.1. ModelBuilder</a></li>
195+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#fastbuilder">3.2. FastBuilder</a></li>
196+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#neuralang">3.3. Neuralang</a></li>
197+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#debugging">3.4. Debugging</a></li>
185198
<li class="nav-item"> <a class="sidebar-link" href="#training">4. Training</a></li>
186-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#create-data"
187-
style="color: #777777;">4.1. Create data</a></li>
188-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#node-classification"
189-
style="color: #777777;">4.2. Node classification</a></li>
190-
<li class="nav-item ps-md-3 text-secondary"><a class="sidebar-link small p-1 subsection" href="#graph-classification"
191-
style="color: #777777;">4.3. Graph classification</a></li>
199+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#create-data">4.1. Create data</a></li>
200+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#node-classification">4.2. Node classification</a></li>
201+
<li class="nav-item ps-md-3"><a class="sidebar-link small p-1 subsection" href="#graph-classification">4.3. Graph classification</a></li>
192202
</ul>
193203
</nav>
194204

@@ -218,12 +228,12 @@ <h1 class="text-center">JGNN</h1>
218228
<p class="text-center"><em>Resource efficient machine learning and graph neural networks in native Java.</em>
219229
</p>
220230

221-
<p>Graph Neural Networks (GNNs) are getting more and more popular as a machine learning paradigm,
231+
<p>Graph Neural Networks (GNNs) are getting more and more popular,
222232
for example to make predictions
223233
based on relational information, or to perform inference on small datasets. JGNN is a library that
224-
provides cross-platform implementations of this paradigm without the need for dedicated
225-
hardware or firmware; create highly portable models that fit and are trained in
226-
a few megabytes of memory.
234+
provides cross-platform implementations of this paradigm and traditional neural networks
235+
without the need for dedicated hardware or firmware; create highly portable models that fit and
236+
are trained in a few megabytes of memory.
227237
</p>
228238

229239
<p>
@@ -448,34 +458,56 @@ <h1>3. GNN Builders</h1>
448458
Use this builder to maintain model definitions in one place (e.g., packed in one string
449459
variable, or in one file) and avoid weaving symbolic expressions in Java code.</li>
450460
</ul>
451-
In this section we cover these three builder classes and summarize debugging mechanisms that
452-
check the integrity of constructed models, visualize their data flow, and monitor specific
461+
In this section we cover these three builder classes. We also summarize debugging mechanisms for
462+
checking the integrity of constructed models, visualize their data flow, and monitor specific
453463
data at runtime.</p>
454464

465+
<section id="modelbuilder">
455466
<h3 id="modelbuilder">3.1. ModelBuilder</h3>
456-
<p>This is the base model builder class; it offers a wide breadth of functionalities that other builders extend.
457-
Before looking at how to use it, though, we need to see what JGNN models look like under the hood.
458-
Models are collections of <code class="language-java">NNOperation</code> instances, each representing a numerical computation with
459-
specified inputs and outputs of
460-
JGNN's <code>Tensor</code> type. Tensors will be covered later; for now, it suffices to think of them as
461-
numerical vectors, which are sometimes endowed with matrix dimensions.
467+
<p>This is the base model builder class; it offers a wide breadth of functionalities that other builders extend. Models
468+
take tensors as input and outputs. Tensors will be covered later; for now, it suffices to think of them as
469+
numerical vectors, which are sometimes endowed with matrix dimensions. The models themselves are built from Java classes
470+
that indicate sub-operations. However, this can be too verbose many lines of code are needed to declare even simple expressions,
471+
making models cumbersome to read and maintain - hence the need for
472+
builders that construct the models from concise symbolic expressions.</p>
473+
<p>To create a model with the <code class="language-java">ModelBuilder</code> class,
474+
instantiating the builder, use a method chain to declare an input variable
475+
with the <code class="language-java">.var(String)</code> method, parse an expression with the
476+
<code class="language-java">.operation(String)</code> method, and finally declare which symbol holds
477+
outputs with the <code class="language-java">.out(String)</code> method.
478+
The first and last of these methods can be called multiple times
479+
to declare several inputs and outputs. Inputs need to be only one symbol, but a whole expression
480+
for evaluation can be declared in outputs. Obtain the created model's instance with the <code class="language-java">.getModel()</code> method.
462481
</p>
482+
463483
<p>
464-
This guidebook does not list operation classes, as they are rarely used directly and can be found the Javadoc, namely
465-
<a href="https://mklab-iti.github.io/JGNN/javadoc/mklab/JGNN/nn/inputs/package-summary.html" target="_blank">nn.inputs</a>,
466-
<a href="https://mklab-iti.github.io/JGNN/javadoc/mklab/JGNN/nn/activations/package-summary.html" target="_blank">nn.activations</a>,
467-
and
468-
<a href="https://mklab-iti.github.io/JGNN/javadoc/mklab/JGNN/nn/pooling/package-summary.html" target="_blank">nn.pooling</a>.
469-
Create models in pure Java like the example computes, where the expression
470-
<code class="language-rust">y=log(2*x+1)</code> does not have any trainable parameters.
471-
After defining models, run them with the method <code class="language-java">Tensor Model.predict(Tensor...)</code>.
472-
This takes as input one or more comma-separated tensors that match the model's
473-
inputs (in the same order) and computes a list of output tensors. If inputs are dynamically created,
474-
an overloaded version of the same method supports an array list of input tensors
475-
<code class="language-java">Tensor Model.predict(ArrayList&lt;Tensor&gt;)</code>.
484+
After defining models, use them to make predictions like below.
485+
The prediction method takes as input one or more comma-separated tensors that match the model's
486+
inputs (in the same order) and computes a list of output tensors. If inputs are dynamically created,
487+
an overloaded version of the same method supports passing an array list of input tensors instead.
476488
</p>
477489

478-
<pre><code class="language-java">Variable x = new Variable();
490+
<pre><code class="language-java">ModelBuilder modelBuilder = new ModelBuilder()
491+
.var("x")
492+
.operation("y = log(2*x+1)")
493+
.out("y");
494+
Model model = modelBuilder.getModel();
495+
System.out.println(model.predict(Tensor.fromDouble(2)));
496+
</code></pre>
497+
498+
<details><summary>Equivalent Java implementation without the builder.</summary>
499+
<p>Under the hood, JGNN models are collections of <code class="language-java">NNOperation</code> instances, each representing a numerical computation with
500+
specified inputs and outputs of
501+
JGNN's <code class="language-java">Tensor</code> type. This guidebook does not list operation classes, as they are rarely used directly and can be found the Javadoc, namely
502+
<a href="https://mklab-iti.github.io/JGNN/javadoc/mklab/JGNN/nn/inputs/package-summary.html" target="_blank">nn.inputs</a>,
503+
<a href="https://mklab-iti.github.io/JGNN/javadoc/mklab/JGNN/nn/activations/package-summary.html" target="_blank">nn.activations</a>,
504+
and
505+
<a href="https://mklab-iti.github.io/JGNN/javadoc/mklab/JGNN/nn/pooling/package-summary.html" target="_blank">nn.pooling</a>.
506+
Create models in pure Java like the example below, where the expression
507+
<code class="language-rust">y=log(2*x+1)</code> does not have any trainable parameters.
508+
</p>
509+
510+
<pre><code class="language-java">Variable x = new Variable();
479511
Constant c1 = new Constant(Tensor.fromDouble(1)); // holds the constant "1"
480512
Constant c2 = new Constant(Tensor.fromDouble(2)); // holds the constant "2"
481513
NNOperation mult = new Multiply()
@@ -490,27 +522,8 @@ <h3 id="modelbuilder">3.1. ModelBuilder</h3>
490522
.addInput(x)
491523
.addOutput(y);
492524
System.out.println(model.predict(Tensor.fromDouble(2))); // one-element input
493-
</code></pre>
494-
495-
<p>Judging by the fact that several lines of code are needed to declare even simple expressions,
496-
pure Java code for creating full models tends to be cumbersome to read and maintain - hence the need for
497-
builders that construct the models from concise symbolic expressions. Let us recreate the above example
498-
with the <code class="language-java">ModelBuilder</code> class.
499-
After instantiating the builder, use a method chain to declare an input variable
500-
with the <code class="language-java">.var(String)</code> method, parse an expression with the
501-
<code class="language-java">.operation(String)</code> method, and finally declare which symbol holds
502-
outputs with the <code class="language-java">.out(String)</code> method.
503-
The first and last of these methods can be called multiple times
504-
to declare several inputs and outputs. Inputs need to be only one symbol, but a whole expression
505-
for evaluation can be declared in outputs.
506-
</p>
507-
508-
<pre><code class="language-java">ModelBuilder modelBuilder = new ModelBuilder()
509-
.var("x")
510-
.operation("y = log(2*x+1)")
511-
.out("y");
512-
System.out.println(model.predict(Tensor.fromDouble(2)));
513-
</code></pre>
525+
</code></pre>
526+
</details>
514527

515528
<details><summary>Differences between expression parsing and Neuralang.</summary>
516529
<p>
@@ -723,22 +736,24 @@ <h3 id="modelbuilder">3.1. ModelBuilder</h3>
723736

724737
<p>Model definitions have so far been too simple to be employed in practice;
725738
we need trainable parameters, which are created inline with the <code>matrix</code>
726-
and <code>vector</code> functions. There is equivalent Java code for this, but its usage
727-
is discouraged to keep model definitions simple.
739+
and <code>vector</code> Neuralang functions. Do not use equivalent Java code, because
740+
it is better to keep model definitions simple.
728741
Additionally, there may be constants and configuration hyperparameters. Of these, constants reflect
729-
untrainable tensors and set with <code class="language-java">ModelBuilder.const(String, Tensor)</code>.
742+
untrainable tensors and set in a builder with <code class="language-java">.const(String, Tensor)</code>.
730743
Both numbers in the last snippet's symbolic definition are internally parsed into constants.
731744
</p>
732745
<p>
733746
On the other hand, configuration hyperparameters are numerical values used by the parser and
734-
set with <code class="language-java">ModelBuilder.config(String, double)</code>. Provide another
747+
set for a builder with <code class="language-java">.config(String, double)</code>. Provide another
735748
configuration's name as the second argument to copy its value.
736749
On the other hand, hyperparameters can be used as arguments to dimension sizes and regularization.
737-
Retrieve previously set hyperparameters though
738-
<code class="language-java">ModelBuilder.getConfigOrDefault(String, double)</code>, where the second argumement may be ommitted.
750+
Retrieve previously set builder hyperparameters though
751+
<code class="language-java">.getConfigOrDefault(String, double)</code>, where the second argumement may be ommitted.
739752
This is mostly useful for bringing into code hyperparameters declared in Neuralang scripts.
740753
</p>
754+
</section>
741755

756+
<section id="fastbuilder">
742757
<h3 id="fastbuilder">3.2. FastBuilder</h3>
743758
<p>The <code class="language-java">FastBuilder</code> class for building GNN architectures extends the generic
744759
<code class="language-java">ModelBuilder</code> with common graph neural network operations. The main difference
@@ -945,8 +960,9 @@ <h3 id="fastbuilder">3.2. FastBuilder</h3>
945960
for each feature dimension, aggregates feature values across all nodes.
946961
</p>
947962
</details>
963+
</section>
948964

949-
965+
<section id="neuralang">
950966
<h3 id="neuralang">3.3. Neuralang</h3>
951967

952968
<p>Neuralang scripts consist of functions that declare machine learning
@@ -1095,8 +1111,9 @@ <h3 id="neuralang">3.3. Neuralang</h3>
10951111
values denoted with <code class="language-java">?</code> from an example input.
10961112
For faster completion of the model, we provide a dataless list of node identifiers as input.</p>
10971113
</details>
1114+
</section>
10981115

1099-
1116+
<section id="debugging">
11001117
<h3 id="debugging">3.4. Debugging</h3>
11011118
<p>JGNN offers high-level tools for debugging
11021119
architectures. Here we cover what diagnostics to run, and how to make
@@ -1153,6 +1170,7 @@ <h3 id="debugging">3.4. Debugging</h3>
11531170
<p>Some tensor or matrix methods do not
11541171
correspond to numerical operations but
11551172
are only responsible for naming dimensions.
1173+
</section>
11561174
Functionally, such methods are largely decorative,
11571175
but they cab improve debugging by throwing errors for
11581176
incompatible non-null names. For example,
@@ -1292,7 +1310,7 @@ <h3 id="debugging">3.4. Debugging</h3>
12921310
monitors the outcome of matrix multiplication:
12931311
</p>
12941312
<pre><code class="language-java">builder.operation("h = relu(monitor(x@matrix(features, 64)) + vector(64))")</code></pre>
1295-
1313+
</section>
12961314
</section>
12971315

12981316
<section id="training">
@@ -1309,6 +1327,7 @@ <h1>4. Training</h1>
13091327
(reach out with requests for helper classes for other kinds of predictive tasks
13101328
in the project's GitHub issues).</p>
13111329

1330+
<section id="create-data">
13121331
<h3 id="create-data">4.1. Create data</h3>
13131332
<p>JGNN contains dataset classes that automatically download and load
13141333
datasets for out-of-the-box experimentation. These datasets can be found
@@ -1447,7 +1466,8 @@ <h3 id="create-data">4.1. Create data</h3>
14471466
long predictedClassId = prediction.argmax();
14481467
System.out.println(classIds.get(predictedClassId));</code></pre>
14491468

1450-
1469+
</section>
1470+
<section id="node-classification">
14511471
<h3 id="node-classification">4.2. Node classification</h3>
14521472
<p>
14531473
Node classification models can be backpropagated by considering a list of node indeces and desired
@@ -1468,11 +1488,10 @@ <h3 id="node-classification">4.2. Node classification</h3>
14681488
nodes.range(trainSplit, validationSplit));
14691489
</code></pre>
14701490

1491+
</section>
14711492

1472-
1493+
<section id="graph-classification">
14731494
<h3 id="graph-classification">4.3. Graph classification</h3>
1474-
1475-
14761495
<p>Most neural network architectures are designed with the idea
14771496
of learning to classify nodes or samples. However, GNNs also
14781497
provide the capability to classify entire graphs based on
@@ -1605,6 +1624,7 @@ <h3 id="graph-classification">4.3. Graph classification</h3>
16051624
}
16061625
}</code></pre>
16071626
</section>
1627+
</section>
16081628

16091629
</div>
16101630
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>

0 commit comments

Comments
 (0)