Skip to content

Commit

Permalink
Exchange Rate controller
Browse files Browse the repository at this point in the history
  • Loading branch information
kiltsonfire authored and gameofpointers committed Oct 11, 2021
1 parent 30ce479 commit 252bfe6
Show file tree
Hide file tree
Showing 28 changed files with 1,687 additions and 1,133 deletions.
184 changes: 184 additions & 0 deletions common/logistic/logistic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package logistic

import (
"fmt"
"math/big"

"github.com/dominant-strategies/go-quai/common"
"github.com/dominant-strategies/go-quai/common/math"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
)

var (
c_learningRate = big.NewFloat(0.001)
c_epochLength = 100
)

// LogisticRegression represents a logistic regression model.
type LogisticRegression struct {
beta0 *big.Float // Model bias (intercept)
beta1 *big.Float // Model weight (slope)
}

// NewLogisticRegression initializes a new LogisticRegression model.
func NewLogisticRegression(beta0, beta1 *big.Float) *LogisticRegression {
return &LogisticRegression{
beta0: beta0,
beta1: beta1,
}
}

// sigmoid computes the sigmoid function.
func sigmoid(z *big.Float) *big.Float {
// Compute exp(-z)
negZ := new(big.Float).Neg(z)
expNegZ := math.EToTheX(negZ)

// Compute 1 + exp(-z)
denom := new(big.Float).Add(new(big.Float).SetInt(common.Big1), expNegZ)

// Compute 1 / (1 + exp(-z))
result := new(big.Float).Quo(new(big.Float).SetInt(common.Big1), denom)

return result
}

// Predict computes the probability that the input belongs to class 1.
func (lr *LogisticRegression) Predict(x *big.Float) *big.Float {
// z = beta0 + beta1 * x
beta1x := new(big.Float).Mul(lr.beta1, x)
z := new(big.Float).Add(lr.beta0, beta1x)

// Apply sigmoid function
return sigmoid(z)
}

// Train trains the logistic regression model using gradient descent.
func (lr *LogisticRegression) Train(x []*big.Int, y []*big.Int) {
nSamples := len(y)

var xfloat, yfloat []*big.Float
for i := 0; i < nSamples; i++ {
xfloat = append(xfloat, new(big.Float).SetInt(x[i]))
yfloat = append(yfloat, new(big.Float).SetInt(y[i]))
}

for epoch := 0; epoch < c_epochLength; epoch++ {
// Initialize gradients
dw := new(big.Float).SetInt(common.Big0)
db := new(big.Float).SetInt(common.Big0)

// Compute gradients
for i := 0; i < nSamples; i++ {
xi := xfloat[i]
yi := yfloat[i]
pred := lr.Predict(xi)
error := new(big.Float).Sub(pred, yi)
dwTerm := new(big.Float).Mul(error, xi)
dw.Add(dw, dwTerm)
db.Add(db, error)
}

nSamplesFloat := new(big.Float).SetInt(big.NewInt(int64(nSamples))) //big.NewFloat(float64(nSamples))

// Compute gradient averages
dw.Quo(dw, nSamplesFloat)
db.Quo(db, nSamplesFloat)

// Update weight: beta1 = beta1 - LearningRate * dw
lrUpdateW := new(big.Float).Mul(c_learningRate, dw)
lr.beta1.Sub(lr.beta1, lrUpdateW)

// Update bias: beta0 = beta0 - LearningRate * db
lrUpdateB := new(big.Float).Mul(c_learningRate, db)
lr.beta0.Sub(lr.beta0, lrUpdateB)
}
}

// Beta0 returns the model's bias (intercept) term.
func (lr *LogisticRegression) Beta0() *big.Float {
return new(big.Float).Set(lr.beta0)
}

// Beta1 returns the model's weight (slope) term.
func (lr *LogisticRegression) Beta1() *big.Float {
return new(big.Float).Set(lr.beta1)
}

// BigBeta0 returns the model's bias (intercept) term.
func (lr *LogisticRegression) BigBeta0() *big.Int {
bigBeta := new(big.Float).Mul(lr.beta0, new(big.Float).SetInt(common.Big2e64))
bigBetaInt, _ := bigBeta.Int(nil)
return bigBetaInt
}

// BigBeta1 returns the model's weight (slope) term.
func (lr *LogisticRegression) BigBeta1() *big.Int {
bigBeta := new(big.Float).Mul(lr.beta1, new(big.Float).SetInt(common.Big2e64))
bigBetaInt, _ := bigBeta.Int(nil)
return bigBetaInt
}

// Plot the given trained logistic regression values with Beta0 and Beta1
func (lr *LogisticRegression) PlotSigmoid(xValues, yValues []float64, blockNumber uint64) error {
// Create a new plot
p := plot.New()

beta0, _ := lr.beta0.Float64()
beta1, _ := lr.beta1.Float64()

p.Title.Text = fmt.Sprintf("Sigmoid Function: Beta0=%.10f, Beta1=%.10f", beta0, beta1)
p.X.Label.Text = "x"
p.Y.Label.Text = "sigmoid(x)"

plotValues := make(plotter.XYs, 0)
for i := range xValues {
value := plotter.XY{xValues[i], yValues[i]}
plotValues = append(plotValues, value)
}

// Create a line plotter with x and y values
line, err := plotter.NewScatter(plotValues)
if err != nil {
return err
}

// Add the line to the plot
p.Add(line)

// Create the function to be plotted
sigmoidFunc := plotter.NewFunction(func(x float64) float64 {
result := lr.Predict(big.NewFloat(x))
resultF, _ := result.Float64()
return resultF
})

// Set the style for the function line
sigmoidFunc.Color = plotter.DefaultLineStyle.Color
sigmoidFunc.Width = vg.Points(2)

// Set the range for x-axis values
// Find the min and max in the xValues
xMin := float64(math.MaxInt64)
xMax := float64(0)
for _, x := range xValues {
if x < xMin {
xMin = x
} else if x > xMax {
xMax = x
}
}
sigmoidFunc.XMin = xMin
sigmoidFunc.XMax = xMax

p.Add(sigmoidFunc)

// Save the plot as a PNG image
if err := p.Save(6*vg.Inch, 4*vg.Inch, fmt.Sprintf("sigmoid-%d.png", blockNumber)); err != nil {
return err
}

return nil
}
70 changes: 70 additions & 0 deletions common/math/big.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,73 @@ func TwoToTheX(x *big.Float) *big.Float {

return result
}

// EToTheX computes the expression 1 + x + (1/2)*x^2 + (1/6)*x^3 + (1/24)*x^4 + (1/120)*x^5 + (1/720)*x^6 + (1/5040)*x^7
func EToTheX(x *big.Float) *big.Float {
// Set the desired precision
prec := uint(16) // You can adjust the precision as needed

// Initialize constants with the specified precision
one := new(big.Float).SetPrec(prec).SetInt64(1)
half := new(big.Float).SetPrec(prec).Quo(
new(big.Float).SetPrec(prec).SetInt64(1),
new(big.Float).SetPrec(prec).SetInt64(6),
)
oneSixth := new(big.Float).SetPrec(prec).Quo(
new(big.Float).SetPrec(prec).SetInt64(1),
new(big.Float).SetPrec(prec).SetInt64(6),
)
oneTwentyFourth := new(big.Float).SetPrec(prec).Quo(
new(big.Float).SetPrec(prec).SetInt64(1),
new(big.Float).SetPrec(prec).SetInt64(24),
)
oneOneTwentieth := new(big.Float).SetPrec(prec).Quo(
new(big.Float).SetPrec(prec).SetInt64(1),
new(big.Float).SetPrec(prec).SetInt64(120),
)
oneOneSevenTwentieth := new(big.Float).SetPrec(prec).Quo(
new(big.Float).SetPrec(prec).SetInt64(1),
new(big.Float).SetPrec(prec).SetInt64(720),
)
oneFiveThousandFourtieth := new(big.Float).SetPrec(prec).Quo(
new(big.Float).SetPrec(prec).SetInt64(1),
new(big.Float).SetPrec(prec).SetInt64(5040),
)

// Compute x^2
x2 := new(big.Float).SetPrec(prec).Mul(x, x)

// Compute x^3
x3 := new(big.Float).SetPrec(prec).Mul(x2, x)

// Compute x^4
x4 := new(big.Float).SetPrec(prec).Mul(x3, x)

// Compute x^5
x5 := new(big.Float).SetPrec(prec).Mul(x4, x)

// Compute x^6
x6 := new(big.Float).SetPrec(prec).Mul(x5, x)

// Compute x^7
x7 := new(big.Float).SetPrec(prec).Mul(x6, x)

// Compute terms
term2 := new(big.Float).SetPrec(prec).Mul(half, x2) // 0.5 * x^2
term3 := new(big.Float).SetPrec(prec).Mul(oneSixth, x3) // (1/6) * x^3
term4 := new(big.Float).SetPrec(prec).Mul(oneTwentyFourth, x4) // (1/24) * x^4
term5 := new(big.Float).SetPrec(prec).Mul(oneOneTwentieth, x5) // (1/120) * x^5
term6 := new(big.Float).SetPrec(prec).Mul(oneOneSevenTwentieth, x6) // (1/720) * x^6
term7 := new(big.Float).SetPrec(prec).Mul(oneFiveThousandFourtieth, x7) // (1/5040) * x^7

// Sum up the terms: result = 1 + x + term2 + term3 + term4
result := new(big.Float).SetPrec(prec).Add(one, x) // result = 1 + x
result.Add(result, term2) // result += term2
result.Add(result, term3) // result += term3
result.Add(result, term4) // result += term4
result.Add(result, term5) // result += term5
result.Add(result, term6) // result += term6
result.Add(result, term7) // result += term7

return result
}
50 changes: 50 additions & 0 deletions common/math/big_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,53 @@ func TestTwoToTheX(t *testing.T) {
}
}
}

func TestEtoTheX(t *testing.T) {
tests := []struct {
x *big.Float
actual *big.Float
}{
{
x: big.NewFloat(1.625),
actual: big.NewFloat(5.0784190371800815),
},
{
x: big.NewFloat(0.765),
actual: big.NewFloat(2.1489943746552203),
},
{
x: big.NewFloat(1.685),
actual: big.NewFloat(5.392450932349507),
},
{
x: big.NewFloat(2.0),
actual: big.NewFloat(7.38905609893065),
},
{
x: big.NewFloat(-1.25),
actual: big.NewFloat(0.2865047968601901),
},
{
x: big.NewFloat(5.5),
actual: big.NewFloat(244.69193226422038),
},
{
x: big.NewFloat(-0.00000045456),
actual: big.NewFloat(0.9999999999),
},
{
x: big.NewFloat(0.0),
actual: big.NewFloat(1.0), // e^0 = 1
},
}

for _, test := range tests {
result := EToTheX(test.x)
lowerBound := new(big.Float).Mul(test.actual, big.NewFloat(0.98))
upperBound := new(big.Float).Mul(test.actual, big.NewFloat(1.02))

if result.Cmp(lowerBound) < 0 || result.Cmp(upperBound) > 0 {
t.Errorf("TwoToTheX(%s) = %g, want %g within 3%%", test.x.Text('f', -1), result, test.actual)
}
}
}
Loading

0 comments on commit 252bfe6

Please sign in to comment.