@ruvector/postgres-cli
Version:
Advanced AI vector database CLI for PostgreSQL - pgvector drop-in replacement with 53+ SQL functions, 39 attention mechanisms, GNN layers, hyperbolic embeddings, and self-learning capabilities
285 lines (277 loc) • 12.9 kB
JavaScript
/**
* Hyperbolic Geometry Commands
* CLI commands for hyperbolic embedding operations (Poincare ball, Lorentz model)
*
* NOTE: These functions require the hyperbolic geometry module to be enabled
* in the RuVector PostgreSQL extension. Currently in development.
*/
import chalk from 'chalk';
import ora from 'ora';
const HYPERBOLIC_REQUIRES_EXTENSION_MSG = `
${chalk.yellow('Hyperbolic geometry requires the RuVector PostgreSQL extension.')}
Ensure you have:
1. Built the ruvector-postgres Docker image
2. Started a container with the extension installed
3. Run: CREATE EXTENSION ruvector;
Available functions:
- ruvector_poincare_distance(a, b, curvature)
- ruvector_lorentz_distance(a, b, curvature)
- ruvector_mobius_add(a, b, curvature)
- ruvector_exp_map(base, tangent, curvature)
- ruvector_log_map(base, target, curvature)
- ruvector_poincare_to_lorentz(poincare, curvature)
- ruvector_lorentz_to_poincare(lorentz, curvature)
- ruvector_minkowski_dot(a, b)
${chalk.gray('See: https://github.com/ruvnet/ruvector for setup instructions.')}
`;
function checkHyperbolicAvailable() {
// Hyperbolic geometry functions are now implemented in the PostgreSQL extension
// The functions are available in ruvector--0.1.0.sql
return true;
}
export class HyperbolicCommands {
static async poincareDistance(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Computing Poincare distance...').start();
try {
await client.connect();
const a = JSON.parse(options.a);
const b = JSON.parse(options.b);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const distance = await client.poincareDistance(a, b, curvature);
spinner.succeed(chalk.green('Poincare distance computed'));
console.log(chalk.bold.blue('\nPoincare Distance:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Distance:')} ${distance.toFixed(6)}`);
console.log(` ${chalk.green('Curvature:')} ${curvature}`);
console.log(` ${chalk.green('Dimension:')} ${a.length}`);
}
catch (err) {
spinner.fail(chalk.red('Distance computation failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async lorentzDistance(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Computing Lorentz distance...').start();
try {
await client.connect();
const a = JSON.parse(options.a);
const b = JSON.parse(options.b);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const distance = await client.lorentzDistance(a, b, curvature);
spinner.succeed(chalk.green('Lorentz distance computed'));
console.log(chalk.bold.blue('\nLorentz Distance:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Distance:')} ${distance.toFixed(6)}`);
console.log(` ${chalk.green('Curvature:')} ${curvature}`);
console.log(` ${chalk.green('Dimension:')} ${a.length}`);
}
catch (err) {
spinner.fail(chalk.red('Distance computation failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async mobiusAdd(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Computing Mobius addition...').start();
try {
await client.connect();
const a = JSON.parse(options.a);
const b = JSON.parse(options.b);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const result = await client.mobiusAdd(a, b, curvature);
spinner.succeed(chalk.green('Mobius addition computed'));
console.log(chalk.bold.blue('\nMobius Addition Result:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Curvature:')} ${curvature}`);
console.log(` ${chalk.green('Result:')} [${result.map((v) => v.toFixed(4)).join(', ')}]`);
// Verify result is in ball
const norm = Math.sqrt(result.reduce((sum, v) => sum + v * v, 0));
console.log(` ${chalk.green('Result Norm:')} ${norm.toFixed(6)} ${norm < 1 ? chalk.green('(valid)') : chalk.red('(invalid)')}`);
}
catch (err) {
spinner.fail(chalk.red('Mobius addition failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async expMap(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Computing exponential map...').start();
try {
await client.connect();
const base = JSON.parse(options.base);
const tangent = JSON.parse(options.tangent);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const result = await client.expMap(base, tangent, curvature);
spinner.succeed(chalk.green('Exponential map computed'));
console.log(chalk.bold.blue('\nExponential Map Result:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Base Point:')} [${base.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Tangent Vector:')} [${tangent.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Result (on manifold):')} [${result.map((v) => v.toFixed(4)).join(', ')}]`);
}
catch (err) {
spinner.fail(chalk.red('Exponential map failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async logMap(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Computing logarithmic map...').start();
try {
await client.connect();
const base = JSON.parse(options.base);
const target = JSON.parse(options.target);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const result = await client.logMap(base, target, curvature);
spinner.succeed(chalk.green('Logarithmic map computed'));
console.log(chalk.bold.blue('\nLogarithmic Map Result:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Base Point:')} [${base.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Target Point:')} [${target.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Tangent (at base):')} [${result.map((v) => v.toFixed(4)).join(', ')}]`);
}
catch (err) {
spinner.fail(chalk.red('Logarithmic map failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async poincareToLorentz(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Converting Poincare to Lorentz...').start();
try {
await client.connect();
const poincare = JSON.parse(options.vector);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const lorentz = await client.poincareToLorentz(poincare, curvature);
spinner.succeed(chalk.green('Conversion completed'));
console.log(chalk.bold.blue('\nCoordinate Conversion:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Poincare (ball):')} [${poincare.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Lorentz (hyperboloid):')} [${lorentz.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Dimension change:')} ${poincare.length} -> ${lorentz.length}`);
}
catch (err) {
spinner.fail(chalk.red('Conversion failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async lorentzToPoincare(client, options) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Converting Lorentz to Poincare...').start();
try {
await client.connect();
const lorentz = JSON.parse(options.vector);
const curvature = options.curvature ? parseFloat(options.curvature) : -1.0;
const poincare = await client.lorentzToPoincare(lorentz, curvature);
spinner.succeed(chalk.green('Conversion completed'));
console.log(chalk.bold.blue('\nCoordinate Conversion:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Lorentz (hyperboloid):')} [${lorentz.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Poincare (ball):')} [${poincare.map((v) => v.toFixed(4)).join(', ')}]`);
console.log(` ${chalk.green('Dimension change:')} ${lorentz.length} -> ${poincare.length}`);
}
catch (err) {
spinner.fail(chalk.red('Conversion failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static async minkowskiDot(client, a, b) {
if (!checkHyperbolicAvailable()) {
console.log(HYPERBOLIC_REQUIRES_EXTENSION_MSG);
return;
}
const spinner = ora('Computing Minkowski inner product...').start();
try {
await client.connect();
const vecA = JSON.parse(a);
const vecB = JSON.parse(b);
const result = await client.minkowskiDot(vecA, vecB);
spinner.succeed(chalk.green('Minkowski inner product computed'));
console.log(chalk.bold.blue('\nMinkowski Inner Product:'));
console.log(chalk.gray('-'.repeat(40)));
console.log(` ${chalk.green('Result:')} ${result.toFixed(6)}`);
console.log(` ${chalk.gray('Note:')} Uses signature (-,+,+,...,+)`);
}
catch (err) {
spinner.fail(chalk.red('Computation failed'));
console.error(chalk.red(err.message));
}
finally {
await client.disconnect();
}
}
static showHelp() {
console.log(chalk.bold.blue('\nHyperbolic Geometry Operations:'));
console.log(chalk.gray('-'.repeat(60)));
console.log(`
${chalk.yellow('Overview:')}
Hyperbolic space is ideal for embedding hierarchical data like
taxonomies, organizational charts, and knowledge graphs.
${chalk.yellow('Models:')}
${chalk.green('Poincare Ball')} - Unit ball model, good for visualization
${chalk.green('Lorentz/Hyperboloid')} - Numerically stable, good for training
${chalk.yellow('Curvature:')}
Default curvature is -1.0. More negative = more "curved" space.
Must always be negative for hyperbolic geometry.
${chalk.yellow('Commands:')}
${chalk.green('hyperbolic poincare-distance')} - Distance in Poincare ball
${chalk.green('hyperbolic lorentz-distance')} - Distance on hyperboloid
${chalk.green('hyperbolic mobius-add')} - Hyperbolic addition
${chalk.green('hyperbolic exp-map')} - Tangent to manifold
${chalk.green('hyperbolic log-map')} - Manifold to tangent
${chalk.green('hyperbolic poincare-to-lorentz')} - Convert coordinates
${chalk.green('hyperbolic lorentz-to-poincare')} - Convert coordinates
${chalk.green('hyperbolic minkowski-dot')} - Minkowski inner product
${chalk.yellow('Use Cases:')}
- Hierarchical clustering
- Knowledge graph embeddings
- Taxonomy representation
- Social network analysis
`);
}
}
export default HyperbolicCommands;