Added load plots.
I merged Strategy and ExplicitStrategy for now. I also pruned zero probability quorums from the strategies. The load plots are cool. I want to plot capacity plots now, though it's a little tricky.
This commit is contained in:
parent
2a1b56e987
commit
9aac3a7541
2 changed files with 102 additions and 39 deletions
|
@ -4,7 +4,7 @@
|
||||||
from . import distribution
|
from . import distribution
|
||||||
from .distribution import Distribution
|
from .distribution import Distribution
|
||||||
from .expr import Expr, Node
|
from .expr import Expr, Node
|
||||||
from .strategy import ExplicitStrategy, Strategy
|
from .strategy import Strategy
|
||||||
from typing import Dict, Iterator, Generic, List, Optional, Set, TypeVar
|
from typing import Dict, Iterator, Generic, List, Optional, Set, TypeVar
|
||||||
import collections
|
import collections
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -251,8 +251,17 @@ class QuorumSystem(Generic[T]):
|
||||||
|
|
||||||
# Solve the linear program.
|
# Solve the linear program.
|
||||||
problem.solve(pulp.apis.PULP_CBC_CMD(msg=False))
|
problem.solve(pulp.apis.PULP_CBC_CMD(msg=False))
|
||||||
return ExplicitStrategy(nodes,
|
|
||||||
read_quorums,
|
non_zero_read_quorums = [
|
||||||
[v.varValue for v in read_quorum_vars],
|
(rq, v.varValue)
|
||||||
write_quorums,
|
for (rq, v) in zip(read_quorums, read_quorum_vars)
|
||||||
[v.varValue for v in write_quorum_vars])
|
if v.varValue != 0]
|
||||||
|
non_zero_write_quorums = [
|
||||||
|
(wq, v.varValue)
|
||||||
|
for (wq, v) in zip(write_quorums, write_quorum_vars)
|
||||||
|
if v.varValue != 0]
|
||||||
|
return Strategy(nodes,
|
||||||
|
[rq for (rq, _) in non_zero_read_quorums],
|
||||||
|
[weight for (_, weight) in non_zero_read_quorums],
|
||||||
|
[wq for (wq, _) in non_zero_write_quorums],
|
||||||
|
[weight for (_, weight) in non_zero_write_quorums])
|
||||||
|
|
|
@ -3,6 +3,8 @@ from .distribution import Distribution
|
||||||
from .expr import Node
|
from .expr import Node
|
||||||
from typing import Dict, Generic, List, Optional, Set, TypeVar
|
from typing import Dict, Generic, List, Optional, Set, TypeVar
|
||||||
import collections
|
import collections
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,26 +12,6 @@ T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
class Strategy(Generic[T]):
|
class Strategy(Generic[T]):
|
||||||
def load(self,
|
|
||||||
read_fraction: Optional[Distribution] = None,
|
|
||||||
write_fraction: Optional[Distribution] = None) \
|
|
||||||
-> float:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def capacity(self,
|
|
||||||
read_fraction: Optional[Distribution] = None,
|
|
||||||
write_fraction: Optional[Distribution] = None) \
|
|
||||||
-> float:
|
|
||||||
return 1 / self.load(read_fraction, write_fraction)
|
|
||||||
|
|
||||||
def get_read_quorum(self) -> Set[T]:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_write_quorum(self) -> Set[T]:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class ExplicitStrategy(Strategy[T]):
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
nodes: Set[Node[T]],
|
nodes: Set[Node[T]],
|
||||||
reads: List[Set[T]],
|
reads: List[Set[T]],
|
||||||
|
@ -63,16 +45,21 @@ class ExplicitStrategy(Strategy[T]):
|
||||||
non_zero_writes = {tuple(w): p
|
non_zero_writes = {tuple(w): p
|
||||||
for (w, p) in zip(self.writes, self.write_weights)
|
for (w, p) in zip(self.writes, self.write_weights)
|
||||||
if p > 0}
|
if p > 0}
|
||||||
return (f'ExplicitStrategy(reads={non_zero_reads}, ' +
|
return f'Strategy(reads={non_zero_reads}, writes={non_zero_writes})'
|
||||||
f'writes={non_zero_writes})')
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (f'ExplicitStrategy(nodes={self.nodes}, '+
|
return (f'Strategy(nodes={self.nodes}, '+
|
||||||
f'reads={self.reads}, ' +
|
f'reads={self.reads}, ' +
|
||||||
f'read_weights={self.read_weights},' +
|
f'read_weights={self.read_weights},' +
|
||||||
f'writes={self.writes}, ' +
|
f'writes={self.writes}, ' +
|
||||||
f'write_weights={self.write_weights})')
|
f'write_weights={self.write_weights})')
|
||||||
|
|
||||||
|
def get_read_quorum(self) -> Set[T]:
|
||||||
|
return np.random.choice(self.reads, p=self.read_weights)
|
||||||
|
|
||||||
|
def get_write_quorum(self) -> Set[T]:
|
||||||
|
return np.random.choice(self.writes, p=self.write_weights)
|
||||||
|
|
||||||
def load(self,
|
def load(self,
|
||||||
read_fraction: Optional[Distribution] = None,
|
read_fraction: Optional[Distribution] = None,
|
||||||
write_fraction: Optional[Distribution] = None) \
|
write_fraction: Optional[Distribution] = None) \
|
||||||
|
@ -90,6 +77,79 @@ class ExplicitStrategy(Strategy[T]):
|
||||||
return sum(weight * self._node_load(x, fr)
|
return sum(weight * self._node_load(x, fr)
|
||||||
for (fr, weight) in d.items())
|
for (fr, weight) in d.items())
|
||||||
|
|
||||||
|
def capacity(self,
|
||||||
|
read_fraction: Optional[Distribution] = None,
|
||||||
|
write_fraction: Optional[Distribution] = None) \
|
||||||
|
-> float:
|
||||||
|
return 1 / self.load(read_fraction, write_fraction)
|
||||||
|
|
||||||
|
|
||||||
|
def plot_node_load(self,
|
||||||
|
filename: str,
|
||||||
|
nodes: Optional[List[Node[T]]] = None,
|
||||||
|
read_fraction: Optional[Distribution] = None,
|
||||||
|
write_fraction: Optional[Distribution] = None) \
|
||||||
|
-> None:
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
self.plot_node_load_on(ax,
|
||||||
|
nodes=nodes or list(self.nodes),
|
||||||
|
read_fraction=read_fraction,
|
||||||
|
write_fraction=write_fraction)
|
||||||
|
ax.set_xlabel('Node')
|
||||||
|
ax.set_ylabel('Load')
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig(filename)
|
||||||
|
|
||||||
|
def plot_node_load_on(self,
|
||||||
|
ax: plt.Axes,
|
||||||
|
nodes: Optional[List[Node[T]]] = None,
|
||||||
|
read_fraction: Optional[Distribution] = None,
|
||||||
|
write_fraction: Optional[Distribution] = None) \
|
||||||
|
-> None:
|
||||||
|
nodes = nodes or list(self.nodes)
|
||||||
|
x_list = [node.x for node in nodes]
|
||||||
|
x_index = {x: i for (i, x) in enumerate(x_list)}
|
||||||
|
x_ticks = list(range(len(x_list)))
|
||||||
|
read_cmap = matplotlib.cm.get_cmap('Reds')
|
||||||
|
write_cmap = matplotlib.cm.get_cmap('Blues')
|
||||||
|
d = distribution.canonicalize_rw(read_fraction, write_fraction)
|
||||||
|
fr = sum(weight * fr for (fr, weight) in d.items())
|
||||||
|
fw = 1 - fr
|
||||||
|
|
||||||
|
def read_quorum_to_bar_heights(quorum: Set[T]) -> np.array:
|
||||||
|
bar_heights = np.zeros(len(x_list))
|
||||||
|
for x in quorum:
|
||||||
|
bar_heights[x_index[x]] = 1 / self.read_capacity[x]
|
||||||
|
return bar_heights
|
||||||
|
|
||||||
|
def write_quorum_to_bar_heights(quorum: Set[T]) -> np.array:
|
||||||
|
bar_heights = np.zeros(len(x_list))
|
||||||
|
for x in quorum:
|
||||||
|
bar_heights[x_index[x]] = 1 / self.write_capacity[x]
|
||||||
|
return bar_heights
|
||||||
|
|
||||||
|
bottom = np.zeros(len(x_list))
|
||||||
|
for (i, (rq, weight)) in enumerate(zip(self.reads, self.read_weights)):
|
||||||
|
bar_heights = fr * weight * read_quorum_to_bar_heights(rq)
|
||||||
|
ax.bar(x_ticks,
|
||||||
|
bar_heights,
|
||||||
|
bottom=bottom,
|
||||||
|
color=read_cmap(1 - i * 0.75 / len(self.reads)),
|
||||||
|
edgecolor='white', width=0.8)
|
||||||
|
bottom += bar_heights
|
||||||
|
|
||||||
|
for (i, (wq, weight)) in enumerate(zip(self.writes, self.write_weights)):
|
||||||
|
bar_heights = fw * weight * write_quorum_to_bar_heights(wq)
|
||||||
|
ax.bar(x_ticks,
|
||||||
|
bar_heights,
|
||||||
|
bottom=bottom,
|
||||||
|
color=write_cmap(1 - i * 0.75 / len(self.writes)),
|
||||||
|
edgecolor='white', width=0.8)
|
||||||
|
bottom += bar_heights
|
||||||
|
|
||||||
|
ax.set_xticks(x_ticks)
|
||||||
|
ax.set_xticklabels(str(x) for x in x_list)
|
||||||
|
|
||||||
def _node_load(self, x: T, fr: float) -> float:
|
def _node_load(self, x: T, fr: float) -> float:
|
||||||
"""
|
"""
|
||||||
_node_load returns the load on x given a fixed read fraction fr.
|
_node_load returns the load on x given a fixed read fraction fr.
|
||||||
|
@ -105,9 +165,3 @@ class ExplicitStrategy(Strategy[T]):
|
||||||
return max(self._node_load(node.x, fr) for node in self.nodes)
|
return max(self._node_load(node.x, fr) for node in self.nodes)
|
||||||
|
|
||||||
# TODO(mwhittaker): Add read/write load and capacity and read/write cap.
|
# TODO(mwhittaker): Add read/write load and capacity and read/write cap.
|
||||||
|
|
||||||
def get_read_quorum(self) -> Set[T]:
|
|
||||||
return np.random.choice(self.reads, p=self.read_weights)
|
|
||||||
|
|
||||||
def get_write_quorum(self) -> Set[T]:
|
|
||||||
return np.random.choice(self.writes, p=self.write_weights)
|
|
||||||
|
|
Loading…
Reference in a new issue