Applies to VAST Cluster 5.5.
Functions › Numeric and math functions › Powers and logarithms. Returns the natural logarithm of the absolute value of the gamma function: ln(abs(gamma(x))), computed directly, so it stays finite where gamma(x) itself is too large for a DOUBLE. lgamma(172) is 711.71472580229, while gamma(172) is inf. It has no aliases.
Syntax
lgamma(x)
| Argument | Type | Description |
|---|---|---|
x |
DOUBLE (any integer, DECIMAL or FLOAT is converted) |
The number to take the log-gamma of. It can't be 0. |
Returns: DOUBLE.
Aliases: none.
Set up the examples
The examples connect with the ADBC driver, as in VAST Query Engine (ADBC), and work in a schema called docs_sql_fn_numeric_lgamma. Set QE_ENDPOINT, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and VASTDB_BUCKET (SQL reference), and run the blocks in order in one Python session. Below each block is the output you should see.
This block connects, defines the run() and plan() helpers, and creates a table of four group sizes n, each with a number k of members to choose from it. The last row has no values:
import os
import re
import adbc_driver_manager
import adbc_driver_manager.dbapi as dbapi
import adbc_driver_vastdb
BUCKET = os.environ["VASTDB_BUCKET"]
SCHEMA = f'"{BUCKET}/docs_sql_fn_numeric_lgamma"'
conn = dbapi.connect(
driver=adbc_driver_vastdb.get_driver_path(),
db_kwargs={
"vast.db.endpoint": os.environ["QE_ENDPOINT"],
"vast.db.access_key": os.environ["AWS_ACCESS_KEY_ID"],
"vast.db.secret_key": os.environ["AWS_SECRET_ACCESS_KEY"],
},
autocommit=True,
)
def server_message(e):
"""The server's message in a driver error, without the request ID or the echoed statement."""
text = re.sub(r"\[req=0x[0-9a-f]+\]: ", "", str(e))
found = (re.search(r'message: "(.*?)", request_id', text, re.S)
or re.search(r"description: (.*)\)", text, re.S))
message = found.group(1) if found else text.strip().splitlines()[-1]
message = re.sub(r"^Plan query: '.*?' failed with: ", "", message, flags=re.S)
message = re.split(r"\\n|\n", message)[0].replace('\\"', '"')
return message.replace(BUCKET, "<bucket>")
def run(sql):
"""Run one statement. Print the rows it returns, or the error."""
try:
with conn.cursor() as cur:
cur.execute(sql)
rows = cur.fetchall() if cur.description else []
except (adbc_driver_manager.Error, OSError) as e:
print(f"{type(e).__name__}: {server_message(e)}")
else:
print(rows)
def plan(sql):
"""Print the plan without its ~N Rows lines: the table's row count, which can still be 0 just after an insert."""
with conn.cursor() as cur:
cur.execute("EXPLAIN " + sql)
steps = cur.fetchall()[0][1]
print("\n".join(line for line in steps.splitlines() if "Rows" not in line))
TABLE = f"{SCHEMA}.groups"
run(f"CREATE SCHEMA IF NOT EXISTS {SCHEMA}")
run(f"CREATE TABLE IF NOT EXISTS {TABLE} (id BIGINT, n DOUBLE, k DOUBLE)")
run(f"INSERT INTO {TABLE} VALUES "
"(1, 5, 2), "
"(2, 52, 5), "
"(3, 200, 2), "
"(4, NULL, NULL)")
[]
[]
[(4,)]
What it does
lgamma(x) is the natural logarithm of the absolute value of gamma(x). For a positive whole number n, that's the logarithm of (n − 1)!: lgamma(5) is 3.1780538303479458, the logarithm of 24, and exp(lgamma(5)) gives back 24.000000000000004. lgamma(1) and lgamma(2) are both 0.0, because 0! and 1! are 1.
Because it computes the logarithm directly, lgamma() stays finite far beyond the range of gamma(): lgamma(172) is 711.71472580229 and lgamma(1e300) is 6.897755278982137e+302, where gamma() returns inf. Use it when you need ratios or products of large factorials: subtract and add logarithms, then take exp of the result.
It takes the absolute value first, so it doesn't show the sign of a negative gamma(): gamma(-0.5) is -3.5449077018110318, and lgamma(-0.5) is 1.2655121234846454, the logarithm of 3.5449… At a negative whole number, where the gamma function isn't defined, lgamma() returns inf. At 0 it is an error.
The argument is converted to DOUBLE, and so is the result. A string of digits is converted too: lgamma('12') returns 17.502307845873887.
NULLs and edge cases
| Input | Result |
|---|---|
NULL |
NULL |
1, 2 |
0.0 |
0.5 |
0.5723649429247001 |
172 |
711.71472580229 |
1e300 |
6.897755278982137e+302 |
-0.5 |
1.2655121234846454 |
-1, -2 (negative whole numbers) |
inf |
'inf'::DOUBLE, '-inf'::DOUBLE |
inf |
'nan'::DOUBLE |
nan |
'12' (a string of digits) |
17.502307845873887 |
0 or -0.0::DOUBLE |
Error: cannot take log gamma of zero |
'abc' |
Error: Could not convert string 'abc' to DOUBLE |
A BLOB, date, boolean, list or interval |
Error: No function matches the given name and argument types, for example 'lgamma(DATE)' |
The same cases, as the cluster returns them. They use the helpers from Set up the examples:
run("SELECT lgamma(NULL), lgamma(1), lgamma(2), lgamma(0.5), lgamma(172), lgamma(1e300)")
run("SELECT lgamma(-0.5), lgamma(-1), lgamma(-2)")
run("SELECT lgamma('inf'::DOUBLE), lgamma('-inf'::DOUBLE), lgamma('nan'::DOUBLE)")
run("SELECT lgamma('12')")
run("SELECT lgamma(0)")
run("SELECT lgamma(-0.0::DOUBLE)")
run("SELECT lgamma('abc')")
run("SELECT lgamma('\\xAA'::BLOB)")
run("SELECT lgamma(DATE '2026-10-16')")
run("SELECT lgamma(true)")
run("SELECT lgamma([1, 2])")
run("SELECT lgamma(INTERVAL 1 DAY)")
[(None, 0.0, 0.0, 0.5723649429247001, 711.71472580229, 6.897755278982137e+302)]
[(1.2655121234846454, inf, inf)]
[(inf, inf, nan)]
[(17.502307845873887,)]
OSError: cannot take log gamma of zero
OSError: cannot take log gamma of zero
OSError: Could not convert string 'abc' to DOUBLE
InternalError: Binder Error: No function matches the given name and argument types 'lgamma(BLOB)'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'lgamma(DATE)'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'lgamma(BOOLEAN)'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'lgamma(INTEGER[])'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'lgamma(INTERVAL)'. You might need to add explicit type casts.
Examples
The number of ways to choose k members of a group of n, n! / (k! (n − k)!), computed from logarithms so that no factorial has to fit in a DOUBLE on its own. lgamma(n + 1) is the logarithm of n!:
run(f"SELECT id, n, k, exp(lgamma(n + 1) - lgamma(k + 1) - lgamma(n - k + 1)) FROM {TABLE} ORDER BY id LIMIT 10")
[(1, 5.0, 2.0, 9.999999999999998), (2, 52.0, 5.0, 2598960.000000018), (3, 200.0, 2.0, 19899.999999999513), (4, None, None, None)]
The result is a DOUBLE, so it carries floating-point rounding: round it with round if you need a whole number.
Keep only the groups whose log-factorial is more than 10:
run(f"SELECT id, n FROM {TABLE} WHERE lgamma(n + 1) > 10 ORDER BY id LIMIT 10")
[(2, 52.0), (3, 200.0)]
How it runs in VAST
The Query Engine runs on the cluster's CNodes, and a query on a table starts with a scan in VAST DataBase (VAST_SCAN). VAST DataBase can evaluate a comparison of a column with a constant, but not a function of a column, so lgamma() is always computed by the Query Engine:
- In the
SELECTlist, the scan reads the column and the Query Engine computeslgamma()for each row it returns. - In
WHERE, the scan sends the column for every row, and aFILTERstep in the Query Engine computeslgamma()and keeps the rows that match. No rows are skipped in storage.
The plan of the WHERE example shows the FILTER step above the scan:
plan(f"SELECT id FROM {TABLE} WHERE lgamma(n + 1) > 10")
┌───────────────────────────┐
│ PROJECTION │
│ ──────────────────── │
│ #1 │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ FILTER │
│ ──────────────────── │
│ (lgamma((n + 1.0)) > 10.0)│
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ │
│ Projections: │
│ n │
│ id │
│ │
└───────────────────────────┘
Make it faster
- Compare the column where you can. For arguments of 2 or more,
lgamma()grows with its argument, so a threshold onlgamma(n + 1)is a threshold onn. A comparison of the raw column with a constant is pushed down to VAST DataBase, so rows that don't match never leave storage. The plan forn > 10has noFILTERstep:
plan(f"SELECT id FROM {TABLE} WHERE n > 10")
┌───────────────────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ Projections: id │
│ │
└───────────────────────────┘
- Store the value if you filter on it often. Compute
lgamma()when you write the rows and keep it in its own column: a comparison of that column is pushed down,lgamma()inWHEREisn't. - Combine it with a pushed-down condition. In
id > 100 AND lgamma(n + 1) > 10,id > 100is still evaluated in VAST DataBase, solgamma()runs only on the rows that pass it.
Limitations and errors
- Zero is an error.
lgamma(0)andlgamma(-0.0::DOUBLE)fail withcannot take log gamma of zero. On a table, one zero fails the whole query, not just that row. Keep zeros out of the column, or filter them out with a condition on the raw column. - Negative whole numbers give
inf, not an error orNULL. Test for it with isinf. - No sign.
lgamma()is the logarithm of the absolute value; for the sign of the gamma function of a negative number, use gamma or sign(gamma(x))wheregamma()doesn't overflow. - Floating point, not exact.
exp(lgamma(6))is119.99999999999997, not 120. For exact factorials of anINTEGER, use factorial. - Numbers only. A
BLOB, a date, a boolean, a list or an interval fails withNo function matches the given name and argument types. A string is converted toDOUBLE, so a string that isn't a number fails withCould not convert string ... to DOUBLE.
run("SELECT lgamma(-0.5), gamma(-0.5), exp(lgamma(6))")
[(1.2655121234846454, -3.5449077018110318, 119.99999999999997)]
Similar functions
| Function | Computes | Result for 172 |
Result for 6 |
|---|---|---|---|
lgamma(x) |
ln of the absolute value of the gamma function | 711.71472580229 |
4.787491742782046 (the logarithm of 120) |
| gamma | the gamma function | inf |
120.0 |
exp(lgamma(x)) |
the gamma function's absolute value, from lgamma |
119.99999999999997 |
|
factorial (x - 1) |
the exact factorial of an INTEGER |
factorial(5) = 120 |
|
| ln | the natural logarithm |
run("SELECT lgamma(172), gamma(172)")
run("SELECT lgamma(6), gamma(6), exp(lgamma(6)), factorial(5)")
[(711.71472580229, inf)]
[(4.787491742782046, 120.0, 119.99999999999997, Decimal('120'))]
Clean up:
run(f"DROP TABLE IF EXISTS {TABLE}")
run(f"DROP SCHEMA IF EXISTS {SCHEMA}")
conn.close()
[]
[]
See also
- Numeric and math functions: all the powers and logarithms.
- gamma, factorial, exp and ln.
- Where filters run and pushdowns.
- EXPLAIN: how to read a plan.