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

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