Local Generator

A generator that runs faster based on local dilation instead of the global dilations

An Exception was encountered at ‘In [22]’.

Generating a latent space representation

To make it more “understandable” where material is placed we generate a slowly varying landscape by interpolation of a low resolution map.

def generate_heatmap(features = 8, zoom = 16):
  heatmap = np.random.random((features,features))-0.5
  large_heatmap = scipy.ndimage.zoom(heatmap, zoom)
  return large_heatmap

large_heatmap = generate_heatmap()
plt.imshow(large_heatmap)
plt.xlabel("x")
plt.ylabel("y")
plt.grid()

Generating the brush

def circular_brush(diameter):
    radius = diameter / 2
    X, Y = np.mgrid[-radius : radius : 1j * diameter, -radius : radius : 1j * diameter]
    _int = lambda x: np.array(x, dtype=int)
    brush = _int(X) ** 2 + _int(Y) ** 2 < radius ** 2
    return brush
kernel_size=9
brush = np.ones((kernel_size, kernel_size))
brush[0,0] = 0
brush[0,-1] = 0
brush[-1,0] = 0
brush[-1,-1] = 0

brush = circular_brush(kernel_size)
brush = brush.astype(bool)

def show_brush(brush):
  nx, ny = brush.shape
  plt.imshow(brush)
  ax = plt.gca()
  ax.set_yticks(np.arange(nx)+0.5)
  ax.set_yticklabels(["" for i in range(nx)])
  ax.set_xticks(np.arange(ny)+0.5)
  ax.set_xticklabels(["" for i in range(ny)])
  ax.set_yticks(np.arange(nx), minor=True)
  ax.set_yticklabels([f"{i}" for i in range(nx)], minor=True)
  ax.set_xticks(np.arange(ny), minor=True)
  ax.set_xticklabels([f"{i}" for i in range(ny)], minor=True)
  plt.grid()

show_brush(brush)

conv_brush = scipy.ndimage.binary_dilation(np.pad(brush, len(brush)//2), brush)
show_brush(conv_brush)
plt.colorbar()

plt.figure()
double_conv_brush = scipy.ndimage.binary_dilation(np.pad(conv_brush, len(brush)//2), brush)
show_brush(double_conv_brush)
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7fe7d8cc67f0>

Running the Generator


source

dilate

 dilate (img, brush, count_time=True)

source

Times

 Times (update:float=0, dilate:float=0, required:float=0,
        resolving:float=0, select:float=0, convolute:float=0,
        existing:float=0, impossible:float=0, possible:float=0,
        valid:float=0, free:float=0, local_required:float=0,
        local_resolving:float=0, local_dilate:float=0)

source

GeneratorState

 GeneratorState (heatmap, brush)

Initialize self. See help(type(self)) for accurate signature.


source

log

 log (*args, level=1)
def compare(s_try, s_actual, v_try, v_actual, name):
  """
  Compares properties of a new implementation and the original slow one.
  Raises Error if they are not equal and plots the comparison
  """
  compare_s = np.logical_xor(s_try,s_actual)
  compare_v = np.logical_xor(v_try,v_actual)

  if compare_s.any() or compare_v.any():
    plt.figure(figsize = (6,9))
    plt.subplot(321)
    plt.title("Compare Solid")
    plt.imshow(compare_s, vmax=1, vmin=0)
    plt.subplot(322)
    plt.title("Compare Void")
    plt.imshow(compare_v, vmax=1, vmin=0)
    plt.subplot(323)
    plt.title(f"{name} Solid")
    plt.imshow(s_try, vmax=1, vmin=0)
    plt.subplot(324)
    plt.title(f"{name} Void")
    plt.imshow(v_try, vmax=1, vmin=0)
    plt.subplot(325)
    plt.title(f"Actual {name} Solid")
    plt.imshow(s_actual, vmax=1, vmin=0)
    plt.subplot(326)
    plt.title(f"Actual {name} Void")
    plt.imshow(v_actual, vmax=1, vmin=0)
    plt.show()
    raise ValueError(f"Calculation of {name} wrong")

def check_valid(state: GeneratorState):
  if debug>2:
    times.possible -= time.process_time() 
    p_s_possible_real = possible_pixels(state.t_s_valid,state.t_s,brush)
    p_v_possible_real = possible_pixels(state.t_v_valid,state.t_v,brush)
    times.possible += time.process_time() 
    compare(state.p_s_possible,p_s_possible_real, state.p_v_possible,p_v_possible_real, "possible")

  if debug>2:
    p_s_possible_dilated_real = dilate(state.p_s_possible, state.brush, False)
    p_v_possible_dilated_real = dilate(state.p_v_possible, state.brush, False)
    compare(state.dilated_p_s_possible,p_s_possible_dilated_real, state.dilated_p_v_possible,p_v_possible_dilated_real, "dilated possible")

    t_s_free_real = free_touches(state.p_v_possible, state.t_s_valid, state.brush)
    t_v_free_real = free_touches(state.p_s_possible, state.t_v_valid, state.brush)
    compare(state.t_s_free,t_s_free_real, state.t_v_free,t_v_free_real, "free")

  if debug>1:
    t_s_valid_real = valid_touches(state.t_s_impossible, state.t_s)
    t_v_valid_real = valid_touches(state.t_v_impossible, state.t_v)
    compare(state.t_s_valid, t_s_valid_real, state.t_v_valid, t_v_valid_real, "valid")

  if state.t_s_free.any() or state.t_v_free.any():
    return

  if debug>1:
    p_s_required_real = required_pixels(state.p_s_existing, state.p_v_possible)
    p_v_required_real = required_pixels(state.p_v_existing, state.p_s_possible)
    compare(state.p_s_required,p_s_required_real, state.p_v_required,p_v_required_real, "required")
  
  if debug>1:
    p_s_required_dilated_real = dilate(state.p_s_required, state.brush)
    p_v_required_dilated_real = dilate(state.p_v_required, state.brush)
    compare(state.dilated_p_s_required, p_s_required_dilated_real, state.dilated_p_v_required, p_v_required_dilated_real, "dilated required")

    t_s_resolving_real = resolving_touches(state.p_s_required, state.t_s_valid, state.brush)
    t_v_resolving_real = resolving_touches(state.p_v_required, state.t_v_valid, state.brush)
    compare(state.t_s_resolving,t_s_resolving_real, state.t_v_resolving,t_v_resolving_real, "resolving")

source

generate

 generate (heatmap, brush, t_s:numpy.ndarray=None, t_v:numpy.ndarray=None)
Type Default Details
heatmap the latent space representation
brush the brush, determining the fabrication constraints
t_s ndarray None initial touches for the solid
t_v ndarray None initial touches for the void

source

force_update

 force_update (state)

source

update_resolving

 update_resolving (state)

source

track

 track (img, pos, brush, invert=False)

source

local_dilate

 local_dilate (img, pos, brush, res, l=None, grow=True, plot=False,
               plot_name='')

source

touch

 touch (flat_index:int, state:__main__.GeneratorState, solid:bool,
        track_possible:bool=True)

Perform a touch on the given index of the flattened map and track the consequences


source

select_single

 select_single (s_valid, v_valid, state)

source

generate_feasible_design

 generate_feasible_design (latent_t, brush, init_touches_solid=None,
                           init_touches_void=None, verbose=False)

source

generate_feasible_design_mask

 generate_feasible_design_mask (latent_t, brush, init_touches_solid=None,
                                init_touches_void=None, verbose=False)

source

generate_feasible_design_mask_jvp

 generate_feasible_design_mask_jvp (primals, tangents)
times = Times()
debug=0
generate(large_heatmap, brush)
<__main__.GeneratorState at 0x7fe7ca3efa60>

Times for 128 sidelength

No tracking: 6.94s

With partial tracking of existing: 6.09s

With full tracking of existing: 5.72s

With tracking of existing, impossible touches and possible pixels: 3.77s

times.__dict__
{'update': 1.4118364999999926,
 'dilate': 1.3667193000000122,
 'required': 0.004797800000002184,
 'resolving': 1.3748482999999938,
 'select': 2.172037500000017,
 'convolute': 1.0879858000000011,
 'existing': 0.2455430000000005,
 'impossible': 0,
 'possible': 0,
 'valid': 0.014091300000004914,
 'free': 0.08850650000001092,
 'local_required': 0.0068542000000002545,
 'local_resolving': 0.47692140000000016,
 'local_dilate': 0.611862099999982}
import pandas as pd
import numpy as np
from tqdm.notebook import tqdm

debug=0 df = [] for features in tqdm([2, 4, 8, 10, 12, 13, 14, 15, 16, 17, 18], desc=“features”, position=0):

for i in tqdm(range(50//features), desc=“iteration”, position=1, leave=False): heatmap = generate_heatmap(features, zoom=16) times = Times() generate(heatmap) dic = times.__dict__ dic[“features”] = features df.append(dic)

df = pd.DataFrame(df)

#df.to_pickle("timing.pkl")

Execution using papermill encountered an exception here and stopped:

df = pd.read_pickle("../resources/timing.pkl")
FileNotFoundError: [Errno 2] No such file or directory: '../resources/timing.pkl'
df["pixels"] = df["features"]**2*16**2
df["total"] = df["update"]+df["select"]
linear = ["local_dilate", "local_required", "local_resolving", "existing", "impossible", "possible", "valid", "free"]
df["linear_ops"] = df[linear].sum(axis="columns")
exclude = ["features", "required", "update+select", "features_sq", "update", "select"]
df = df.drop(columns=exclude)

t_mean = df.groupby("pixels").agg(np.mean)
t_std = df.groupby("pixels").agg(np.std)
def plot_timing(t_mean, t_std):
  for ((col_mean,val_mean), (col_std, val_std)) in zip(t_mean.items(), t_std.items()):
      plt.errorbar(val_mean.index, val_mean, val_std*2, label=col_mean)
      #t_mean.drop(columns=linear).plot(y="mean", yerr="std")
      plt.ylabel("Time [s]")
      plt.xlabel("Number Pixels")
plt.figure(constrained_layout=True)
plot_timing(t_mean.drop(columns=linear), t_std.drop(columns=linear))
plt.legend()
plt.savefig("total_time.png")
plt.figure(constrained_layout=True)
plot_timing(t_mean[linear], t_std[linear])
plt.legend()
plt.savefig("linear_time.png")