Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/PolyPhyHub/PolyPhy
Browse files Browse the repository at this point in the history
  • Loading branch information
OskarElek committed Dec 8, 2023
2 parents bca5316 + 52458a0 commit bb5c74c
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 120 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/batch_mode.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Polyphy CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Create /tmp/flag file
run: echo "This is a flag file" > /tmp/flag

- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run polyphy.py for 2D_discrete
run: |
cd src/polyphy/ && python3 polyphy.py 2d_discrete -b -n 100 -f data/csv/sample_2D_linW.csv
- name: Run polyphy.py for 3D_discrete
run: |
cd src/polyphy/ && python3 polyphy.py 3d_discrete -b -n 100 -f data/csv/sample_3D_linW.csv
- name: Check if 4 files are created in data/fits
run: |
file_count=$(ls -1q data/fits/*.npy | wc -l)
if [ $file_count -eq 4 ]; then
echo "Success: 4 files found in data/fits"
else
echo "Error: Expected 4 files in data/fits, found $file_count"
exit 1
fi
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
# stop the build if there are Python syntax errors or undefined names
flake8 ./src --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 90 chars wide
flake8 ./src --ignore=E501,W503,E402,F401,F403,E722,F841,W504,C901,E302,E226,E126 --max-line-length=90 --max-complexity=36 2>&1
flake8 ./src --ignore=E501,W503,E402,F401,F403,E722,F841,W504,C901,E302,E226,E126,E123 --max-line-length=90 --max-complexity=36 2>&1
# isort ./src -c -v
- name: Test with pytest
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

- name: Check if Python package already exists
run: |
pip install polyphy==$(cat pyproject.toml | head -3 | tail -1 | cut -d '"' -f 2) && echo "true" > ./flag || echo "false" > ./flag
pip install PolyPhyPy==$(cat pyproject.toml | head -3 | tail -1 | cut -d '"' -f 2) && echo "true" > ./flag || echo "false" > ./flag
- name: Build and publish package
env:
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ status](https://api.reuse.software/badge/git.fsfe.org/reuse/api)](https://api.re
- Email list: [Google Groups](https://groups.google.com/g/polyphy-news)

## System Requirements
- Decent GPU, recommended a mid-range discrete one
- currently running best on NVIDIA GPUs, other brands supported as well (subject to the current capabilities of the [Taichi API](https://github.com/taichi-dev/taichi))
- Decent GPU, recommended a mid-range discrete NVIDIA/AMD device
- currently running best on NVIDIA GPUs, other vendors supported as well (subject to the current capabilities of the [Taichi API](https://github.com/taichi-dev/taichi))
- CPU fallback available for debugging purposes
- Corresponding GPU environment and drivers (e.g. Vulkan, Metal)
- Recent Windows, Linux or Mac OS
- Python 3.x, Anaconda recommended

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[project]
name = "polyphy"
name = "PolyPhyPy"
version = "0.0.1"
description = "description for polyphy"
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ formats = bdist_wheel
[flake8]
# Some sane defaults for the code style checker flake8
max_line_length = 88
extend_ignore = E203, W503
extend_ignore = E203, W503, E501
# ^ Black-compatible
# E203 and W503 have edge cases handled by black
exclude =
Expand Down
3 changes: 1 addition & 2 deletions src/polyphy/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ def set_precision(float_precision):
PPTypes.FLOAT_CPU = np.float16
PPTypes.FLOAT_GPU = ti.f16
else:
raise ValueError("Invalid float precision value. Supported values: \
float64, float32, float16")
raise ValueError("Invalid float precision value. Supported values: float64, float32, float16")


class PPConfig:
Expand Down
34 changes: 15 additions & 19 deletions src/polyphy/core/discrete3D.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@ def register_data(self, ppData):
self.DOMAIN_SIZE_MAX = np.max(
[ppData.DOMAIN_SIZE[0], ppData.DOMAIN_SIZE[1], ppData.DOMAIN_SIZE[2]])
self.TRACE_RESOLUTION = PPTypes.INT_CPU((
PPTypes.FLOAT_CPU(self.TRACE_RESOLUTION_MAX) * ppData.DOMAIN_SIZE[0] /
self.DOMAIN_SIZE_MAX,
PPTypes.FLOAT_CPU(self.TRACE_RESOLUTION_MAX) * ppData.DOMAIN_SIZE[1] /
self.DOMAIN_SIZE_MAX,
PPTypes.FLOAT_CPU(self.TRACE_RESOLUTION_MAX) * ppData.DOMAIN_SIZE[2] /
self.DOMAIN_SIZE_MAX))
PPTypes.FLOAT_CPU(self.TRACE_RESOLUTION_MAX) * ppData.DOMAIN_SIZE[0] / self.DOMAIN_SIZE_MAX,
PPTypes.FLOAT_CPU(self.TRACE_RESOLUTION_MAX) * ppData.DOMAIN_SIZE[1] / self.DOMAIN_SIZE_MAX,
PPTypes.FLOAT_CPU(self.TRACE_RESOLUTION_MAX) * ppData.DOMAIN_SIZE[2] / self.DOMAIN_SIZE_MAX
))
self.DEPOSIT_RESOLUTION = (
self.TRACE_RESOLUTION[0] // PPConfig.DEPOSIT_DOWNSCALING_FACTOR,
self.TRACE_RESOLUTION[1] // PPConfig.DEPOSIT_DOWNSCALING_FACTOR,
self.TRACE_RESOLUTION[2] // PPConfig.DEPOSIT_DOWNSCALING_FACTOR)
self.TRACE_RESOLUTION[2] // PPConfig.DEPOSIT_DOWNSCALING_FACTOR
)

# Check if these are set and if not give them decent initial estimates
if self.sense_distance < 1.e-4:
Expand Down Expand Up @@ -137,12 +136,10 @@ def store_fit(self):
Logger.logToStdOut("info", 'Storing solution data in data/fits/')
deposit = self.deposit_field.to_numpy()
np.save(
self.ppConfig.ppData.ROOT + 'data/fits/deposit_' + current_stamp +
'.npy', deposit)
self.ppConfig.ppData.ROOT + 'data/fits/deposit_' + current_stamp + '.npy', deposit)
trace = self.trace_field.to_numpy()
np.save(
self.ppConfig.ppData.ROOT + 'data/fits/trace_' + current_stamp +
'.npy', trace)
self.ppConfig.ppData.ROOT + 'data/fits/trace_' + current_stamp + '.npy', trace)
return current_stamp, deposit, trace

def __init__(self, rng, ppKernels, ppConfig):
Expand Down Expand Up @@ -195,13 +192,8 @@ def __init__(self, rng, ppKernels, ppConfig):
Logger.logToStdOut(
"info",
'Total GPU memory allocated:', PPTypes.INT_CPU(
4 * (
self.data_field.shape[0] * 4 +
self.agents_field.shape[0] * 6 +
self.deposit_field.shape[0] * self.deposit_field.shape[1] * 2 +
self.trace_field.shape[0] * self.trace_field.shape[1] * 1 +
self.vis_field.shape[0] * self.vis_field.shape[1] * 3
) / 2 ** 20), 'MB')
4 * (self.data_field.shape[0] * 4 + self.agents_field.shape[0] * 6 + self.deposit_field.shape[0] * self.deposit_field.shape[1] * 2 + self.trace_field.shape[0] * self.trace_field.shape[1] * 1 + self.vis_field.shape[0] * self.vis_field.shape[1] * 3
) / 2 ** 20), 'MB')
self.ppConfig = ppConfig
self.ppKernels = ppKernels
self.__init_internal_data__(ppKernels)
Expand All @@ -211,7 +203,11 @@ class PPSimulation_3DDiscrete(PPSimulation):
def __drawGUI__(self, window, ppConfig):
GuiHelper.draw(self, window, ppConfig)

def __init__(self, ppInternalData, ppConfig, batch_mode=False, num_iterations=-1):
def __init__(self,
ppInternalData,
ppConfig,
batch_mode=False,
num_iterations=-1):
self.current_deposit_index = 0
self.do_export = False
self.do_screenshot = False
Expand Down
3 changes: 1 addition & 2 deletions src/polyphy/kernel/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ def ray_AABB_intersection(self, ray_pos, ray_dir, AABB_min, AABB_max):
t5 = (AABB_max[2] - ray_pos[2]) / ray_dir[2]
t6 = ti.max(ti.max(ti.min(t0, t1), ti.min(t2, t3)), ti.min(t4, t5))
t7 = ti.min(ti.min(ti.max(t0, t1), ti.max(t2, t3)), ti.max(t4, t5))
return PPTypes.VEC2f(-1.0, -1.0) if (
t7 < 0.0 or t6 >= t7) else PPTypes.VEC2f(t6, t7)
return PPTypes.VEC2f(-1.0, -1.0) if (t7 < 0.0 or t6 >= t7) else PPTypes.VEC2f(t6, t7)

# GPU kernels (callable by core classes via Taichi API) ========================
@ti.kernel
Expand Down
85 changes: 29 additions & 56 deletions src/polyphy/kernel/discrete2D.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,86 +81,64 @@ def agent_step_2D_discrete(
# Generate new mutated direction by perturbing the original
dir_fwd = self.angle_to_dir_2D(angle)
angle_mut = angle
if directional_sampling_distribution \
== PPConfig.EnumDirectionalSamplingDistribution.DISCRETE:
angle_mut += (
1.0 if ti.random(dtype=PPTypes.FLOAT_GPU) > 0.5 else -1.0)\
* sense_angle
elif directional_sampling_distribution \
== PPConfig.EnumDirectionalSamplingDistribution.CONE:
angle_mut += 2.0 * (ti.random(dtype=PPTypes.FLOAT_GPU) - 0.5)\
* sense_angle
if directional_sampling_distribution == PPConfig.EnumDirectionalSamplingDistribution.DISCRETE:
angle_mut += (1.0 if ti.random(dtype=PPTypes.FLOAT_GPU) > 0.5 else -1.0) * sense_angle
elif directional_sampling_distribution == PPConfig.EnumDirectionalSamplingDistribution.CONE:
angle_mut += 2.0 * (ti.random(dtype=PPTypes.FLOAT_GPU) - 0.5) * sense_angle
dir_mut = self.angle_to_dir_2D(angle_mut)

# Generate sensing distance for the agent, constant or probabilistic
agent_sensing_distance = sense_distance
distance_scaling_factor = 1.0
if distance_sampling_distribution \
== PPConfig.EnumDistanceSamplingDistribution.EXPONENTIAL:
if distance_sampling_distribution == PPConfig.EnumDistanceSamplingDistribution.EXPONENTIAL:
xi = timath.clamp(ti.random(dtype=PPTypes.FLOAT_GPU), 0.001, 0.999)
# log & pow are unstable in extremes
distance_scaling_factor = -ti.log(xi)
elif distance_sampling_distribution \
== PPConfig.EnumDistanceSamplingDistribution.MAXWELL_BOLTZMANN:
elif distance_sampling_distribution == PPConfig.EnumDistanceSamplingDistribution.MAXWELL_BOLTZMANN:
xi = timath.clamp(ti.random(dtype=PPTypes.FLOAT_GPU), 0.001, 0.999)
# log & pow are unstable in extremes
distance_scaling_factor = -0.3033 * ti.log(
(ti.pow(xi + 0.005, -0.4) - 0.9974) / 7.326)
distance_scaling_factor = -0.3033 * ti.log((ti.pow(xi + 0.005, -0.4) - 0.9974) / 7.326)
agent_sensing_distance *= distance_scaling_factor

# Fetch deposit to guide the agent
deposit_fwd = 1.0
deposit_mut = 0.0
if deposit_fetching_strategy == PPConfig.EnumDepositFetchingStrategy.NN:
deposit_fwd = deposit_field[self.world_to_grid_2D(
pos + agent_sensing_distance * dir_fwd, PPTypes.VEC2f(DOMAIN_MIN),
pos + agent_sensing_distance * dir_fwd,
PPTypes.VEC2f(DOMAIN_MIN),
PPTypes.VEC2f(DOMAIN_MAX),
PPTypes.VEC2i(DEPOSIT_RESOLUTION))][current_deposit_index]
deposit_mut = deposit_field[self.world_to_grid_2D(
pos + agent_sensing_distance * dir_mut,
PPTypes.VEC2f(DOMAIN_MIN), PPTypes.VEC2f(DOMAIN_MAX),
PPTypes.VEC2f(DOMAIN_MIN),
PPTypes.VEC2f(DOMAIN_MAX),
PPTypes.VEC2i(DEPOSIT_RESOLUTION))][current_deposit_index]
elif deposit_fetching_strategy \
== PPConfig.EnumDepositFetchingStrategy.NN_PERTURBED:
elif deposit_fetching_strategy == PPConfig.EnumDepositFetchingStrategy.NN_PERTURBED:
# Fetches the deposit by perturbing the original position by small delta
# Provides cheap stochastic filtering instead of multi-fetch filters
field_dd = 2.0 * ti.cast(DOMAIN_SIZE[0], PPTypes.FLOAT_GPU) / \
ti.cast(DEPOSIT_RESOLUTION[0], PPTypes.FLOAT_GPU)
pos_fwd = pos + agent_sensing_distance * dir_fwd + (
field_dd * ti.random(dtype=PPTypes.FLOAT_GPU) *
self.angle_to_dir_2D(2.0 * timath.pi *
ti.random(dtype=PPTypes.FLOAT_GPU)))
field_dd = 2.0 * ti.cast(DOMAIN_SIZE[0], PPTypes.FLOAT_GPU) / ti.cast(DEPOSIT_RESOLUTION[0], PPTypes.FLOAT_GPU)
pos_fwd = pos + agent_sensing_distance * dir_fwd + (field_dd * ti.random(dtype=PPTypes.FLOAT_GPU) * self.angle_to_dir_2D(2.0 * timath.pi * ti.random(dtype=PPTypes.FLOAT_GPU)))
deposit_fwd = deposit_field[self.world_to_grid_2D(
pos_fwd, PPTypes.VEC2f(DOMAIN_MIN),
PPTypes.VEC2f(DOMAIN_MAX),
PPTypes.VEC2i(DEPOSIT_RESOLUTION))][current_deposit_index]
pos_mut = pos + agent_sensing_distance * dir_mut + (
field_dd * ti.random(dtype=PPTypes.FLOAT_GPU) *
self.angle_to_dir_2D(2.0 * timath.pi *
ti.random(dtype=PPTypes.FLOAT_GPU)))
pos_mut = pos + agent_sensing_distance * dir_mut + (field_dd * ti.random(dtype=PPTypes.FLOAT_GPU) * self.angle_to_dir_2D(2.0 * timath.pi * ti.random(dtype=PPTypes.FLOAT_GPU)))
deposit_mut = deposit_field[self.world_to_grid_2D(
pos_mut, PPTypes.VEC2f(DOMAIN_MIN),
pos_mut,
PPTypes.VEC2f(DOMAIN_MIN),
PPTypes.VEC2f(DOMAIN_MAX),
PPTypes.VEC2i(DEPOSIT_RESOLUTION))][current_deposit_index]

# Generate new direction for the agent based on the sampled deposit
angle_new = angle
if directional_mutation_type \
== PPConfig.EnumDirectionalMutationType.DETERMINISTIC:
angle_new = (
steering_rate * angle_mut + (1.0-steering_rate) * angle) if (
deposit_mut > deposit_fwd) else (
angle)
elif directional_mutation_type \
== PPConfig.EnumDirectionalMutationType.PROBABILISTIC:
if directional_mutation_type == PPConfig.EnumDirectionalMutationType.DETERMINISTIC:
angle_new = (steering_rate * angle_mut + (1.0-steering_rate) * angle) if (deposit_mut > deposit_fwd) else (angle)
elif directional_mutation_type == PPConfig.EnumDirectionalMutationType.PROBABILISTIC:
p_remain = ti.pow(deposit_fwd, sampling_exponent)
p_mutate = ti.pow(deposit_mut, sampling_exponent)
mutation_probability = p_mutate / (p_remain + p_mutate)
angle_new = (
steering_rate * angle_mut + (1.0-steering_rate) * angle) if (
ti.random(
dtype=PPTypes.FLOAT_GPU) < mutation_probability) else (
angle)
angle_new = (steering_rate * angle_mut + (1.0-steering_rate) * angle) if (ti.random(dtype=PPTypes.FLOAT_GPU) < mutation_probability) else (angle)
dir_new = self.angle_to_dir_2D(angle_new)
pos_new = pos + step_size * distance_scaling_factor * dir_new

Expand All @@ -172,33 +150,30 @@ def agent_step_2D_discrete(
pos_new[1] = self.custom_mod(
pos_new[1] - DOMAIN_MIN[1] + DOMAIN_SIZE[1],
DOMAIN_SIZE[1]) + DOMAIN_MIN[1]
elif agent_boundary_handling \
== PPConfig.EnumAgentBoundaryHandling.REINIT_CENTER:
elif agent_boundary_handling == PPConfig.EnumAgentBoundaryHandling.REINIT_CENTER:
if pos_new[0] <= DOMAIN_MIN[0] \
or pos_new[0] >= DOMAIN_MAX[0] \
or pos_new[1] <= DOMAIN_MIN[1] \
or pos_new[1] >= DOMAIN_MAX[1]:
pos_new[0] = 0.5 * (DOMAIN_MIN[0] + DOMAIN_MAX[0])
pos_new[1] = 0.5 * (DOMAIN_MIN[1] + DOMAIN_MAX[1])
elif agent_boundary_handling \
== PPConfig.EnumAgentBoundaryHandling.REINIT_RANDOMLY:
elif agent_boundary_handling == PPConfig.EnumAgentBoundaryHandling.REINIT_RANDOMLY:
if pos_new[0] <= DOMAIN_MIN[0] \
or pos_new[0] >= DOMAIN_MAX[0] \
or pos_new[1] <= DOMAIN_MIN[1] \
or pos_new[1] >= DOMAIN_MAX[1]:
pos_new[0] = DOMAIN_MIN[0] + timath.clamp(
ti.random(dtype=PPTypes.FLOAT_GPU),
0.001, 0.999) * DOMAIN_SIZE[0]
ti.random(dtype=PPTypes.FLOAT_GPU), 0.001, 0.999) * DOMAIN_SIZE[0]
pos_new[1] = DOMAIN_MIN[1] + timath.clamp(
ti.random(dtype=PPTypes.FLOAT_GPU),
0.001, 0.999) * DOMAIN_SIZE[1]
ti.random(dtype=PPTypes.FLOAT_GPU), 0.001, 0.999) * DOMAIN_SIZE[1]

agents_field[agent][0] = pos_new[0]
agents_field[agent][1] = pos_new[1]
agents_field[agent][2] = angle_new

# Generate deposit and trace at the new position
deposit_cell = self.world_to_grid_2D(pos_new, PPTypes.VEC2f(DOMAIN_MIN),
deposit_cell = self.world_to_grid_2D(pos_new,
PPTypes.VEC2f(DOMAIN_MIN),
PPTypes.VEC2f(DOMAIN_MAX),
PPTypes.VEC2i(DEPOSIT_RESOLUTION))
deposit_field[deposit_cell][current_deposit_index] += agent_deposit * weight
Expand Down Expand Up @@ -249,8 +224,7 @@ def trace_relaxation_step_2D_discrete(
for cell in ti.grouped(trace_field):
# Perturb the attenuation by a small factor
# to avoid accumulating quantization errors
trace_field[cell][0] *= (
attenuation - 0.001 + 0.002 * ti.random(dtype=PPTypes.FLOAT_GPU))
trace_field[cell][0] *= (attenuation - 0.001 + 0.002 * ti.random(dtype=PPTypes.FLOAT_GPU))
return

@ti.kernel
Expand All @@ -276,6 +250,5 @@ def render_visualization_2D_discrete(
PPTypes.VEC3f(
trace_vis * trace_val,
deposit_vis * deposit_val,
ti.pow(ti.log(1.0 + 0.2 * trace_vis * trace_val),
3.0)), 1.0/2.2)
ti.pow(ti.log(1.0 + 0.2 * trace_vis * trace_val), 3.0)), 1.0/2.2)
return
Loading

0 comments on commit bb5c74c

Please sign in to comment.