Skip to content

Commit

Permalink
Feat: Add promql trigonometric functions (#940)
Browse files Browse the repository at this point in the history
* Allow parsing promql trigonometric functions

* Add promql trigonometric functions processing logic

* Add metrics prompt json

* Add unit tests

* Fix up
  • Loading branch information
Jiale-Fang committed May 23, 2024
1 parent 8446cf5 commit 862aad7
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 0 deletions.
24 changes: 24 additions & 0 deletions pkg/integrations/prometheus/promql/metricsSearchHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,30 @@ func convertPqlToMetricsQuery(searchText string, startTime, endTime uint32, myid
mquery.Function = structs.Function{MathFunction: segutils.Deg}
case "rad":
mquery.Function = structs.Function{MathFunction: segutils.Rad}
case "acos":
mquery.Function = structs.Function{MathFunction: segutils.Acos}
case "acosh":
mquery.Function = structs.Function{MathFunction: segutils.Acosh}
case "asin":
mquery.Function = structs.Function{MathFunction: segutils.Asin}
case "asinh":
mquery.Function = structs.Function{MathFunction: segutils.Asinh}
case "atan":
mquery.Function = structs.Function{MathFunction: segutils.Atan}
case "atanh":
mquery.Function = structs.Function{MathFunction: segutils.Atanh}
case "cos":
mquery.Function = structs.Function{MathFunction: segutils.Cos}
case "cosh":
mquery.Function = structs.Function{MathFunction: segutils.Cosh}
case "sin":
mquery.Function = structs.Function{MathFunction: segutils.Sin}
case "sinh":
mquery.Function = structs.Function{MathFunction: segutils.Sinh}
case "tan":
mquery.Function = structs.Function{MathFunction: segutils.Tan}
case "tanh":
mquery.Function = structs.Function{MathFunction: segutils.Tanh}
case "clamp":
if len(expr.Args) != 3 {
return fmt.Errorf("parser.Inspect: Incorrect parameters: %v for the clamp function", expr.Args.String())
Expand Down
84 changes: 84 additions & 0 deletions pkg/integrations/prometheus/promql/metricsconsts.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,90 @@ const metricFunctions = `[
"eg": "rad(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "acos",
"name": "Arccosine (acos)",
"desc": "Calculates the arccosine of all elements in v.",
"eg": "acos(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "acosh",
"name": "Inverse hyperbolic cosine (acosh)",
"desc": "Calculates the inverse hyperbolic cosine of all elements in v.",
"eg": "acosh(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "asin",
"name": "Arcsine (asin)",
"desc": "Calculates the arcsine of all elements in v.",
"eg": "asin(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "asinh",
"name": "Inverse hyperbolic sine (asinh)",
"desc": "Calculates the inverse hyperbolic sine of all elements in v.",
"eg": "asinh(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "atan",
"name": "Arctangent (atan)",
"desc": "Calculates the arctangent of all elements in v.",
"eg": "atan(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "atanh",
"name": "Inverse hyperbolic tangent (atanh)",
"desc": "Calculates the inverse hyperbolic tangent of all elements in v.",
"eg": "atanh(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "cos",
"name": "Cosine (cos)",
"desc": "Calculates the cosine of all elements in v.",
"eg": "cos(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "cosh",
"name": "Hyperbolic cosine (cosh)",
"desc": "Calculates the hyperbolic cosine of all elements in v.",
"eg": "cosh(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "sin",
"name": "Sine (sin)",
"desc": "Calculates the sine of all elements in v.",
"eg": "sin(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "sinh",
"name": "Hyperbolic sine (sinh)",
"desc": "Calculates the hyperbolic sine of all elements in v.",
"eg": "sinh(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "tan",
"name": "Tangent (tan)",
"desc": "Calculates the tangent of all elements in v.",
"eg": "tan(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "tanh",
"name": "Hyperbolic tangent (tanh)",
"desc": "Calculates the hyperbolic tangent of all elements in v.",
"eg": "tanh(avg (system.disk.used))",
"isTimeRangeFunc": false
},
{
"fn": "clamp",
"name": "Clamp",
Expand Down
57 changes: 57 additions & 0 deletions pkg/segment/results/mresults/seriesresult.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,51 @@ func ApplyMathFunction(ts map[uint32]float64, function structs.Function) (map[ui
evaluate(ts, func(val float64) float64 {
return val * math.Pi / 180
})

case segutils.Acos:
err = evaluateWithErr(ts, func(val float64) (float64, error) {
if val < -1 || val > 1 {
return val, fmt.Errorf("evaluateWithErr: acos evaluate values in the range [-1,1]")
}
return math.Acos(val), nil
})
case segutils.Acosh:
err = evaluateWithErr(ts, func(val float64) (float64, error) {
if val < 1 {
return val, fmt.Errorf("evaluateWithErr: acosh evaluate values in the range [1,+Inf]")
}
return math.Acosh(val), nil
})
case segutils.Asin:
err = evaluateWithErr(ts, func(val float64) (float64, error) {
if val < -1 || val > 1 {
return val, fmt.Errorf("evaluateWithErr: asin evaluate values in the range [-1,1]")
}
return math.Asin(val), nil
})
case segutils.Asinh:
evaluate(ts, math.Asinh)
case segutils.Atan:
evaluate(ts, math.Atan)
case segutils.Atanh:
err = evaluateWithErr(ts, func(val float64) (float64, error) {
if val <= -1 || val >= 1 {
return val, fmt.Errorf("evaluateWithErr: atanh evaluate values in the range [-1,1]")
}
return math.Atanh(val), nil
})
case segutils.Cos:
evaluate(ts, math.Cos)
case segutils.Cosh:
evaluate(ts, math.Cosh)
case segutils.Sin:
evaluate(ts, math.Sin)
case segutils.Sinh:
evaluate(ts, math.Sinh)
case segutils.Tan:
evaluate(ts, math.Tan)
case segutils.Tanh:
evaluate(ts, math.Tanh)
case segutils.Clamp:
if len(function.ValueList) != 2 {
return ts, fmt.Errorf("ApplyMathFunction: clamp has incorrect parameters: %v", function.ValueList)
Expand Down Expand Up @@ -783,13 +828,25 @@ func reduceRunningEntries(entries []RunningEntry, fn utils.AggregateFunctions, f
}

type float64Func func(float64) float64
type float64FuncWithErr func(float64) (float64, error)

func evaluate(ts map[uint32]float64, mathFunc float64Func) {
for key, val := range ts {
ts[key] = mathFunc(val)
}
}

func evaluateWithErr(ts map[uint32]float64, mathFunc float64FuncWithErr) error {
for key, val := range ts {
resVal, err := mathFunc(val)
if err != nil {
return err
}
ts[key] = resVal
}
return nil
}

func applyFuncToNonNegativeValues(ts map[uint32]float64, mathFunc float64Func) error {
for key, val := range ts {
if val < 0 {
Expand Down
110 changes: 110 additions & 0 deletions pkg/segment/results/mresults/seriesresult_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/siglens/siglens/pkg/common/dtypeutils"
"github.com/siglens/siglens/pkg/segment/structs"
"github.com/siglens/siglens/pkg/segment/utils"
segutils "github.com/siglens/siglens/pkg/segment/utils"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -1391,6 +1392,115 @@ func Test_applyMathFunctionRad(t *testing.T) {
}
}

func Test_applyTrigonometricFunctionCos(t *testing.T) {
runTrigonometricFunctionTest(t, math.Cos, segutils.Cos, false)
}

func Test_applyTrigonometricFunctionCosh(t *testing.T) {
runTrigonometricFunctionTest(t, math.Cosh, segutils.Cosh, false)
}

func Test_applyTrigonometricFunctionSin(t *testing.T) {
runTrigonometricFunctionTest(t, math.Sin, segutils.Sin, false)
}

func Test_applyTrigonometricFunctionSinh(t *testing.T) {
runTrigonometricFunctionTest(t, math.Sinh, segutils.Sinh, false)
}

func Test_applyTrigonometricFunctionTan(t *testing.T) {
runTrigonometricFunctionTest(t, math.Tan, segutils.Tan, false)
}

func Test_applyTrigonometricFunctionTanh(t *testing.T) {
runTrigonometricFunctionTest(t, math.Tanh, segutils.Tanh, false)
}

func Test_applyTrigonometricFunctionAsinh(t *testing.T) {
runTrigonometricFunctionTest(t, math.Asinh, segutils.Asinh, false)
}

func Test_applyTrigonometricFunctionAtan(t *testing.T) {
runTrigonometricFunctionTest(t, math.Atan, segutils.Atan, false)
}

func Test_applyTrigonometricFunctionAcos(t *testing.T) {
runTrigonometricFunctionTest(t, math.Acos, segutils.Acos, true)
}

func Test_applyTrigonometricFunctionAsin(t *testing.T) {
runTrigonometricFunctionTest(t, math.Asin, segutils.Asin, true)
}

func Test_applyTrigonometricFunctionAtanh(t *testing.T) {
runTrigonometricFunctionTest(t, math.Atanh, segutils.Atanh, true)
}

func Test_applyTrigonometricFunctionAcosh(t *testing.T) {
runTrigonometricFunctionTest(t, math.Acosh, segutils.Acosh, true)
}

func runTrigonometricFunctionTest(t *testing.T, mathFunc float64Func, mathFunction utils.MathFunctions, testError bool) {
result := make(map[string]map[uint32]float64)
ts := make(map[uint32]float64)

// Define initial values based on whether we're testing an error case
if mathFunction == utils.Acosh {
ts[1] = 1.255
ts[2] = 6
ts[3] = 2.465
} else if testError {
ts[1] = 0.255
ts[2] = 0.6
ts[3] = -0.2465
} else {
ts[1] = -0.255
ts[2] = 0.6
ts[3] = 11.2465
}

result["metric"] = ts
ans := make(map[uint32]float64)
for key, val := range ts {
ans[key] = mathFunc(val)
}

metricsResults := &MetricsResult{
Results: result,
}

function := structs.Function{MathFunction: mathFunction}
err := metricsResults.ApplyFunctionsToResults(8, function)
assert.Nil(t, err)
for _, timeSeries := range metricsResults.Results {
for key, val := range timeSeries {

expectedVal, exists := ans[key]
if !exists {
t.Errorf("Should not have this key: %v", key)
}

if val != expectedVal {
t.Errorf("Expected value should be %v, but got %v", expectedVal, val)
}
}
}

if testError {
// Modify values to trigger error
ts[3] = -10.2465
err = metricsResults.ApplyFunctionsToResults(8, function)
assert.NotNil(t, err)
} else {
// Add specific test for acosh case where valid input should be > 1
if mathFunction == utils.Acosh {
ts[3] = 0.2465
err = metricsResults.ApplyFunctionsToResults(8, function)
assert.NotNil(t, err)
}
}
}

func Test_applyMathFunctionClamp(t *testing.T) {
result := make(map[string]map[uint32]float64)
ts := make(map[uint32]float64)
Expand Down
12 changes: 12 additions & 0 deletions pkg/segment/utils/segconsts.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,18 @@ const (
Clamp_Max
Clamp_Min
Timestamp
Acos
Acosh
Asin
Asinh
Atan
Atanh
Cos
Cosh
Sin
Sinh
Tan
Tanh
)

type RangeFunctions int
Expand Down

0 comments on commit 862aad7

Please sign in to comment.