Skip to main content

Advanced Weight Expressions  

Helix supports sophisticated mathematical expressions for calculating path weights. By combining mathematical functions with property contexts, you can model complex real-world routing scenarios with exponential decay, multi-factor scoring, conditional logic, and more.
All mathematical functions available in Helix can be used in weight expressions. See the Mathematical Functions reference for the complete list.

Available Mathematical Functions

Arithmetic Operations

  • ADD(a, b) - Addition
  • SUB(a, b) - Subtraction
  • MUL(a, b) - Multiplication
  • DIV(a, b) - Division
  • MOD(a, b) - Modulo (remainder)

Power & Exponential

  • POW(base, exponent) - Power
  • SQRT(x) - Square root
  • EXP(x) - e^x (exponential)
  • LN(x) - Natural logarithm
  • LOG(x, base) - Logarithm with custom base

Rounding Functions

  • CEIL(x) - Round up
  • FLOOR(x) - Round down
  • ROUND(x) - Round to nearest
  • ABS(x) - Absolute value

Trigonometric Functions

  • SIN(x) - Sine
  • COS(x) - Cosine
  • TAN(x) - Tangent

Constants

  • PI() - Ï€ (3.14159…)

Example 1: Exponential time decay

QUERY FindFreshestPath(start_id: ID, end_id: ID) =>
    result <- N<DataCenter>(start_id)
        ::ShortestPathDijkstras<Link>(
            MUL(_::{distance}, POW(0.95, DIV(_::{days_since_update}, 30)))
        )
        ::To(end_id)
    RETURN result

QUERY CreateDataCenter(name: String) =>
    datacenter <- AddN<DataCenter>({ name: name })
    RETURN datacenter

QUERY CreateLink(from_id: ID, to_id: ID, distance: F64, days_old: I64) =>
    link <- AddE<Link>({ distance: distance, days_since_update: days_old })::From(from_id)::To(to_id)
    RETURN link
Here’s how to run the query using the SDKs or curl
from helix.client import Client

client = Client(local=True, port=6969)

# Weight = distance * 0.95^(days_since_update/30)
# Exponential decay: fresher routes get lower weights

# Create data centers
datacenters = {}
for name in ["DC1", "DC2", "DC3", "DC4", "Target"]:
    result = client.query("CreateDataCenter", {"name": name})
    datacenters[name] = result["datacenter"]["id"]

# Create links with varying freshness
# Format: (from, to, distance, days_old)
links = [
    ("DC1", "DC2", 100.0, 0),     # Fresh route
    ("DC1", "DC3", 90.0, 60),     # Stale route (2 months)
    ("DC2", "Target", 120.0, 10), # Recent route
    ("DC3", "DC4", 80.0, 90),     # Very stale (3 months)
    ("DC4", "Target", 110.0, 5),  # Fresh route
]

for from_dc, to_dc, distance, days_old in links:
    client.query("CreateLink", {
        "from_id": datacenters[from_dc],
        "to_id": datacenters[to_dc],
        "distance": distance,
        "days_old": days_old
    })

# Find path preferring fresh routes
result = client.query("FindFreshestPath", {
    "start_id": datacenters["DC1"],
    "end_id": datacenters["Target"]
})

print(f"Path using exponential time decay:")
print(f"Route: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Decay-adjusted weight: {result['result']['total_weight']:.2f}")
print("\nFresher routes are preferred over stale ones")
How it works:
  • POW(0.95, DIV(days_since_update, 30)) creates exponential decay
  • At 0 days: multiplier = 1.0 (no penalty)
  • At 30 days: multiplier = 0.95 (5% increase)
  • At 60 days: multiplier = 0.90 (10% increase)
  • At 90 days: multiplier = 0.86 (14% increase)

Example 2: Multi-factor composite scoring

QUERY FindOptimalPath(start_id: ID, end_id: ID) =>
    result <- N<Server>(start_id)
        ::ShortestPathDijkstras<Connection>(
            ADD(
                MUL(_::{latency}, 0.4),
                ADD(
                    MUL(DIV(1, _::{bandwidth}), 0.3),
                    MUL(
                        SUB(1, _::{reliability}),
                        0.3
                    )
                )
            )
        )
        ::To(end_id)
    RETURN result

QUERY CreateServer(name: String) =>
    server <- AddN<Server>({ name: name })
    RETURN server

QUERY CreateConnection(from_id: ID, to_id: ID, latency: F64, bandwidth: F64, reliability: F64) =>
    connection <- AddE<Connection>({ latency: latency, bandwidth: bandwidth, reliability: reliability })::From(from_id)::To(to_id)
    RETURN connection
Here’s how to run the query using the SDKs or curl
from helix.client import Client

client = Client(local=True, port=6969)

# Composite weight: 40% latency + 30% reciprocal bandwidth + 30% unreliability
# Balances multiple competing factors

# Create servers
servers = {}
for name in ["S1", "S2", "S3", "S4", "S5"]:
    result = client.query("CreateServer", {"name": name})
    servers[name] = result["server"]["id"]

# Create connections with different characteristics
# Format: (from, to, latency_ms, bandwidth_gbps, reliability_0_to_1)
connections = [
    ("S1", "S2", 10.0, 10.0, 0.99),   # Low latency, high bandwidth, reliable
    ("S1", "S3", 5.0, 5.0, 0.85),     # Lowest latency, medium bandwidth, less reliable
    ("S2", "S5", 20.0, 20.0, 0.98),   # Higher latency, highest bandwidth
    ("S3", "S4", 15.0, 8.0, 0.90),    # Balanced
    ("S4", "S5", 8.0, 12.0, 0.95),    # Good all-around
]

for from_srv, to_srv, latency, bandwidth, reliability in connections:
    client.query("CreateConnection", {
        "from_id": servers[from_srv],
        "to_id": servers[to_srv],
        "latency": latency,
        "bandwidth": bandwidth,
        "reliability": reliability
    })

# Find optimal path balancing all factors
result = client.query("FindOptimalPath", {
    "start_id": servers["S1"],
    "end_id": servers["S5"]
})

print(f"Multi-factor optimized path:")
print(f"Route: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Composite score: {result['result']['total_weight']:.3f}")
print(f"(40% latency + 30% inv_bandwidth + 30% unreliability)")
Weight breakdown:
  • 40% latency: Direct contribution (lower is better)
  • 30% reciprocal bandwidth: 1/bandwidth (lower bandwidth → higher cost)
  • 30% unreliability: (1 - reliability) (less reliable → higher cost)

Example 3: Conditional weights with thresholds

QUERY FindConditionalPath(start_id: ID, end_id: ID, threshold: F64) =>
    result <- N<Router>(start_id)
        ::ShortestPathDijkstras<Cable>(
            MUL(
                _::{length},
                ADD(
                    1,
                    MUL(CEIL(DIV(SUB(_::{capacity_used}, threshold), 10)), 0.5)
                )
            )
        )
        ::To(end_id)
    RETURN result

QUERY CreateRouter(name: String) =>
    router <- AddN<Router>({ name: name })
    RETURN router

QUERY CreateCable(from_id: ID, to_id: ID, length: F64, capacity_used: F64) =>
    cable <- AddE<Cable>({ length: length, capacity_used: capacity_used })::From(from_id)::To(to_id)
    RETURN cable
Here’s how to run the query using the SDKs or curl
from helix.client import Client

client = Client(local=True, port=6969)

# Conditional weight: Adds penalty when capacity exceeds threshold
# Weight = length * (1 + ceil((capacity_used - threshold) / 10) * 0.5)
# Penalizes overcapacity cables progressively

# Create routers
routers = {}
for name in ["R1", "R2", "R3", "R4", "R5"]:
    result = client.query("CreateRouter", {"name": name})
    routers[name] = result["router"]["id"]

# Create cables with varying capacity usage
# Format: (from, to, length_km, capacity_percent)
cables = [
    ("R1", "R2", 100.0, 45.0),   # Below threshold
    ("R1", "R3", 90.0, 85.0),    # Above threshold
    ("R2", "R4", 110.0, 50.0),   # Below threshold
    ("R3", "R4", 95.0, 95.0),    # Way above threshold
    ("R4", "R5", 105.0, 60.0),   # Slightly above threshold
]

for from_r, to_r, length, capacity in cables:
    client.query("CreateCable", {
        "from_id": routers[from_r],
        "to_id": routers[to_r],
        "length": length,
        "capacity_used": capacity
    })

# Find path with 70% capacity threshold
# Cables above 70% get progressive penalties
result = client.query("FindConditionalPath", {
    "start_id": routers["R1"],
    "end_id": routers["R5"],
    "threshold": 70.0
})

print(f"Path avoiding overcapacity cables (>70%):")
print(f"Route: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Penalty-adjusted weight: {result['result']['total_weight']:.2f}")
How conditional weighting works:
  • Below threshold: No penalty (multiplier = 1)
  • Above threshold: Progressive penalty based on excess
  • Uses CEIL to create step-function penalties
  • Every 10% over threshold adds 0.5x multiplier

Complex Expression Patterns

Vector Distance with Property Weighting

// Euclidean-style distance combining two properties
SQRT(ADD(POW(_::{distance}, 2), POW(MUL(1000, SUB(1, _::{reliability})), 2)))

Logarithmic Scaling for Large Values

// Compress large bandwidth values logarithmically
MUL(_::{distance}, LN(ADD(_::{bandwidth}, 1)))

Periodic Patterns with Trigonometry

// Prefer routes updated on certain days (weekly cycle)
MUL(_::{distance}, ADD(1, MUL(SIN(DIV(_::{days_since_update}, 7)), 0.2)))

Quantized Weights

// Round to nearest 10 for simplified routing
MUL(ROUND(DIV(_::{distance}, 10)), 10)

Performance Considerations

Expression Complexity Impact

ComplexityOperationsPerformance Impact
SimpleSingle propertyMinimal (1% overhead)
Moderate2-5 operationsLow (1-5% overhead)
Complex6-15 operationsModerate (5-15% overhead)
Very Complex15+ operationsHigher (15-30% overhead)

Optimization Tips

  1. Pre-calculate when possible: Store derived values as properties
    // Instead of: POW(0.95, DIV(days, 30))
    // Pre-calculate: decay_factor property
    
  2. Simplify expressions: Combine constants
    // Instead of: MUL(MUL(_::{x}, 0.3), 0.4)
    // Use: MUL(_::{x}, 0.12)
    
  3. Avoid expensive operations in hot paths:
    • Trigonometric functions (SIN, COS, TAN) are slower
    • Multiple POW operations add up
    • Consider lookup tables for complex functions
  4. Profile your queries: Test with realistic data volumes

Common Patterns Library

Time-based Decay

// Exponential: POW(0.95, DIV(age, period))
// Linear: MUL(distance, ADD(1, DIV(age, 100)))
// Step: MUL(distance, ADD(1, FLOOR(DIV(age, 30))))

Multi-factor Scoring

// Weighted sum: ADD(MUL(factor1, w1), MUL(factor2, w2))
// Geometric mean: SQRT(MUL(factor1, factor2))
// Harmonic mean: DIV(2, ADD(DIV(1, f1), DIV(1, f2)))

Threshold-based Penalties

// Hard threshold: IF(GT(value, thresh), penalty, 1)
// Soft threshold: ADD(1, MUL(MAX(0, SUB(value, thresh)), rate))
// Step function: CEIL(DIV(MAX(0, SUB(value, thresh)), step))

Normalization

// Min-max: DIV(SUB(value, min), SUB(max, min))
// Z-score: DIV(SUB(value, mean), stddev)
// Log scale: LN(ADD(value, 1))