Published
Edited
Mar 23, 2021
Fork of Contours
1 fork
1 star
Insert cell
Insert cell
value = (x, y) =>
(1 + (x + y + 1) ** 2 * (19 - 14 * x + 3 * x ** 2 - 14 * y + 6 * x * y + 3 * y ** 2))
* (30 + (2 * x - 3 * y) ** 2 * (18 - 32 * x + 12 * x * x + 48 * y - 36 * x * y + 27 * y ** 2))
Insert cell
unit_circle = (x, y) =>
(x**2 + y**2)
Insert cell
l1norm = (x,y) => (Math.abs(x) + Math.abs(y))
Insert cell
quadratic = (x, y) => (x**2 - x*y + y**2)
Insert cell
simple_quadratic = (x, y) => (alpha*x**2/2 + beta*y**2/2)
Insert cell
simple_quadratic_prime = (x, y) => [alpha*x, beta*y]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
x3 = d3.scaleLinear([-2, 2], [padding-4, size - 2*padding+4]);
Insert cell
y3 = d3.scaleLinear([-2, 2], [size - padding+2, padding-3]);
Insert cell
x4 = d3.scaleLinear([0, 20], [padding, size-padding]);
Insert cell
min_exponent = -10;
Insert cell
y4 = d3.scaleLog([10**(min_exponent), 1], [size - padding/2, padding]);
Insert cell
viewof alpha = Range([0, 4], {label: 'm_x', value: 2})
Insert cell
viewof beta = Range([0, 4], {label: 'm_y', value: 1})
Insert cell
gd_widget = gradient_descent_widget(simple_quadratic, simple_quadratic_prime, x3, y3, d3.range(-3, 10, .3), x4, y4)
Insert cell
Insert cell
padding = 35;
Insert cell
size = (width - (2 + 1) * padding) / 2 + padding
Insert cell
gradient_descent_widget = (f, fprime, x2, y2, thresholds, x2b, y2b) => {
const x_init = [1,1];
const x3 = d3.scaleLinear([-2, 2], [-3*padding, size]);
const y3 = d3.scaleLinear([-2, 2], [size, -2*padding]);
const color = d3.scaleSequential(d3.extent(thresholds), d3.interpolateMagma);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height/2+2*padding])
.style("display", "block")
.style("margin", "10 10");
//.style("width", "800px")
//.style("height", "500px");
const cell = svg.append("g")
.selectAll("g")
.data(d3.cross(d3.range(2), d3.range(1)))
.join("g")
.attr("transform", ([i, j]) => `translate(${i * size},${j * size})`);


cell.each(function([i, j]) {
let a = d3.select(this);
if (i == 0) {
a.append("g")
.attr("fill", "none")
.attr("stroke", "#fff")
.attr("stroke-opacity", 0.5)
.selectAll("path")
.data(generate_contours(f, x3, y3, thresholds, size-3*padding, size-2*padding))
.join("path")
.attr("fill", d => color(d.value))
.attr("d", d3.geoPath())
.attr("transform", `translate(${padding},${padding})`);
a.append("g")
.attr("transform", `translate(0,${y2(-2)})`)
.call(d3.axisBottom(x2).ticks(5,"f"))
.style("font", "14px sans-serif");

a.append("g")
.attr("transform", `translate(${x2(-2)},0)`)
.call(d3.axisLeft(y2).ticks(5, "f"))
.style("font", "14px sans-serif");
//Make data running gradient descent
a.append("path")
.attr("fill", "none")
.attr("stroke", "#22ff22")
.attr("stroke-width", 7)
.attr("d", d3.line(d => x2(d.x), d => y2(d.y))(run_gradient_descent(f, fprime, x_init)));
}
if (i == 1) {
a.append("path")
.attr("fill", "none")
.attr("stroke", "#000000")
.attr("stroke-width", 3)
.attr("d", d3.line(d => x2b(d.step), d => y2b(d.gap))(run_gradient_descent(f, fprime, x_init)));
a.append("g")
.attr("transform", `translate(0,${y2b(1e-10)})`)
.call(d3.axisBottom(x2b).ticks(5,"f"))
.style("font", "14px sans-serif");

a.append("g")
.attr("transform", `translate(${x2b(-2)},0)`)
.call(d3.axisLeft(y2b).ticks(5, "f").tickFormat(d3.format(".1e")))
.style("font", "14px sans-serif");
a.append("text")
.attr("class", "x label")
.attr("text-anchor", "middle")
.attr("x", size/2)
.attr("y", size+20)
.text("iteration")
.style("font", "18px sans-serif");
}
/*svg.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("x", size)
.attr("y", 20)
.text("Convergence of gradient descent")
.style("font", "bold 18px sans-serif");*/
});
svg.append("g")
svg.property("value", [])
return svg.node();
}
Insert cell
run_gradient_descent = (f, fprime, xinit) => {
const n = 21;
const stepsize = 1/(Math.max(alpha, beta));
// Start at the center of the field.
let vx = xinit[0];
let vy = xinit[1];
let gap = f(vx, vy);
let pre_gap = gap;
var grad = [0,0];
let i_prime = 0;
let below = false;
const data = [];
data.push({
step: i_prime,
x: vx,
y: vy,
gap: gap});
for (let i = 1; i < n; i++) {
grad = fprime(vx, vy)
pre_gap = gap;
vx -= stepsize*grad[0];
vy -= stepsize*grad[1];
gap = f(vx, vy);
if (gap < 10**(min_exponent)) {
i_prime = i - 1 + (Math.log10(pre_gap) - min_exponent)/(Math.log10(pre_gap/gap));
gap = 10**(min_exponent);
below = true;
}
else {
i_prime = i;
}
data.push({
step: i_prime,
x: vx,
y: vy,
gap: gap
});
if (below)
break
}
return data;
}
Insert cell
import {Range} from "@observablehq/inputs"
Insert cell
//x0 = rgb[1]
Insert cell
//m = rgb[0]
Insert cell
strong_convex_widget = (f, f_prime, xmin, xmax) => {
const g = x => f(x0) + f_prime(x0)*(x-x0) + m/2*(x - x0)**2;
const height = 320;
const width = 480;
const margin = {top: 20, right: 30, bottom: 20, left: 40};
const x = d3.scaleLinear().domain([xmin, xmax]).range([margin.left, width - margin.right]);
const y = d3.scaleLinear().domain([-1, 4]).range([height - margin.bottom, margin.top]);

const svg = d3.select(DOM.svg(width, height));

svg.append("g")
.attr("transform", `translate(0,${y(0)})`)
.call(d3.axisBottom(x).ticks(5,"f"));

svg.append("g")
.attr("transform", `translate(${x(0)},0)`)
.call(d3.axisLeft(y).ticks(5, "f"));

svg.append("path")
.attr("fill", "none")
.attr("stroke", xColor)
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))(make_func_data(f, xmin, xmax)));

svg.append("path")
.attr("fill", "none")
.attr("stroke", xColor2)
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))(make_func_data(g, xmin, xmax)));

return svg.node();
}
Insert cell
plot_1d_function = (func, xmin, xmax) => {

const height = 320;
const width = 480;
const margin = {top: 20, right: 30, bottom: 20, left: 40};
const x = d3.scaleLinear().domain([xmin, xmax]).range([margin.left, width - margin.right]);
const y = d3.scaleLinear().domain([-1, 4]).range([height - margin.bottom, margin.top]);

const svg = d3.select(DOM.svg(width, height));

svg.append("g")
.attr("transform", `translate(0,${y(0)})`)
.call(d3.axisBottom(x).ticks(5,"f"));

svg.append("g")
.attr("transform", `translate(${x(0)},0)`)
.call(d3.axisLeft(y).ticks(5, "f"));

svg.append("path")
.attr("fill", "none")
.attr("stroke", xColor)
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))(make_func_data(func, xmin, xmax)));

return svg.node();
}
Insert cell
make_func_data = (f, xmin, xmax) => {
const n = 50;
const stepsize = (xmax - xmin)/n;
// Start at the center of the field.
let vx = xmin;
const data = [];
for (let i = 0; i < n; i++) {
// Random walk with large or small steps.
data.push({
step: i,
x: vx += stepsize,
y: f(vx)
});
}
return data;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
generate_grid = (val, x, y, width, height) => {
const q = 4; // The level of detail, e.g., sample every 4 pixels in x and y.
const x0 = -q / 2, x1 = width + q;
const y0 = -q / 2, y1 = height + q;
const n = Math.ceil((x1 - x0) / q);
const m = Math.ceil((y1 - y0) / q);
const grid = new Array(n * m);
for (let j = 0; j < m; ++j) {
for (let i = 0; i < n; ++i) {
grid[j * n + i] = val(x.invert(i * q + x0), y.invert(j * q + y0));
}
}
grid.x = -q;
grid.y = -q;
grid.k = q;
grid.n = n;
grid.m = m;
return grid;
}
Insert cell
generate_transform = gr => {
return ({type, value, coordinates}) => {
return {type, value, coordinates: coordinates.map(rings => {
return rings.map(points => {
return points.map(([x, y]) => ([
gr.x + gr.k * x,
gr.y + gr.k * y
]));
});
})};
}
}
Insert cell
Insert cell
generate_contours = (val, x, y, thresholds, width, height) => {
const grid = generate_grid(val, x, y, width, height);
const contours = d3.contours()
.size([grid.n, grid.m])
.thresholds(thresholds)
(grid)
.map(generate_transform(grid));
return contours;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//width = 400;
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more