Module src.TokenLab.simulationcomponents.transactionclasses
Created on Fri Nov 18 12:19:09 2022
@author: stylianoskampakis
Expand source code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 18 12:19:09 2022
@author: stylianoskampakis
"""
from baseclasses import *
from typing import List, Dict,Union
from usergrowthclasses import UserGrowth
import scipy
from scipy.stats import binom,norm,poisson
import numpy as np
from utils.helpers import log_saturated_space
import copy
import time
class TransactionManagement(Controller):
"""
Abstract class.
Models transactions for individual agents, by modelling them as an aggregate.
It requires the total number of users conducting those transactions.
"""
def __init__(self):
super(TransactionManagement,self).__init__()
self.dependencies={AgentPool:None,TokenEconomy:None}
self.transactions_value=None
#keeps a record of transactions across all iterations
self._transactions_value_store=[]
pass
def execute(self,dependency:str="AgentPool"):
"""
Parameters
----------
dependency: The execute function of TransactionsManagement requires the user to define the
dependency. Usually this is either AgentPool or TokenEconomy.
This is where the number of users will be acquired from, when calculating the total transaction value.
Returns
-------
None.
"""
return None
def reset(self)->None:
"""
Sets the iteration counter to 0.
"""
self.iteration=0
pass
def get_transactions(self)->List:
return self.transactions_value
class TransactionManagement_Constant(TransactionManagement):
"""
TransactionManagement implementation where the total value per user stays the same.
"""
def __init__(self,average_transaction_value:float):
super(TransactionManagement_Constant,self).__init__()
#if average_transaction_value<0:
# raise Exception('Average transaction value cannot be negative')
self.average_transaction_value=average_transaction_value
def execute(self,dependency:str="AgentPool")->float:
"""
Parameters
----------
dependency:str: Available options are either AgentPool or TokenEconomy.
The execute function of TransactionsManagement requires the user to define the
dependency. Usually this is either AgentPool or TokenEconomy.
This is where the number of users will be acquired from, when calculating the total transaction value.
Final transaction value is simply num_users*constant (defined at class initialisation)
"""
if dependency=="AgentPool":
dependency=AgentPool
elif dependency=="TokenEconomy":
dependency=TokenEconomy
else:
raise Exception('You must use either AgentPool or TokenEconomy')
num_users=self.dependencies[dependency]['num_users']
self.total_users=num_users
self.transactions_value=num_users*self.average_transaction_value
self._transactions_value_store.append(self.transactions_value)
self.iteration+=1
return self.transactions_value
class TransactionManagement_Trend(TransactionManagement):
"""
Creates a trend of transactions (increasing or decreasing), while also accepting noise add-ons.
It has a dependency on a UserGrowth class. To be used inside an agent pool. At execution it can read
either from the agent class or the token economy.
So, it essentially functions like a constant tranction class, where the constant is either rising or
going down in a smooth way.
Properties of interest:
_transactions_means_store: The average transaction size for a given iteration
_transactions_value_store: The actual value of the transaction
"""
def __init__(self,average_transaction_initial:float, average_transaction_final:float,num_steps:int,
space_function:Union[np.linspace,np.logspace,np.geomspace,log_saturated_space]=np.linspace,name:str=None,
noise_addon:AddOn=None):
"""
Parameters
----------
average_transaction_initial : float
The initial transaction size.
average_transaction_final : float
The final transaction size.
num_steps : int
This should be equal to the number of iterations you want the simulation to run for.
space_function : Union[np.linspace,np.logspace,np.geomspace,log_saturated_space], optional
The function that will generate the steps. The default is np.linspace. Other options include np.geomspace
and log_saturated_space from the helpers module.
name : str, optional
The name of this controller. The default is None.
noise_addon : AddOn, optional
The default is None.
"""
self.dependencies={AgentPool:None}
self.num_steps=num_steps
self.space_function=space_function
self._noise_component=noise_addon
self.name=name
self._transactions_means_store_original=np.round(self.space_function(start=average_transaction_initial,stop=average_transaction_final,num=num_steps)).astype(int)
self._transactions_means_store=copy.deepcopy(self._transactions_means_store_original)
#applies the noise addon. If a value is below 0, then it is kept at 0.
if self._noise_component!=None:
dummy=[]
for i in range(len(self._transactions_means_store)):
temporary= self._transactions_means_store_original[i]+ self._noise_component.apply(**{'value':self._transactions_means_store_original[i]})
if temporary>=0:
dummy.append(temporary)
else:
dummy.append(self._transactions_means_store_original[i])
self._transactions_means_store=dummy
self.max_iterations=num_steps
self._transactions_value_store=[]
def execute(self,dependency:str="AgentPool")->float:
"""
Parameters
----------
dependency : str, optional
Where to read the number of users from. The default is "AgentPool".
Returns
-------
float
The total value of transactions.
"""
if dependency=="AgentPool":
dependency=AgentPool
elif dependency=="TokenEconomy":
dependency=TokenEconomy
else:
raise Exception('You must use either AgentPool or TokenEconomy')
num_users=self.dependencies[dependency]['num_users']
self.total_users=num_users
self.transactions_value=num_users*self._transactions_means_store[self.iteration]
self._transactions_value_store.append(self.transactions_value)
self.iteration+=1
return self.transactions_value
def reset(self)->bool:
"""
Sets the iterations counter to 0, and recalculates the effect of any
noise add-ons.
Returns
-------
True if reset was successful.
"""
self.iteration=0
self._transactions_means_store=copy.deepcopy(self._transactions_means_store_original)
#applies the noise addon. If a value is below 0, then it is kept at 0.
if self._noise_component!=None:
dummy=[]
for i in range(len(self._transactions_means_store)):
temporary= self._transactions_means_store_original[i]+ self._noise_component.apply(**{'value':self._transactions_means_store_original[i]})
if temporary>=0:
dummy.append(temporary)
else:
dummy.append(self._transactions_means_store_original[i])
self._transactions_means_store=dummy
return True
class TransactionManagement_Stochastic(TransactionManagement):
"""
Users a simple binomial distribution to model the number of active users.
Uses the normal distribution to simulate the transaction value.
So, at each iteration the execute() function does the following
1) Uses the binomial distribution to simulate the number of active users.
2) Uses the value_mean parameter and the value_std to calculate the average value of transactions.
3) Uses 1 and 2 to get an estimate of the total value.
Dependent class: The execute() argument is reading from a UserGrowth class that provides
the current number of users
"""
def __init__(self,
value_per_transaction:float=None,
transactions_per_user:Union[int,list[int]]=None,
value_distribution:scipy.stats=norm,transactions_distribution:scipy.stats=poisson,
value_dist_parameters:Union[Dict[str,float],List[Dict[str,float]]]={'loc':10,'scale':100},
transactions_dist_parameters:Union[Dict[str,float],List[Dict[str,float]]]={'mu':5},name:str=None,
activity_probs:Union[float,list[float]]=1,type_transaction:str='positive')->None:
"""
activity_probs: This is either a float or a list of floats, and determines the parameter p of the binomial
distribution
value_per_transaction: If this is set, then the value per transaction is fixed, and overrides the distribution. Otherwise,
leave to None.
transactions_per_user: If this is set, then the number of transactions is fixed, and overrides the distribution. Otherwise,
leave to None.
value_dist_parameters: Parameters for the value distribution. This can also be a list of dictionaries, where each dictionary
within the list is a set of parameters. The values inside the dictionary must use the keys 'loc' and 'scale'.
value_distribution: by default this is the normal distribution, but any scipy distribution
is supported.
transactions_distribution: uses a distribution to model the number of transactions per user. By default
this is a Poisson distribution
transactions_dist_parameters: Parameters for the transaction distribution. This can also be a list of dictionaries, where each dictionary
within the list is a set of parameters.
type_transaction: positive (positive transactions only), negative or mixed. It is positive by default
The value at each iteration of execution is sampled from the value distribution (if the distribution is set)
The final equation is using the following formulas when value distribution is active:
total value = distribution(loc,scale)
a) loc = num_active_users*num_transactions*value_per_transaction(or loc parameter)
b) scale = scale
If the value distribution is absent, and a value_per_transaction is defined instead, then
the formula becomes:
total value = val_mean*self.active_users*trans
NOTE: Average transactions per user can never be below 0. If 0 (due to random sampling) then it's set to 1
"""
super(TransactionManagement_Stochastic,self).__init__()
self.name=name
self.active_users=0
self.total_users=0
self._transactions_value_store=[]
self.dependencies={AgentPool:None}
self.activity_probabilities=activity_probs
self.transactions_distribution=transactions_distribution
self.transactions_per_user=transactions_per_user
self.transaction_dist_parameters=transactions_dist_parameters
self.value_distribution=value_distribution
self.value_per_transaction=value_per_transaction
self.value_dist_parameters=value_dist_parameters
if type_transaction not in ['positive','negative','mix']:
raise Exception ('You must define a type_transaction as positive, negative or mixed')
self.type_transaction=type_transaction
if value_per_transaction==None and value_dist_parameters==None:
raise Exception('You need to define at least value per transaction or value_dist_parameters')
if transactions_per_user==None and transactions_dist_parameters==None:
raise Exception('You need to define at least transaction_per_user or transaction_dist_parameters')
#sanity test, all lists should be the same length
lengths=[]
if isinstance(self.value_dist_parameters,(list,np.ndarray)):
self.max_iterations=len(self.value_dist_parameters)
lengths.append(len(self.value_dist_parameters))
if isinstance(self.transaction_dist_parameters,(list,np.ndarray)):
self.max_iterations=len(self.transaction_dist_parameters)
lengths.append(len(self.transaction_dist_parameters))
if isinstance(self.activity_probabilities,(list,np.ndarray)):
self.max_iterations=len(self.activity_probabilities)
lengths.append(len(self.activity_probabilities))
if len(set(lengths))>1:
raise Exception('When supplying lists as arguments, they should all have the same length.')
return None
def execute(self,dependency:str="AgentPool")->float:
if dependency=="AgentPool":
dependency=AgentPool
elif dependency=="TokenEconomy":
dependency=TokenEconomy
else:
raise Exception('You must use either AgentPool or TokenEconomy')
num_users=self.dependencies[dependency]['num_users']
self.total_users=num_users
#because activitiy_probabilities can be either an int or a list, we have to take into account both scenarios
if isinstance(self.activity_probabilities,(list,np.ndarray)):
act=self.activity_probabilities[self.iteration]
else:
act=self.activity_probabilities
seed=int(int(time.time())*np.random.rand())
self.active_users=binom.rvs(int(num_users),act,random_state=seed)
if isinstance(self.transaction_dist_parameters,(list,np.ndarray)):
trans_param=self.transaction_dist_parameters[self.iteration]
else:
trans_param=self.transaction_dist_parameters
if self.transactions_per_user is None:
seed=int(int(time.time())*np.random.rand())
trans=self.transactions_distribution.rvs(size=1,**trans_param,random_state=seed)[0]
if trans<=0:
trans=1
else:
trans=self.transactions_per_user
self.num_transactions=trans
if isinstance(self.value_dist_parameters,(list,np.ndarray)):
val_param=self.value_dist_parameters[self.iteration]
else:
val_param=self.value_dist_parameters
if self.value_per_transaction is None:
seed=int(int(time.time())*np.random.rand())
value_mean=self.value_distribution.rvs(size=1,loc=val_param['loc']*self.active_users*trans,scale=val_param['scale'],random_state=seed)[0]
else:
value_mean=self.value_per_transaction*self.active_users*trans
#transaction value can never be negative
if value_mean<0 and self.type_transaction=='positive':
value_mean=0
elif value_mean>0 and self.type_transaction=='negative':
value_mean=0
self.transactions_value=value_mean
self._transactions_value_store.append(value_mean)
self.iteration+=1
return self.transactions_value
Classes
class TransactionManagement
-
Abstract class.
Models transactions for individual agents, by modelling them as an aggregate.
It requires the total number of users conducting those transactions.
Expand source code
class TransactionManagement(Controller): """ Abstract class. Models transactions for individual agents, by modelling them as an aggregate. It requires the total number of users conducting those transactions. """ def __init__(self): super(TransactionManagement,self).__init__() self.dependencies={AgentPool:None,TokenEconomy:None} self.transactions_value=None #keeps a record of transactions across all iterations self._transactions_value_store=[] pass def execute(self,dependency:str="AgentPool"): """ Parameters ---------- dependency: The execute function of TransactionsManagement requires the user to define the dependency. Usually this is either AgentPool or TokenEconomy. This is where the number of users will be acquired from, when calculating the total transaction value. Returns ------- None. """ return None def reset(self)->None: """ Sets the iteration counter to 0. """ self.iteration=0 pass def get_transactions(self)->List: return self.transactions_value
Ancestors
- baseclasses.Controller
Subclasses
Methods
def execute(self, dependency: str = 'AgentPool')
-
Parameters
dependency
:The execute function
ofTransactionsManagement requires the user to define the
dependency. Usually this is either AgentPool or TokenEconomy.
This is where the number of users will be acquired from, when calculating the total transaction value.
Returns
None.
Expand source code
def execute(self,dependency:str="AgentPool"): """ Parameters ---------- dependency: The execute function of TransactionsManagement requires the user to define the dependency. Usually this is either AgentPool or TokenEconomy. This is where the number of users will be acquired from, when calculating the total transaction value. Returns ------- None. """ return None
def get_transactions(self) ‑> List
-
Expand source code
def get_transactions(self)->List: return self.transactions_value
def reset(self) ‑> None
-
Sets the iteration counter to 0.
Expand source code
def reset(self)->None: """ Sets the iteration counter to 0. """ self.iteration=0 pass
class TransactionManagement_Constant (average_transaction_value: float)
-
TransactionManagement implementation where the total value per user stays the same.
Expand source code
class TransactionManagement_Constant(TransactionManagement): """ TransactionManagement implementation where the total value per user stays the same. """ def __init__(self,average_transaction_value:float): super(TransactionManagement_Constant,self).__init__() #if average_transaction_value<0: # raise Exception('Average transaction value cannot be negative') self.average_transaction_value=average_transaction_value def execute(self,dependency:str="AgentPool")->float: """ Parameters ---------- dependency:str: Available options are either AgentPool or TokenEconomy. The execute function of TransactionsManagement requires the user to define the dependency. Usually this is either AgentPool or TokenEconomy. This is where the number of users will be acquired from, when calculating the total transaction value. Final transaction value is simply num_users*constant (defined at class initialisation) """ if dependency=="AgentPool": dependency=AgentPool elif dependency=="TokenEconomy": dependency=TokenEconomy else: raise Exception('You must use either AgentPool or TokenEconomy') num_users=self.dependencies[dependency]['num_users'] self.total_users=num_users self.transactions_value=num_users*self.average_transaction_value self._transactions_value_store.append(self.transactions_value) self.iteration+=1 return self.transactions_value
Ancestors
- TransactionManagement
- baseclasses.Controller
Methods
def execute(self, dependency: str = 'AgentPool') ‑> float
-
Parameters
dependency:str: Available options are either AgentPool or TokenEconomy.
The execute function of TransactionsManagement requires the user to define the dependency. Usually this is either AgentPool or TokenEconomy.
This is where the number of users will be acquired from, when calculating the total transaction value.
Final transaction value is simply num_users*constant (defined at class initialisation)
Expand source code
def execute(self,dependency:str="AgentPool")->float: """ Parameters ---------- dependency:str: Available options are either AgentPool or TokenEconomy. The execute function of TransactionsManagement requires the user to define the dependency. Usually this is either AgentPool or TokenEconomy. This is where the number of users will be acquired from, when calculating the total transaction value. Final transaction value is simply num_users*constant (defined at class initialisation) """ if dependency=="AgentPool": dependency=AgentPool elif dependency=="TokenEconomy": dependency=TokenEconomy else: raise Exception('You must use either AgentPool or TokenEconomy') num_users=self.dependencies[dependency]['num_users'] self.total_users=num_users self.transactions_value=num_users*self.average_transaction_value self._transactions_value_store.append(self.transactions_value) self.iteration+=1 return self.transactions_value
Inherited members
class TransactionManagement_Stochastic (value_per_transaction: float = None, transactions_per_user: Union[int, list[int]] = None, value_distribution:
= <scipy.stats._continuous_distns.norm_gen object>, transactions_distribution: = <scipy.stats._discrete_distns.poisson_gen object>, value_dist_parameters: Union[Dict[str, float], List[Dict[str, float]]] = {'loc': 10, 'scale': 100}, transactions_dist_parameters: Union[Dict[str, float], List[Dict[str, float]]] = {'mu': 5}, name: str = None, activity_probs: Union[float, list[float]] = 1, type_transaction: str = 'positive') -
Users a simple binomial distribution to model the number of active users.
Uses the normal distribution to simulate the transaction value.
So, at each iteration the execute() function does the following
1) Uses the binomial distribution to simulate the number of active users. 2) Uses the value_mean parameter and the value_std to calculate the average value of transactions. 3) Uses 1 and 2 to get an estimate of the total value.
Dependent class: The execute() argument is reading from a UserGrowth class that provides the current number of users
activity_probs: This is either a float or a list of floats, and determines the parameter p of the binomial distribution
value_per_transaction: If this is set, then the value per transaction is fixed, and overrides the distribution. Otherwise, leave to None.
transactions_per_user: If this is set, then the number of transactions is fixed, and overrides the distribution. Otherwise, leave to None.
value_dist_parameters: Parameters for the value distribution. This can also be a list of dictionaries, where each dictionary within the list is a set of parameters. The values inside the dictionary must use the keys 'loc' and 'scale'.
value_distribution: by default this is the normal distribution, but any scipy distribution is supported.
transactions_distribution: uses a distribution to model the number of transactions per user. By default this is a Poisson distribution
transactions_dist_parameters: Parameters for the transaction distribution. This can also be a list of dictionaries, where each dictionary within the list is a set of parameters.
type_transaction: positive (positive transactions only), negative or mixed. It is positive by default
The value at each iteration of execution is sampled from the value distribution (if the distribution is set)
The final equation is using the following formulas when value distribution is active:
total value = distribution(loc,scale) a) loc = num_active_usersnum_transactionsvalue_per_transaction(or loc parameter) b) scale = scale
If the value distribution is absent, and a value_per_transaction is defined instead, then the formula becomes:
total value = val_meanself.active_userstrans
NOTE: Average transactions per user can never be below 0. If 0 (due to random sampling) then it's set to 1
Expand source code
class TransactionManagement_Stochastic(TransactionManagement): """ Users a simple binomial distribution to model the number of active users. Uses the normal distribution to simulate the transaction value. So, at each iteration the execute() function does the following 1) Uses the binomial distribution to simulate the number of active users. 2) Uses the value_mean parameter and the value_std to calculate the average value of transactions. 3) Uses 1 and 2 to get an estimate of the total value. Dependent class: The execute() argument is reading from a UserGrowth class that provides the current number of users """ def __init__(self, value_per_transaction:float=None, transactions_per_user:Union[int,list[int]]=None, value_distribution:scipy.stats=norm,transactions_distribution:scipy.stats=poisson, value_dist_parameters:Union[Dict[str,float],List[Dict[str,float]]]={'loc':10,'scale':100}, transactions_dist_parameters:Union[Dict[str,float],List[Dict[str,float]]]={'mu':5},name:str=None, activity_probs:Union[float,list[float]]=1,type_transaction:str='positive')->None: """ activity_probs: This is either a float or a list of floats, and determines the parameter p of the binomial distribution value_per_transaction: If this is set, then the value per transaction is fixed, and overrides the distribution. Otherwise, leave to None. transactions_per_user: If this is set, then the number of transactions is fixed, and overrides the distribution. Otherwise, leave to None. value_dist_parameters: Parameters for the value distribution. This can also be a list of dictionaries, where each dictionary within the list is a set of parameters. The values inside the dictionary must use the keys 'loc' and 'scale'. value_distribution: by default this is the normal distribution, but any scipy distribution is supported. transactions_distribution: uses a distribution to model the number of transactions per user. By default this is a Poisson distribution transactions_dist_parameters: Parameters for the transaction distribution. This can also be a list of dictionaries, where each dictionary within the list is a set of parameters. type_transaction: positive (positive transactions only), negative or mixed. It is positive by default The value at each iteration of execution is sampled from the value distribution (if the distribution is set) The final equation is using the following formulas when value distribution is active: total value = distribution(loc,scale) a) loc = num_active_users*num_transactions*value_per_transaction(or loc parameter) b) scale = scale If the value distribution is absent, and a value_per_transaction is defined instead, then the formula becomes: total value = val_mean*self.active_users*trans NOTE: Average transactions per user can never be below 0. If 0 (due to random sampling) then it's set to 1 """ super(TransactionManagement_Stochastic,self).__init__() self.name=name self.active_users=0 self.total_users=0 self._transactions_value_store=[] self.dependencies={AgentPool:None} self.activity_probabilities=activity_probs self.transactions_distribution=transactions_distribution self.transactions_per_user=transactions_per_user self.transaction_dist_parameters=transactions_dist_parameters self.value_distribution=value_distribution self.value_per_transaction=value_per_transaction self.value_dist_parameters=value_dist_parameters if type_transaction not in ['positive','negative','mix']: raise Exception ('You must define a type_transaction as positive, negative or mixed') self.type_transaction=type_transaction if value_per_transaction==None and value_dist_parameters==None: raise Exception('You need to define at least value per transaction or value_dist_parameters') if transactions_per_user==None and transactions_dist_parameters==None: raise Exception('You need to define at least transaction_per_user or transaction_dist_parameters') #sanity test, all lists should be the same length lengths=[] if isinstance(self.value_dist_parameters,(list,np.ndarray)): self.max_iterations=len(self.value_dist_parameters) lengths.append(len(self.value_dist_parameters)) if isinstance(self.transaction_dist_parameters,(list,np.ndarray)): self.max_iterations=len(self.transaction_dist_parameters) lengths.append(len(self.transaction_dist_parameters)) if isinstance(self.activity_probabilities,(list,np.ndarray)): self.max_iterations=len(self.activity_probabilities) lengths.append(len(self.activity_probabilities)) if len(set(lengths))>1: raise Exception('When supplying lists as arguments, they should all have the same length.') return None def execute(self,dependency:str="AgentPool")->float: if dependency=="AgentPool": dependency=AgentPool elif dependency=="TokenEconomy": dependency=TokenEconomy else: raise Exception('You must use either AgentPool or TokenEconomy') num_users=self.dependencies[dependency]['num_users'] self.total_users=num_users #because activitiy_probabilities can be either an int or a list, we have to take into account both scenarios if isinstance(self.activity_probabilities,(list,np.ndarray)): act=self.activity_probabilities[self.iteration] else: act=self.activity_probabilities seed=int(int(time.time())*np.random.rand()) self.active_users=binom.rvs(int(num_users),act,random_state=seed) if isinstance(self.transaction_dist_parameters,(list,np.ndarray)): trans_param=self.transaction_dist_parameters[self.iteration] else: trans_param=self.transaction_dist_parameters if self.transactions_per_user is None: seed=int(int(time.time())*np.random.rand()) trans=self.transactions_distribution.rvs(size=1,**trans_param,random_state=seed)[0] if trans<=0: trans=1 else: trans=self.transactions_per_user self.num_transactions=trans if isinstance(self.value_dist_parameters,(list,np.ndarray)): val_param=self.value_dist_parameters[self.iteration] else: val_param=self.value_dist_parameters if self.value_per_transaction is None: seed=int(int(time.time())*np.random.rand()) value_mean=self.value_distribution.rvs(size=1,loc=val_param['loc']*self.active_users*trans,scale=val_param['scale'],random_state=seed)[0] else: value_mean=self.value_per_transaction*self.active_users*trans #transaction value can never be negative if value_mean<0 and self.type_transaction=='positive': value_mean=0 elif value_mean>0 and self.type_transaction=='negative': value_mean=0 self.transactions_value=value_mean self._transactions_value_store.append(value_mean) self.iteration+=1 return self.transactions_value
Ancestors
- TransactionManagement
- baseclasses.Controller
Inherited members
class TransactionManagement_Trend (average_transaction_initial: float, average_transaction_final: float, num_steps: int, space_function: Union[linspace, logspace, geomspace, log_saturated_space] = <function linspace>, name: str = None, noise_addon: baseclasses.AddOn = None)
-
Creates a trend of transactions (increasing or decreasing), while also accepting noise add-ons.
It has a dependency on a UserGrowth class. To be used inside an agent pool. At execution it can read either from the agent class or the token economy.
So, it essentially functions like a constant tranction class, where the constant is either rising or going down in a smooth way.
Properties of interest:
_transactions_means_store: The average transaction size for a given iteration _transactions_value_store: The actual value of the transaction
Parameters
average_transaction_initial
:float
- The initial transaction size.
average_transaction_final
:float
- The final transaction size.
num_steps
:int
- This should be equal to the number of iterations you want the simulation to run for.
space_function
:Union[np.linspace,np.logspace,np.geomspace,log_saturated_space]
, optional- The function that will generate the steps. The default is np.linspace. Other options include np.geomspace and log_saturated_space from the helpers module.
name
:str
, optional- The name of this controller. The default is None.
noise_addon
:AddOn
, optional- The default is None.
Expand source code
class TransactionManagement_Trend(TransactionManagement): """ Creates a trend of transactions (increasing or decreasing), while also accepting noise add-ons. It has a dependency on a UserGrowth class. To be used inside an agent pool. At execution it can read either from the agent class or the token economy. So, it essentially functions like a constant tranction class, where the constant is either rising or going down in a smooth way. Properties of interest: _transactions_means_store: The average transaction size for a given iteration _transactions_value_store: The actual value of the transaction """ def __init__(self,average_transaction_initial:float, average_transaction_final:float,num_steps:int, space_function:Union[np.linspace,np.logspace,np.geomspace,log_saturated_space]=np.linspace,name:str=None, noise_addon:AddOn=None): """ Parameters ---------- average_transaction_initial : float The initial transaction size. average_transaction_final : float The final transaction size. num_steps : int This should be equal to the number of iterations you want the simulation to run for. space_function : Union[np.linspace,np.logspace,np.geomspace,log_saturated_space], optional The function that will generate the steps. The default is np.linspace. Other options include np.geomspace and log_saturated_space from the helpers module. name : str, optional The name of this controller. The default is None. noise_addon : AddOn, optional The default is None. """ self.dependencies={AgentPool:None} self.num_steps=num_steps self.space_function=space_function self._noise_component=noise_addon self.name=name self._transactions_means_store_original=np.round(self.space_function(start=average_transaction_initial,stop=average_transaction_final,num=num_steps)).astype(int) self._transactions_means_store=copy.deepcopy(self._transactions_means_store_original) #applies the noise addon. If a value is below 0, then it is kept at 0. if self._noise_component!=None: dummy=[] for i in range(len(self._transactions_means_store)): temporary= self._transactions_means_store_original[i]+ self._noise_component.apply(**{'value':self._transactions_means_store_original[i]}) if temporary>=0: dummy.append(temporary) else: dummy.append(self._transactions_means_store_original[i]) self._transactions_means_store=dummy self.max_iterations=num_steps self._transactions_value_store=[] def execute(self,dependency:str="AgentPool")->float: """ Parameters ---------- dependency : str, optional Where to read the number of users from. The default is "AgentPool". Returns ------- float The total value of transactions. """ if dependency=="AgentPool": dependency=AgentPool elif dependency=="TokenEconomy": dependency=TokenEconomy else: raise Exception('You must use either AgentPool or TokenEconomy') num_users=self.dependencies[dependency]['num_users'] self.total_users=num_users self.transactions_value=num_users*self._transactions_means_store[self.iteration] self._transactions_value_store.append(self.transactions_value) self.iteration+=1 return self.transactions_value def reset(self)->bool: """ Sets the iterations counter to 0, and recalculates the effect of any noise add-ons. Returns ------- True if reset was successful. """ self.iteration=0 self._transactions_means_store=copy.deepcopy(self._transactions_means_store_original) #applies the noise addon. If a value is below 0, then it is kept at 0. if self._noise_component!=None: dummy=[] for i in range(len(self._transactions_means_store)): temporary= self._transactions_means_store_original[i]+ self._noise_component.apply(**{'value':self._transactions_means_store_original[i]}) if temporary>=0: dummy.append(temporary) else: dummy.append(self._transactions_means_store_original[i]) self._transactions_means_store=dummy return True
Ancestors
- TransactionManagement
- baseclasses.Controller
Methods
def execute(self, dependency: str = 'AgentPool') ‑> float
-
Parameters
dependency
:str
, optional- Where to read the number of users from. The default is "AgentPool".
Returns
float
- The total value of transactions.
Expand source code
def execute(self,dependency:str="AgentPool")->float: """ Parameters ---------- dependency : str, optional Where to read the number of users from. The default is "AgentPool". Returns ------- float The total value of transactions. """ if dependency=="AgentPool": dependency=AgentPool elif dependency=="TokenEconomy": dependency=TokenEconomy else: raise Exception('You must use either AgentPool or TokenEconomy') num_users=self.dependencies[dependency]['num_users'] self.total_users=num_users self.transactions_value=num_users*self._transactions_means_store[self.iteration] self._transactions_value_store.append(self.transactions_value) self.iteration+=1 return self.transactions_value
def reset(self) ‑> bool
-
Sets the iterations counter to 0, and recalculates the effect of any noise add-ons.
Returns
True if reset was successful.
Expand source code
def reset(self)->bool: """ Sets the iterations counter to 0, and recalculates the effect of any noise add-ons. Returns ------- True if reset was successful. """ self.iteration=0 self._transactions_means_store=copy.deepcopy(self._transactions_means_store_original) #applies the noise addon. If a value is below 0, then it is kept at 0. if self._noise_component!=None: dummy=[] for i in range(len(self._transactions_means_store)): temporary= self._transactions_means_store_original[i]+ self._noise_component.apply(**{'value':self._transactions_means_store_original[i]}) if temporary>=0: dummy.append(temporary) else: dummy.append(self._transactions_means_store_original[i]) self._transactions_means_store=dummy return True