Skip to content

Commit

Permalink
In progress: finish refactor of all generators; pending tests
Browse files Browse the repository at this point in the history
  • Loading branch information
knakamura13 committed Aug 13, 2024
1 parent 015f6f3 commit c3cd776
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 71 deletions.
7 changes: 3 additions & 4 deletions mlrose_hiive/generators/continuous_peaks_generator.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"""Classes for defining optimization problem objects."""
"""Class defining a Continuous Peaks optimization problem generator."""

# Author: Andrew Rollings
# Authors: Andrew Rollings (modified by Kyle Nakamura)
# License: BSD 3 clause

import numpy as np
from typing import Any

from mlrose_hiive import DiscreteOpt, ContinuousPeaks

Expand All @@ -13,7 +12,7 @@ class ContinuousPeaksGenerator:
"""A class to generate Continuous Peaks optimization problems."""

@staticmethod
def generate(seed: int, size: int = 20, threshold_percentage: float = 0.1) -> Any:
def generate(seed: int, size: int = 20, threshold_percentage: float = 0.1) -> DiscreteOpt:
"""
Generate a Continuous Peaks optimization problem instance.
Expand Down
34 changes: 30 additions & 4 deletions mlrose_hiive/generators/flip_flop_generator.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
"""Classes for defining optimization problem objects."""
"""Class defining a FlipFlop optimization problem generator."""

# Author: Andrew Rollings
# Authors: Andrew Rollings (modified by Kyle Nakamura)
# License: BSD 3 clause

import numpy as np

from mlrose_hiive import FlipFlopOpt


class FlipFlopGenerator:
"""A class to generate FlipFlop optimization problem instances."""

@staticmethod
def generate(seed, size=20):
def generate(seed: int, size: int = 20) -> FlipFlopOpt:
"""
Generate a FlipFlop optimization problem with a given seed and size.
Parameters
----------
seed : int
The seed for the random number generator.
size : int, optional
The size of the problem (default is 20).
Returns
-------
FlipFlopOpt
An instance of FlipFlopOpt configured with the specified seed and size.
Raises
------
ValueError
If the size is not a positive integer.
"""
if size <= 0:
raise ValueError(f"Size must be a positive integer. Got {size}.")

np.random.seed(seed)

problem = FlipFlopOpt(length=size)

return problem
41 changes: 37 additions & 4 deletions mlrose_hiive/generators/four_peaks_generator.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
"""Classes for defining optimization problem objects."""
"""Class defining a Four Peaks optimization problem generator."""

# Authors: Andrew Rollings (modified by Kyle Nakamura)
# License: BSD 3 clause

import numpy as np

from mlrose_hiive import DiscreteOpt, FourPeaks


class FourPeaksGenerator:
"""Generator class for Four Peaks."""
"""A class to generate Four Peaks optimization problem instances."""

@staticmethod
def generate(seed, size=20, t_pct=0.1):
def generate(seed: int, size: int = 20, threshold_percentage: float = 0.1) -> DiscreteOpt:
"""
Generate a Four Peaks optimization problem with a given seed, size, and threshold percentage.
Parameters
----------
seed : int
The seed for the random number generator.
size : int, optional
The size of the problem (default is 20).
threshold_percentage : float, optional
The threshold percentage for the Four Peaks problem (default is 0.1).
Returns
-------
DiscreteOpt
An instance of DiscreteOpt configured with the specified parameters.
Raises
------
ValueError
If the size is not a positive integer or if the threshold_percentage is not between 0 and 1.
"""
if size <= 0:
raise ValueError(f"Size must be a positive integer. Got {size}.")
if not (0 <= threshold_percentage <= 1):
raise ValueError(f"Threshold percentage must be between 0 and 1. Got {threshold_percentage}.")

np.random.seed(seed)
fitness = FourPeaks(threshold_percentage=t_pct)

fitness = FourPeaks(threshold_percentage=threshold_percentage)
problem = DiscreteOpt(length=size, fitness_fn=fitness)

return problem
82 changes: 70 additions & 12 deletions mlrose_hiive/generators/knapsack_generator.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,80 @@
"""Classes for defining optimization problem objects."""
"""Class defining a Knapsack optimization problem generator."""

# Author: Genevieve Hayes
# Author: Genevieve Hayes (modified by Kyle Nakamura)
# License: BSD 3 clause

import numpy as np

import mlrose_hiive
from mlrose_hiive import KnapsackOpt


class KnapsackGenerator:
"""A class to generate Knapsack optimization problems."""

@staticmethod
def generate(seed, number_of_items_types=10,
max_item_count=5, max_weight_per_item=25,
max_value_per_item=10, max_weight_pct=0.6,
multiply_by_max_item_count=True):
def generate(seed: int,
number_of_item_types: int = 10,
max_item_count: int = 5,
max_weight_per_item: int = 25,
max_value_per_item: int = 10,
max_weight_percentage: float = 0.6,
multiply_by_max_item_count: bool = True) -> KnapsackOpt:
"""
Generate a Knapsack optimization problem instance.
Parameters
----------
seed : int
Seed for the random number generator.
number_of_item_types : int, optional, default=10
Number of different item types.
max_item_count : int, optional, default=5
Maximum count for each item type.
max_weight_per_item : int, optional, default=25
Maximum weight for each item type.
max_value_per_item : int, optional, default=10
Maximum value for each item type.
max_weight_percentage : float, optional, default=0.6
Maximum weight percentage of the knapsack.
multiply_by_max_item_count : bool, optional, default=True
If True, multiply weights and values by max_item_count.
Returns
-------
KnapsackOpt
An instance of KnapsackOpt configured with the specified parameters.
Raises
------
ValueError
If any parameter is not of the expected type or value.
"""
if not isinstance(seed, int):
raise ValueError(f"Seed must be an integer. Got {seed}")
if not isinstance(number_of_item_types, int) or number_of_item_types <= 0:
raise ValueError(f"Number of item types must be a positive integer. Got {number_of_item_types}")
if not isinstance(max_item_count, int) or max_item_count <= 0:
raise ValueError(f"Max item count must be a positive integer. Got {max_item_count}")
if not isinstance(max_weight_per_item, int) or max_weight_per_item <= 0:
raise ValueError(f"Max weight per item must be a positive integer. Got {max_weight_per_item}")
if not isinstance(max_value_per_item, int) or max_value_per_item <= 0:
raise ValueError(f"Max value per item must be a positive integer. Got {max_value_per_item}")
if not isinstance(max_weight_percentage, float) or not (0 <= max_weight_percentage <= 1):
raise ValueError(f"Max weight percentage must be a float between 0 and 1. Got {max_weight_percentage}")
if not isinstance(multiply_by_max_item_count, bool):
raise ValueError(f"multiply_by_max_item_count must be a boolean. Got {multiply_by_max_item_count}")

np.random.seed(seed)
weights = 1 + np.random.randint(max_weight_per_item, size=number_of_items_types)
values = 1 + np.random.randint(max_value_per_item, size=number_of_items_types)
problem = mlrose_hiive.KnapsackOpt(length=number_of_items_types, max_val=max_item_count, weights=weights, values=values,
max_weight_pct=max_weight_pct, multiply_by_max_item_count=multiply_by_max_item_count)

weights = 1 + np.random.randint(max_weight_per_item, size=number_of_item_types)
values = 1 + np.random.randint(max_value_per_item, size=number_of_item_types)

problem = KnapsackOpt(
length=number_of_item_types,
max_val=max_item_count,
weights=weights,
values=values,
max_weight_pct=max_weight_percentage,
multiply_by_max_item_count=multiply_by_max_item_count
)

return problem
99 changes: 74 additions & 25 deletions mlrose_hiive/generators/max_k_color_generator.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,92 @@
from mlrose_hiive.opt_probs import MaxKColorOpt
"""Class defining a Max-K Color optimization problem generator."""

import numpy as np
import networkx as nx

from mlrose_hiive.opt_probs import MaxKColorOpt


class MaxKColorGenerator:
@staticmethod
def generate(seed, number_of_nodes=20, max_connections_per_node=4, max_colors=None, maximize=False):
"""A class to generate Max-K Color optimization problems."""

@staticmethod
def generate(seed: int,
number_of_nodes: int = 20,
max_connections_per_node: int = 4,
max_colors: int | None = None,
maximize: bool = False) -> MaxKColorOpt:
"""
>>> edges = [(0, 1), (0, 2), (0, 4), (1, 3), (2, 0), (2, 3), (3, 4)]
>>> fitness = mlrose_hiive.MaxKColor(edges)
Generate a Max-K Color optimization problem instance.
Parameters
----------
seed : int
Seed for the random number generator.
number_of_nodes : int, optional, default=20
The number of nodes in the graph.
max_connections_per_node : int, optional, default=4
The maximum number of connections (edges) per node.
max_colors : int or None, optional, default=None
The maximum number of colors available.
maximize : bool, optional, default=False
Whether the optimization problem should be maximized.
Returns
-------
MaxKColorOpt
An instance of MaxKColorOpt configured with the specified parameters.
Raises
------
ValueError
If any parameter is not of the expected type or value.
Examples
--------
>>> import mlrose_hiive
>>> test_edges = [(0, 1), (0, 2), (0, 4), (1, 3), (2, 0), (2, 3), (3, 4)]
>>> fitness = mlrose_hiive.MaxKColor(test_edges)
>>> state = np.array([0, 1, 0, 1, 1])
>>> fitness.evaluate(state)
3.0
"""
if not isinstance(seed, int):
raise ValueError(f"Seed must be an integer. Got {seed}")
if not isinstance(number_of_nodes, int) or number_of_nodes <= 0:
raise ValueError(f"Number of nodes must be a positive integer. Got {number_of_nodes}")
if not isinstance(max_connections_per_node, int) or max_connections_per_node <= 0:
raise ValueError(f"Max connections per node must be a positive integer. Got {max_connections_per_node}")
if max_colors is not None and not isinstance(max_colors, int):
raise ValueError(f"Max colors must be an integer or None. Got {max_colors}")
if not isinstance(maximize, bool):
raise ValueError(f"Maximize must be a boolean. Got {maximize}")

np.random.seed(seed)
# all nodes have to be connected, somehow.

# Generate random connection counts for each node
node_connection_counts = 1 + np.random.choice(max_connections_per_node, size=number_of_nodes)

node_connections = {}
nodes = range(number_of_nodes)
for n in nodes:
all_other_valid_nodes = [o for o in nodes if (o != n and (o not in node_connections or
n not in node_connections[o]))]
count = min(node_connection_counts[n], len(all_other_valid_nodes))
other_nodes = sorted(np.random.choice(all_other_valid_nodes, count, replace=False))
node_connections[n] = [(n, o) for o in other_nodes]

# check connectivity
g = nx.Graph()
g.add_edges_from([x for y in node_connections.values() for x in y])

for n in nodes:
cannot_reach = [(n, o) if n < o else (o, n) for o in nodes if o not in nx.bfs_tree(g, n).nodes()]
for s, f in cannot_reach:
g.add_edge(s, f)
check_reach = len([(n, o) if n < o else (o, n) for o in nodes if o not in nx.bfs_tree(g, n).nodes()])
if check_reach == 0:
for node in nodes:
valid_other_nodes = [other for other in nodes if (
other != node and (other not in node_connections or node not in node_connections[other]))]
count = min(node_connection_counts[node], len(valid_other_nodes))
connected_nodes = sorted(np.random.choice(valid_other_nodes, count, replace=False))
node_connections[node] = [(node, other) for other in connected_nodes]

# Ensure graph connectivity
graph = nx.Graph()
graph.add_edges_from([edge for edges in node_connections.values() for edge in edges])

for node in nodes:
unreachable = [(node, other) if node < other else (other, node) for other in nodes if other not in nx.bfs_tree(graph, node).nodes()]
for start, end in unreachable:
graph.add_edge(start, end)
remaining_unreachable = len([(node, other) if node < other else (other, node) for other in nodes if other not in nx.bfs_tree(graph, node).nodes()])
if remaining_unreachable == 0:
break

edges = [(s, f) for (s, f) in g.edges()]
problem = MaxKColorOpt(edges=edges, length=number_of_nodes, maximize=maximize, max_colors=max_colors, source_graph=g)
problem = MaxKColorOpt(edges=list(graph.edges()), length=number_of_nodes, maximize=maximize, max_colors=max_colors, source_graph=graph)

return problem
37 changes: 33 additions & 4 deletions mlrose_hiive/generators/one_max_generator.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
"""Classes for defining optimization problem objects."""
"""Class defining a One Max optimization problem generator."""

import numpy as np

from mlrose_hiive import DiscreteOpt, OneMax


class OneMaxGenerator:
"""Generator class for One Max."""
"""A class to generate One Max optimization problems."""

@staticmethod
def generate(seed, size=20):
def generate(seed: int, size: int = 20) -> DiscreteOpt:
"""
Generate a One Max optimization problem instance.
Parameters
----------
seed : int
Seed for the random number generator.
size : int, optional, default=20
The size of the optimization problem (number of bits).
Returns
-------
DiscreteOpt
An instance of DiscreteOpt configured for the One Max problem.
Raises
------
ValueError
If `size` is not a positive integer.
"""
if not isinstance(seed, int):
raise ValueError(f"Seed must be an integer. Got {seed}")
if not isinstance(size, int) or size <= 0:
raise ValueError(f"Size must be a positive integer. Got {size}")

np.random.seed(seed)

fitness = OneMax()
return DiscreteOpt(length=size, fitness_fn=fitness)
problem = DiscreteOpt(length=size, fitness_fn=fitness)

return problem
Loading

0 comments on commit c3cd776

Please sign in to comment.