Skip to content

Commit

Permalink
feat(src): generic exponent plugin
Browse files Browse the repository at this point in the history
Signed-off-by: Paz Barda <[email protected]>
  • Loading branch information
pazbardanl committed May 16, 2024
1 parent 4a2998a commit b9e5d4f
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [SCI-O](./src/lib/sci-o/README.md)
- [Shell](./src/lib/shell/README.md)
- [Sum](./src/lib/sum/README.md)
- [Exponent](./src/lib/exponent/README.md)
- [TDP Finder](./src/lib/tdp-finder/README.md)

## Contributing
Expand Down
118 changes: 118 additions & 0 deletions src/__tests__/unit/lib/exponent/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {Exponent} from '../../../../lib';

import {ERRORS} from '../../../../util/errors';

const {InputValidationError} = ERRORS;

describe('lib/exponent: ', () => {
describe('Exponent: ', () => {
const globalConfig = {
'input-parameter': 'energy/base',
exponent: 3,
'output-parameter': 'energy',
};
const exponent = Exponent(globalConfig);

describe('init: ', () => {
it('successfully initalized.', () => {
expect(exponent).toHaveProperty('metadata');
expect(exponent).toHaveProperty('execute');
});
});

describe('execute(): ', () => {
it('successfully applies Exponent strategy to given input.', async () => {
expect.assertions(1);

const expectedResult = [
{
duration: 3600,
'energy/base': 2,
energy: 8,
timestamp: '2021-01-01T00:00:00Z',
},
];

const result = await exponent.execute([
{
duration: 3600,
'energy/base': 2,
timestamp: '2021-01-01T00:00:00Z',
},
]);

expect(result).toStrictEqual(expectedResult);
});

it('throws an error on missing params in input.', async () => {
const expectedMessage =
'Exponent: energy/base is missing from the input array.';

expect.assertions(1);

try {
await exponent.execute([
{
duration: 3600,
timestamp: '2021-01-01T00:00:00Z',
},
]);
} catch (error) {
expect(error).toStrictEqual(
new InputValidationError(expectedMessage)
);
}
});

it('throws an error on input param value not numeric.', async () => {
const expectedMessage = 'Exponent: i-am-not-a-number is not numeric.';

expect.assertions(1);

try {
await exponent.execute([
{
duration: 3600,
'energy/base': 'i-am-not-a-number',
timestamp: '2021-01-01T00:00:00Z',
},
]);
} catch (error) {
expect(error).toStrictEqual(
new InputValidationError(expectedMessage)
);
}
});

it('returns a result with input params not related to energy.', async () => {
expect.assertions(1);
const newConfig = {
'input-parameter': 'carbon/base',
exponent: 4,
'output-parameter': 'carbon',
};
const exponent = Exponent(newConfig);

const data = [
{
duration: 3600,
timestamp: '2021-01-01T00:00:00Z',
'carbon/base': 2,
},
];
const response = await exponent.execute(data);

const expectedResult = [
{
duration: 3600,
'carbon/base': 2,
carbon: 16,
timestamp: '2021-01-01T00:00:00Z',
},
];

expect(response).toEqual(expectedResult);
});
});
});
});
98 changes: 98 additions & 0 deletions src/lib/exponent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Exponent

`exponent` is a generic plugin for calculating exponent of an input param (as base) and another (as the exponent) in an `input` array.

You provide the names of the values you want to use for the exponent calculation, and a name to use to add the exponent result to the output array.

For example, you use `cpu/energy` as base and `network/energy` as and name the result `energy`. `energy` would then be added to every observation in your input array as `cpu/energy` raised by the exponent `network/energy`.

## Parameters

### Plugin config

Three parameters are required in global config: `input-parameter`, `exponent` and `output-parameter`.

`input-parameter`: a string defining the base. Must match an existing key in the `inputs` array
`exponent`: a number defining the exponent.
`output-parameter`: a string defining the name to use to add the result of the exponent to the output array.

### Inputs

`input-parameter` and `exponent` must be available in the input array.

## Returns

- `output-parameter`: `input-parameter` raised by `exponent` with the parameter name defined by `output-parameter` in global config.

## Calculation

```pseudocode
output = input ^ exponent
```

## Implementation

To run the plugin, you must first create an instance of `Exponent`. Then, you can call `execute()`.

```typescript
import {Exponent} from '@grnsft/if-plugins';

const config = {
inputParameter: ['cpu/energy'],
exponent: 2
outputParameter: 'energy',
};

const exponent = Exponent(config);
const result = await exponent.execute([
{
duration: 3600,
timestamp: '2021-01-01T00:00:00Z',
'cpu/energy': 0.1,
'energy': 0.01,
},
]);
```

## Example manifest

IF users will typically call the plugin as part of a pipeline defined in a manifest file. In this case, instantiating the plugin is handled by and does not have to be done explicitly by the user. The following is an example manifest that calls `exponent`:

```yaml
name: exponent demo
description:
tags:
initialize:
outputs:
- yaml
plugins:
exponent:
method: Exponent
path: '@grnsft/if-plugins'
global-config:
input-parameter: 'cpu/energy'
exponent: 2
output-parameter: 'energy'
tree:
children:
child:
pipeline:
- exponent
config:
exponent:
inputs:
- timestamp: 2023-08-06T00:00
duration: 3600
cpu/energy: 0.001
network/energy: 0.001
```
You can run this example by saving it as `./manifests/examples/test/exponent.yml` and executing the following command from the project root:

```sh
npm i -g @grnsft/if
npm i -g @grnsft/if-plugins
ie --manifest ./manifests/examples/test/exponent.yml --output ./examples/outputs/exponent.yml
```

The results will be saved to a new `yaml` file in `./examples/outputs`.
98 changes: 98 additions & 0 deletions src/lib/exponent/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {z} from 'zod';

import {ERRORS} from '../../util/errors';
import {buildErrorMessage} from '../../util/helpers';
import {validate} from '../../util/validations';

import {PluginInterface} from '../../interfaces';
import {PluginParams} from '../../types/common';
import {ExponentConfig} from './types';

const {InputValidationError} = ERRORS;

export const Exponent = (globalConfig: ExponentConfig): PluginInterface => {
const errorBuilder = buildErrorMessage(Exponent.name);
const metadata = {
kind: 'execute',
};

/**
* Checks global config value are valid.
*/
const validateGlobalConfig = () => {
const globalConfigSchema = z.object({
'input-parameter': z.string().min(1),
exponent: z.number().min(1),
'output-parameter': z.string().min(1),
});

return validate<z.infer<typeof globalConfigSchema>>(
globalConfigSchema,
globalConfig
);
};

/**
* Checks for required fields in input.
*/
const validateSingleInput = (input: PluginParams, inputParameter: string) => {
validateParamExists(input, inputParameter);
validateNumericString(input[inputParameter]);
};

const validateParamExists = (input: PluginParams, param: string) => {
if (input[param] === undefined) {
throw new InputValidationError(
errorBuilder({
message: `${param} is missing from the input array`,
})
);
}
};

const validateNumericString = (str: string) => {
if (typeof str !== 'number') {
throw new InputValidationError(
errorBuilder({
message: `${str} is not numeric`,
})
);
}
};

/**
* Calculate the input param raised by to the power of the given exponent.
*/
const execute = async (inputs: PluginParams[]): Promise<PluginParams[]> => {
const {
'input-parameter': inputParameter,
exponent: exponent,
'output-parameter': outputParameter,
} = validateGlobalConfig();
return inputs.map(input => {
validateSingleInput(input, inputParameter);

return {
...input,
[outputParameter]: calculateExponent(input, inputParameter, exponent),
};
});
};

/**
* Calculates the input param raised by the power of a given exponent.
*/
const calculateExponent = (
input: PluginParams,
inputParameter: string,
exponent: number
) => {
const base = input[inputParameter];
return Math.pow(base, exponent);
};

return {
metadata,
execute,
};
};
5 changes: 5 additions & 0 deletions src/lib/exponent/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type ExponentConfig = {
'input-parameter': string;
exponent: number;
'output-parameter': string;
};
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export {Sum} from './sum';
export {Coefficient} from './coefficient';
export {Divide} from './divide';
export {Regex} from './regex';
export {Exponent} from './exponent';

0 comments on commit b9e5d4f

Please sign in to comment.