Add optional parameter to sample and sampleSize

This commit is contained in:
Michael Zhu
2019-12-11 10:56:56 -08:00
parent 1bd906ecb3
commit 1d023e6db5
7 changed files with 66 additions and 42 deletions

View File

@@ -118,7 +118,10 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
const fromStatus =
fromPoolId === undefined || stakingPools[fromPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
? StakeStatus.Undelegated
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
: (Pseudorandom.sample(
[StakeStatus.Undelegated, StakeStatus.Delegated],
[0.2, 0.8],
) as StakeStatus);
const from = new StakeInfo(fromStatus, fromPoolId);
// Pick a random pool to move the stake to
@@ -128,7 +131,10 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
const toStatus =
toPoolId === undefined || stakingPools[toPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
? StakeStatus.Undelegated
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
: (Pseudorandom.sample(
[StakeStatus.Undelegated, StakeStatus.Delegated],
[0.2, 0.8],
) as StakeStatus);
const to = new StakeInfo(toStatus, toPoolId);
// The next epoch balance of the `from` stake is the amount that can be moved

View File

@@ -1,5 +1,6 @@
import { Numberish } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import * as seedrandom from 'seedrandom';
class PRNGWrapper {
@@ -10,11 +11,19 @@ class PRNGWrapper {
* Pseudorandom version of _.sample. Picks an element of the given array with uniform probability.
* Return undefined if the array is empty.
*/
public sample<T>(arr: T[]): T | undefined {
public sample<T>(arr: T[], weights?: number[]): T | undefined {
if (arr.length === 0) {
return undefined;
}
const index = Math.abs(this.rng.int32()) % arr.length;
let index: number;
if (weights !== undefined) {
const cdf = weights.map((_weight, i) => _.sum(weights.slice(0, i + 1)) / _.sum(weights));
const x = this.rng();
index = cdf.findIndex(value => value > x);
} else {
index = Math.abs(this.rng.int32()) % arr.length;
}
return arr[index];
}
@@ -22,13 +31,13 @@ class PRNGWrapper {
* Pseudorandom version of _.sampleSize. Returns an array of `n` samples from the given array
* (with replacement), chosen with uniform probability. Return undefined if the array is empty.
*/
public sampleSize<T>(arr: T[], n: number): T[] | undefined {
public sampleSize<T>(arr: T[], n: number, weights?: number[]): T[] | undefined {
if (arr.length === 0) {
return undefined;
}
const samples = [];
for (let i = 0; i < n; i++) {
samples.push(this.sample(arr) as T);
samples.push(this.sample(arr, weights) as T);
}
return samples;
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import { PoolOperator } from '../framework/actors/pool_operator';
@@ -14,12 +15,12 @@ export class PoolManagementSimulation extends Simulation {
const { actors } = this.environment;
const operators = filterActorsByRole(actors, PoolOperator);
const actions = [
...operators.map(operator => operator.simulationActions.validCreateStakingPool),
...operators.map(operator => operator.simulationActions.validDecreaseStakingPoolOperatorShare),
];
const [actions, weights] = _.unzip([
...operators.map(operator => [operator.simulationActions.validCreateStakingPool, 0.4]),
...operators.map(operator => [operator.simulationActions.validDecreaseStakingPoolOperatorShare, 0.6]),
]) as [AsyncIterableIterator<AssertionResult | void>[], number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import { MakerTaker } from '../framework/actors/hybrids';
@@ -22,14 +23,14 @@ export class PoolMembershipSimulation extends Simulation {
const poolManagement = new PoolManagementSimulation(this.environment);
const actions = [
...makers.map(maker => maker.simulationActions.validJoinStakingPool),
...takers.map(taker => taker.simulationActions.validFillOrder),
poolManagement.generator,
];
const [actions, weights] = _.unzip([
...makers.map(maker => [maker.simulationActions.validJoinStakingPool, 0.2 / makers.length]),
...takers.map(taker => [taker.simulationActions.validFillOrder, 0.6 / takers.length]),
[poolManagement.generator, 0.2],
]) as [AsyncIterableIterator<AssertionResult | void>[], number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import { StakerOperator } from '../framework/actors/hybrids';
@@ -20,14 +21,15 @@ export class StakeManagementSimulation extends Simulation {
const poolManagement = new PoolManagementSimulation(this.environment);
const actions = [
...stakers.map(staker => staker.simulationActions.validStake),
...stakers.map(staker => staker.simulationActions.validUnstake),
...stakers.map(staker => staker.simulationActions.validMoveStake),
poolManagement.generator,
];
const [actions, weights] = _.unzip([
...stakers.map(staker => [staker.simulationActions.validStake, 0.3]),
...stakers.map(staker => [staker.simulationActions.validUnstake, 0.2]),
...stakers.map(staker => [staker.simulationActions.validMoveStake, 0.3]),
[poolManagement.generator, 0.2],
]) as [AsyncIterableIterator<AssertionResult | void>[], number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -1,4 +1,5 @@
import { blockchainTests } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { Actor } from '../framework/actors/base';
import {
@@ -32,15 +33,15 @@ export class StakingRewardsSimulation extends Simulation {
const poolMembership = new PoolMembershipSimulation(this.environment);
const stakeManagement = new StakeManagementSimulation(this.environment);
const actions = [
...stakers.map(staker => staker.simulationActions.validWithdrawDelegatorRewards),
...keepers.map(keeper => keeper.simulationActions.validFinalizePool),
...keepers.map(keeper => keeper.simulationActions.validEndEpoch),
poolMembership.generator,
stakeManagement.generator,
];
const [actions, weights] = _.unzip([
...stakers.map(staker => [staker.simulationActions.validWithdrawDelegatorRewards, 0.1 / stakers.length]),
...keepers.map(keeper => [keeper.simulationActions.validFinalizePool, 0.1 / keepers.length]),
...keepers.map(keeper => [keeper.simulationActions.validEndEpoch, 0.1 / keepers.length]),
[poolMembership.generator, 0.5],
[stakeManagement.generator, 0.2],
]) as [AsyncIterableIterator<AssertionResult | void>[], number[]];
while (true) {
const action = Pseudorandom.sample(actions);
const action = Pseudorandom.sample(actions, weights);
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
}
}

View File

@@ -83,9 +83,10 @@ export function loadCurrentBalance(balance: StoredBalance, epoch: BigNumber): St
* Simulates _increaseNextBalance
*/
export function increaseNextBalance(balance: StoredBalance, amount: Numberish, epoch: BigNumber): StoredBalance {
const newBalance = loadCurrentBalance(balance, epoch);
return {
...loadCurrentBalance(balance, epoch),
nextEpochBalance: balance.nextEpochBalance.plus(amount),
...newBalance,
nextEpochBalance: newBalance.nextEpochBalance.plus(amount),
};
}
@@ -93,9 +94,10 @@ export function increaseNextBalance(balance: StoredBalance, amount: Numberish, e
* Simulates _decreaseNextBalance
*/
export function decreaseNextBalance(balance: StoredBalance, amount: Numberish, epoch: BigNumber): StoredBalance {
const newBalance = loadCurrentBalance(balance, epoch);
return {
...loadCurrentBalance(balance, epoch),
nextEpochBalance: balance.nextEpochBalance.minus(amount),
...newBalance,
nextEpochBalance: newBalance.nextEpochBalance.minus(amount),
};
}
@@ -107,10 +109,11 @@ export function increaseCurrentAndNextBalance(
amount: Numberish,
epoch: BigNumber,
): StoredBalance {
const newBalance = loadCurrentBalance(balance, epoch);
return {
...loadCurrentBalance(balance, epoch),
currentEpochBalance: balance.currentEpochBalance.plus(amount),
nextEpochBalance: balance.nextEpochBalance.plus(amount),
...newBalance,
currentEpochBalance: newBalance.currentEpochBalance.plus(amount),
nextEpochBalance: newBalance.nextEpochBalance.plus(amount),
};
}
@@ -122,10 +125,11 @@ export function decreaseCurrentAndNextBalance(
amount: Numberish,
epoch: BigNumber,
): StoredBalance {
const newBalance = loadCurrentBalance(balance, epoch);
return {
...loadCurrentBalance(balance, epoch),
currentEpochBalance: balance.currentEpochBalance.minus(amount),
nextEpochBalance: balance.nextEpochBalance.minus(amount),
...newBalance,
currentEpochBalance: newBalance.currentEpochBalance.minus(amount),
nextEpochBalance: newBalance.nextEpochBalance.minus(amount),
};
}