Float exponentiation and logarithm instructions - MLH (#2968)

* Float exponentiation and logarithm instructions

* Corrections to instructions and tests

* Revamping Math Instructions

* Changed E Constant and modified compute max

* Formatting and Clippy Linting

* increased log computation allowance

Co-authored-by: Ronald Hood <ronald.hood@yale.edu>
This commit is contained in:
Ronald Hood Jr 2022-04-13 08:39:14 -04:00 committed by GitHub
parent 1d1c2b178b
commit 5611ad8bd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 160 additions and 7 deletions

View File

@ -70,6 +70,25 @@ pub enum MathInstruction {
/// The divisor
divisor: f32,
},
/// Exponentiate a float base by a power
///
/// No accounts required for this instruction
F32Exponentiate {
/// The base
base: f32,
/// The exponent
exponent: f32,
},
/// Natural Log of a float
///
/// No accounts required for this instruction
F32NaturalLog {
/// The argument
argument: f32,
},
/// Don't do anything for comparison
///
/// No accounts required for this instruction
@ -87,7 +106,7 @@ pub fn precise_sqrt(radicand: u64) -> Instruction {
}
}
/// Create SquareRoot instruction
/// Create U64 SquareRoot instruction
pub fn sqrt_u64(radicand: u64) -> Instruction {
Instruction {
program_id: id(),
@ -98,7 +117,7 @@ pub fn sqrt_u64(radicand: u64) -> Instruction {
}
}
/// Create SquareRoot instruction
/// Create U128 SquareRoot instruction
pub fn sqrt_u128(radicand: u128) -> Instruction {
Instruction {
program_id: id(),
@ -109,7 +128,7 @@ pub fn sqrt_u128(radicand: u128) -> Instruction {
}
}
/// Create PreciseSquareRoot instruction
/// Create U64 Multiplication instruction
pub fn u64_multiply(multiplicand: u64, multiplier: u64) -> Instruction {
Instruction {
program_id: id(),
@ -123,7 +142,7 @@ pub fn u64_multiply(multiplicand: u64, multiplier: u64) -> Instruction {
}
}
/// Create PreciseSquareRoot instruction
/// Create U64 Division instruction
pub fn u64_divide(dividend: u64, divisor: u64) -> Instruction {
Instruction {
program_id: id(),
@ -134,7 +153,7 @@ pub fn u64_divide(dividend: u64, divisor: u64) -> Instruction {
}
}
/// Create PreciseSquareRoot instruction
/// Create F32 Multiplication instruction
pub fn f32_multiply(multiplicand: f32, multiplier: f32) -> Instruction {
Instruction {
program_id: id(),
@ -148,7 +167,7 @@ pub fn f32_multiply(multiplicand: f32, multiplier: f32) -> Instruction {
}
}
/// Create PreciseSquareRoot instruction
/// Create F32 Division instruction
pub fn f32_divide(dividend: f32, divisor: f32) -> Instruction {
Instruction {
program_id: id(),
@ -159,7 +178,29 @@ pub fn f32_divide(dividend: f32, divisor: f32) -> Instruction {
}
}
/// Create PreciseSquareRoot instruction
/// Create F32 Exponentiate instruction
pub fn f32_exponentiate(base: f32, exponent: f32) -> Instruction {
Instruction {
program_id: id(),
accounts: vec![],
data: MathInstruction::F32Exponentiate { base, exponent }
.try_to_vec()
.unwrap(),
}
}
/// Create F32 Natural Log instruction
pub fn f32_natural_log(argument: f32) -> Instruction {
Instruction {
program_id: id(),
accounts: vec![],
data: MathInstruction::F32NaturalLog { argument }
.try_to_vec()
.unwrap(),
}
}
/// Create Noop instruction
pub fn noop() -> Instruction {
Instruction {
program_id: id(),
@ -277,6 +318,35 @@ mod tests {
assert_eq!(instruction.program_id, crate::id());
}
#[test]
fn test_f32_exponentiate() {
let instruction = f32_exponentiate(f32::MAX, f32::MAX);
assert_eq!(0, instruction.accounts.len());
assert_eq!(
instruction.data,
MathInstruction::F32Exponentiate {
base: f32::MAX,
exponent: f32::MAX
}
.try_to_vec()
.unwrap()
);
assert_eq!(instruction.program_id, crate::id())
}
#[test]
fn test_f32_natural_log() {
let instruction = f32_natural_log(f32::MAX);
assert_eq!(0, instruction.accounts.len());
assert_eq!(
instruction.data,
MathInstruction::F32NaturalLog { argument: f32::MAX }
.try_to_vec()
.unwrap()
);
assert_eq!(instruction.program_id, crate::id())
}
#[test]
fn test_noop() {
let instruction = noop();

View File

@ -33,6 +33,16 @@ fn f32_divide(dividend: f32, divisor: f32) -> f32 {
dividend / divisor
}
#[inline(never)]
fn f32_exponentiate(base: f32, exponent: f32) -> f32 {
base.powf(exponent)
}
#[inline(never)]
fn f32_natural_log(argument: f32) -> f32 {
argument.ln()
}
/// Instruction processor
pub fn process_instruction(
_program_id: &Pubkey,
@ -104,6 +114,22 @@ pub fn process_instruction(
msg!("{}", result as u64);
Ok(())
}
MathInstruction::F32Exponentiate { base, exponent } => {
msg!("Calculating f32 Exponent");
sol_log_compute_units();
let result = f32_exponentiate(base, exponent);
sol_log_compute_units();
msg!("{}", result as u64);
Ok(())
}
MathInstruction::F32NaturalLog { argument } => {
msg!("Calculating f32 Natural Log");
sol_log_compute_units();
let result = f32_natural_log(argument);
sol_log_compute_units();
msg!("{}", result as u64);
Ok(())
}
MathInstruction::Noop => {
msg!("Do nothing");
msg!("{}", 0_u64);
@ -142,6 +168,24 @@ mod tests {
assert_eq!(2.0, f32_divide(2.0, 1.0));
}
#[test]
fn test_f32_exponentiate() {
assert_eq!(16.0, f32_exponentiate(4.0, 2.0));
assert_eq!(4.0, f32_exponentiate(16.0, 0.5))
}
#[test]
fn test_f32_natural_log() {
let one = 1.0f32;
// e^1
let e = one.exp();
// ln(e) - 1 == 0
let abs_difference = (f32_natural_log(e) - 1.0).abs();
assert!(abs_difference <= f32::EPSILON);
}
#[test]
fn test_process_instruction() {
let program_id = Pubkey::new_unique();
@ -167,6 +211,13 @@ mod tests {
dividend: 2.0,
divisor: 2.0,
},
MathInstruction::F32Exponentiate {
base: 4.0,
exponent: 2.0,
},
MathInstruction::F32NaturalLog {
argument: std::f32::consts::E,
},
MathInstruction::Noop,
] {
let input = math_instruction.try_to_vec().unwrap();

View File

@ -147,6 +147,38 @@ async fn test_f32_divide() {
banks_client.process_transaction(transaction).await.unwrap();
}
#[tokio::test]
async fn test_f32_exponentiate() {
let mut pc = ProgramTest::new("spl_math", id(), processor!(process_instruction));
pc.set_compute_max_units(1400);
let (mut banks_client, payer, recent_blockhash) = pc.start().await;
let mut transaction = Transaction::new_with_payer(
&[instruction::f32_exponentiate(4_f32, 2_f32)],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);
banks_client.process_transaction(transaction).await.unwrap();
}
#[tokio::test]
async fn test_f32_natural_log() {
let mut pc = ProgramTest::new("spl_math", id(), processor!(process_instruction));
pc.set_compute_max_units(3500);
let (mut banks_client, payer, recent_blockhash) = pc.start().await;
let mut transaction = Transaction::new_with_payer(
&[instruction::f32_natural_log(1_f32.exp())],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);
banks_client.process_transaction(transaction).await.unwrap();
}
#[tokio::test]
async fn test_noop() {
let mut pc = ProgramTest::new("spl_math", id(), processor!(process_instruction));