Rust library for computing differentiable shape intersections with automatic differentiation. Used by apvd for area-proportional Venn diagram generation.
- Automatic differentiation: Compute gradients of region areas w.r.t. shape parameters
- Multiple shape types: Circles, axis-aligned ellipses (XYRR), rotated ellipses (XYRRT), polygons
- WASM support: Run in the browser via WebAssembly
- Native CLI: Faster training with parallel scene optimization
- WebSocket server: Real-time training updates for frontends
shapes/
├── apvd-core/ # Core computation library (platform-agnostic)
├── apvd-wasm/ # WASM bindings + WorkerTrainingClient for browser
├── apvd-cli/ # Native CLI and WebSocket server
└── client/ # @apvd/client - TypeScript types + WebSocketTrainingClient
cd apvd-wasm && wasm-pack build --target webOutput: apvd-wasm/pkg/
# Build @apvd/client (types + WebSocketTrainingClient)
cd client && pnpm build
# Build apvd-wasm TypeScript (WorkerTrainingClient)
cd apvd-wasm/ts && pnpm buildcargo build -p apvd-cli --releaseOutput: target/release/apvd
Two packages provide a unified TrainingClient interface:
@apvd/client: Types +WebSocketTrainingClient(connects toapvd serve)apvd-wasm: WASM +WorkerTrainingClient(runs training in Web Worker)
// WebSocket transport (native Rust server)
import { createTrainingClient } from "@apvd/client";
const client = createTrainingClient({
transport: "websocket",
url: "ws://localhost:8080"
});
// Worker transport (WASM in browser) - use apvd-wasm package
import { createWorkerTrainingClient } from "apvd-wasm/client";
const client = createWorkerTrainingClient();
// Both clients share the same API
const handle = await client.startTraining({
inputs: [
[{ kind: "Circle", c: { x: 0, y: 0 }, r: 1 }, [true, true, true]],
[{ kind: "Circle", c: { x: 1, y: 0 }, r: 1 }, [true, true, true]],
],
targets: { "0*": 3, "*1": 5, "01": 1 },
});
client.onProgress((update) => {
console.log(`Step ${update.currentStep}, error: ${update.error}`);
});import init, { make_model, train_robust, step, is_converged } from 'apvd-wasm';
await init();
// Define shapes with trainable parameters
const inputs = [
[{ kind: "Circle", c: { x: 0, y: 0 }, r: 1 }, [[1], [1], [1]]], // cx, cy, r trainable
[{ kind: "Circle", c: { x: 1, y: 0 }, r: 1 }, [[1], [0], [1]]], // cx, r trainable (cy fixed)
];
// Target area fractions
const targets = { "10": 0.3, "01": 0.3, "11": 0.4 };
// Train
const model = make_model(inputs, targets);
const trained = train_robust(model, 1000);
console.log(`Final error: ${trained.steps.at(-1).error.v}`);# Batch training
apvd train -s shapes.json -t targets.json -m 1000 -p 6
# WebSocket server for frontend
apvd serve -p 8080Connect to ws://host:port/ws:
// Start training
ws.send(JSON.stringify({
type: "StartTraining",
shapes: [...],
targets: {...},
max_steps: 1000
}));
// Receive updates
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "StepUpdate") {
console.log(`Step ${msg.step_idx}: error=${msg.error}`);
}
};- Scene analysis: Find all intersection points between shapes (quartic solver for ellipses)
- Region computation: Build boundary graph and compute signed area of each region
- Gradient descent: Autodiff computes error gradients; optimizer updates shape parameters
- Convergence: Repeat until error threshold reached or max steps
# Run tests
cargo test
# Run specific test
cargo test -p apvd-core fizz_buzz_bazz
# With logging
RUST_LOG=debug cargo testMIT