Published
Edited
Apr 18, 2021
Insert cell
Insert cell
Insert cell
quadratic = x => x**2/2
Insert cell
f = (x => x**2*(x-1)*(x+1))
Insert cell
Insert cell
md`### Widget for convex conjugate`
Insert cell
viewof lambda = Range([-2, 2], {label: tex`\lambda`, value: 0})
Insert cell
x2_conjugate = conjugate_widget(f, -2, 2, -2, 2)
Insert cell
x2_conjugate_func = conjugate_functions(f, -2, 2, -0.5, 2)
Insert cell
viewof a = Range([-2, 2], {label: tex`\lambda`, value: 0})
Insert cell
x2_primal_problem = primal_problem(quadratic, -2, 2, -1, 2)
Insert cell
x2_dual_problem = dual_problem(quadratic, 0, 4, -1, 2)
Insert cell
dual_problem = (f, xmin, xmax, ymin, ymax) => {
const g = lambda => -(lambda**2)/2 - lambda*a
const height = 240;
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([ymin, ymax]).range([height - margin.bottom, margin.top]);

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

//plot a
const a_x = Math.min(a, 0);
const p_star = f(a_x);
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", 'red')
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))([{x: xmin, y: p_star},{x: xmax, y: p_star}]));

svg.append("path")
.attr("fill", "none")
.attr("stroke", 'black')
.attr("stroke-width", 2)
.style("stroke-dasharray", ("3,5"))
.attr("d", d3.line(d => x(d.x), d => y(d.y))([{x: xmin, y: p_star},{x: xmax, y: p_star}]));
svg.append("path")
.attr("fill", "none")
.attr("stroke", 'black')
.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
primal_problem = (f, xmin, xmax, ymin, ymax) => {
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([ymin, ymax]).range([height - margin.bottom, margin.top]);

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

//plot a
const a_x = Math.min(a, 0);
const p_star = f(a_x);
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", 'blue')
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))([{x: a, y: ymin},{x: a, y: ymax}]));

svg.append("path")
.attr("fill", "none")
.attr("stroke", 'red')
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))([{x: xmin, y: p_star},{x: xmax, y: p_star}]));
svg.append("path")
.attr("fill", "none")
.attr("stroke", 'black')
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))(make_func_data(f, xmin, xmax)));
return svg.node();
}
Insert cell
find_max = (g, xmin, xmax) => {
const n = 200;
const stepsize = (xmax - xmin)/n;
let vx = xmin;
let x_star = xmin;
let g_star = -100;
for (let i = 0; i < n; i++) {
if (g(vx) > g_star) {
g_star = g(vx);
x_star = vx;
}
vx += stepsize;
}
return [x_star, g_star];
}
Insert cell
conjugate_functions = (f, lmin, lmax, ymin, ymax) => {
const xmin = -2;
const xmax = 2;
const g = l => (x=> l * x - f(x));
const f_star = la => Math.max.apply(Math,make_y_data(g(la), xmin, xmax));
const height = 240;
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([ymin, ymax]).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", 'black')
.attr("stroke-width", 2)
.attr("d", d3.line(d => x(d.x), d => y(d.y))(make_func_data(f_star, lmin, lmax)));
svg
.append('circle')
.attr('cx', x(lambda))
.attr('cy', y(f_star(lambda)))
.attr('r', 5)
.style('fill', 'red');
return svg.node();
}
Insert cell
conjugate_widget = (f, xmin, xmax, ymin, ymax) => {
const g = x=> lambda * x - f(x);
const g_l = l => (x=> l * x - f(x));
const x_star = find_max(g_l(lambda), 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([ymin, ymax]).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(g, xmin, xmax)));
svg
.append('circle')
.attr('cx', x(x_star[0]))
.attr('cy', y(x_star[1]))
.attr('r', 5)
.style('fill', 'red');

return svg.node();
}
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
vl.markCircle() // Make a scatter chart
.data(cars) // Using the cars data (below)
.encode(
vl.x().fieldQ("Horsepower"), // For x, use the Horsepower field
vl.y().fieldQ("Miles_per_Gallon"), // For y, use the Miles_per_Gallon field
vl.tooltip().fieldN("Name") // For tooltips, show the Name field
)
.render() // Draw the chart
Insert cell
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Plots a function using a generated sequence.",
"width": 300,
"height": 150,
"data": {"sequence": {"start": 0, "stop": 12.7, "step": 0.1, "as": "x"}},
"transform": [{"calculate": "sin(datum.x)", "as": "sin(x)"}],
"mark": "line",
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "sin(x)", "type": "quantitative"}
}
}
Insert cell
cars = FileAttachment("cars.json").json()
Insert cell
Insert cell
Insert cell
padding = 35;
Insert cell
size = (width - (2 + 1) * padding) / 2 + padding
Insert cell
import {Range} from "@observablehq/inputs"
Insert cell
//x0 = rgb[1]
Insert cell
//m = rgb[0]
Insert cell
plot_1d_function = (func, xmin, xmax, ymin, ymax) => {

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([ymin, ymax]).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, 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,
y: f(vx)
});
vx += stepsize;
}
return data;
}
Insert cell
make_y_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(f(vx));
vx += stepsize;
}
return data;
}
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
//width = 400;
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more