"""UNREGISTERED input consumer; no I/O, brain execution, or training. The frozen L/AK interface is discrete float32 Bernoulli (one request per PN and tick), not a continuous-time Poisson sampler. PN/rate mapping is supplied and externally authenticated by the caller; this module never selects odors, rebuilds mappings, rescales rates, clips probabilities, or resets brain RNGs. generate_cs_requests lazily uses the original CUDA Torch random operation. CPU mocks test its contract only, not CUDA sequence equivalence. A full (steps, PN-columns) matrix is drawn once, then the explicit half-open CS window masks requests. A 100 ms CS in a 120 ms epoch is NOT the full old L 120 ms input. Different draw shapes need not share identical random prefixes. The caller must register source/rate authority, device/environment, budgets, stream aliases, and a separate bounded CUDA equivalence test before real use. Returned request/source arrays are owned read-only copies, not immutable capabilities; the consumer must recheck their recorded hashes before use. """ from __future__ import annotations import hashlib import re import numpy as np N_NEURONS = 166700 L_SOURCE_SHA = 'e97d58d8c1f44920d0e953f8f5831ddc86fa0198784e86460e315de4116ac553' AK_ENCODER_SHA = '0061689abb1832e963718bd836bc8a50009ee38b258143a753b4d59e63414925' def require(value, message): if not value: raise ValueError(message) def array_identity(value): return dict(dtype=value.dtype.str, shape=list(value.shape), order='C-logical-bytes', sha256=hashlib.sha256(value.tobytes(order='C')).hexdigest()) def _vector(value, dtype, name): require(isinstance(value, np.ndarray) and value.dtype == np.dtype(dtype) and value.ndim == 1, name + ' must be an exact one-dimensional ' + np.dtype(dtype).name + ' array') def _targets(value): _vector(value, np.int32, 'targets') require(np.all((value >= 0) & (value < N_NEURONS)), 'Target outside canonical global neuron range') require(len(np.unique(value)) == len(value), 'Duplicate PN columns rejected; never merge rates or deduplicate RNG columns') def _clock(steps, start, stop): require(type(steps) is int and 0 < steps <= np.iinfo(np.int32).max, 'steps must be a positive int32-range integer') require(type(start) is type(stop) is int and 0 <= start <= stop <= steps, 'Invalid explicit half-open CS tick window') def probabilities_f32(targets, rates_hz, *, dt_ms=0.1): """Preserve the L expression's separate float32 multiply then divide.""" _targets(targets); _vector(rates_hz, np.float32, 'rates_hz') require(rates_hz.shape == targets.shape and np.all(np.isfinite(rates_hz)) and np.all(rates_hz >= 0), 'Invalid PN rate vector') require(type(dt_ms) in (int, float) and dt_ms == 0.1, 'Only the frozen dt_ms=0.1 contract is supported') probability = np.divide(np.multiply(rates_hz, np.float32(dt_ms), dtype=np.float32), np.float32(1000.0), dtype=np.float32) original = rates_hz * dt_ms / 1000.0 require(original.dtype == np.float32 and original.tobytes() == probability.tobytes(), 'Installed NumPy changed the frozen rate expression') require(np.all(np.isfinite(probability)) and np.all((probability >= 0) & (probability <= 1)), 'Probability outside [0,1]; no clipping or rate adjustment') return probability def forced_from_uniforms(uniforms, probability): """Pure arithmetic fixture helper, NOT a replacement CUDA generator.""" _vector(probability, np.float32, 'probability') require(isinstance(uniforms, np.ndarray) and uniforms.dtype == np.float32 and uniforms.ndim == 2 and uniforms.shape[1] == len(probability), 'Uniform fixture requires exact float32 [ticks,columns]') require(np.all(np.isfinite(uniforms)) and np.all((uniforms >= 0) & (uniforms < 1)) and np.all(np.isfinite(probability)) and np.all((probability >= 0) & (probability <= 1)), 'Invalid uniform/probability fixture') return uniforms < probability[None, :] def requests_from_forced(forced, targets, *, cs_start_tick, cs_stop_tick): """Sort resulting requests only; never reorder the pre-sampling columns.""" _targets(targets) require(isinstance(forced, np.ndarray) and forced.dtype == np.bool_ and forced.ndim == 2 and forced.shape[1] == len(targets), 'Forced samples must be Boolean [steps,original PN columns]') steps = forced.shape[0]; _clock(steps, cs_start_tick, cs_stop_tick) ticks, columns = np.nonzero(forced[cs_start_tick:cs_stop_tick]) ticks = ticks.astype(np.int32) + np.int32(cs_start_tick) neurons = targets[columns].copy() order = np.lexsort((neurons, ticks)) requests = {'tick': ticks[order], 'neuron_idx': neurons[order]} validate_requests(requests, steps) for value in requests.values(): value.setflags(write=False) generated = int(np.count_nonzero(forced)); retained = len(ticks) return dict(requests=requests, audit=dict(steps=steps, CS_window=[cs_start_tick, cs_stop_tick], full_matrix_boolean_hits=generated, retained_CS_requests=retained, outside_CS_boolean_hits_masked=generated - retained, original_PN_column_order_preserved=True, sorted_only_after_sampling=True, duplicate_requests_removed=0, probabilities_clipped=False, returned_requests_read_only=True, consumer_must_recheck_array_hashes=True, forced_identity=array_identity(forced), targets_identity=array_identity(targets), requests_identity={k: array_identity(v) for k, v in requests.items()})) def _load_torch(): import torch return torch def generate_cs_requests(targets, rates_hz, *, seed, steps, cs_start_tick, cs_stop_tick, device, max_uniform_draws, dt_ms=0.1): """Future explicit CUDA input generation; no implicit CPU fallback. ``seed`` is the caller's full semantic seed (Python int in [0,2**63)). The generator is private to this call. No offset/modulo/truncation or global/manual brain seed is used. Aliases must reuse the same complete sampling descriptor, including steps/column order/window, not seed alone. The caller owns authorization, resource admission, and external rate pins. ``device`` and ``max_uniform_draws`` must be explicitly supplied. The latter bounds steps * PN columns, not total process/GPU memory. Retaining initial_seed proves only integer delivery, not CUDA high-bit influence. """ require(type(seed) is int and 0 <= seed < 2**63, 'Exact full 63-bit Python integer seed required') _clock(steps, cs_start_tick, cs_stop_tick) probability = probabilities_f32(targets, rates_hz, dt_ms=dt_ms) require(type(device) is str and re.fullmatch('cuda:[0-9]+', device), 'Explicit indexed CUDA device required; no CPU fallback') require(type(max_uniform_draws) is int and max_uniform_draws > 0, 'Explicit positive integer uniform-draw budget required') uniform_draws = steps * len(targets) require(uniform_draws <= max_uniform_draws, 'Full matrix exceeds caller uniform-draw budget; no chunking or clipping') torch = _load_torch() require(torch.cuda.is_available() and torch.get_default_dtype() == torch.float32, 'Original CUDA/default-float32 sampler environment required') actual_device = torch.device(device) generator = torch.Generator(device=actual_device) generator.manual_seed(seed) require(generator.initial_seed() == seed, 'Generator did not retain the complete semantic seed') p_gpu = torch.as_tensor(probability, dtype=torch.float32, device=actual_device) # Exactly one complete draw. No chunking, per-PN loop, or tail-dependent redraw. uniforms = torch.rand((steps, len(targets)), generator=generator, device=actual_device) require(uniforms.dtype == torch.float32 and str(uniforms.device) == str(actual_device), 'Sampler dtype/device changed') forced = (uniforms < p_gpu[None, :]).cpu().numpy() result = requests_from_forced(forced, targets, cs_start_tick=cs_start_tick, cs_stop_tick=cs_stop_tick) # Optional identifiers are recorded only when the installed Torch exposes # them. An indexed device name is not presented as a UUID or external pin. identity = {'uuid': None, 'pci_bus_id': None, 'query_error': None} try: properties = torch.cuda.get_device_properties(actual_device) for key in ('uuid', 'pci_bus_id'): value = getattr(properties, key, None) if value is not None: identity[key] = str(value) except (AttributeError, RuntimeError) as exc: identity['query_error'] = type(exc).__name__ + ': ' + str(exc) result['audit'].update(schema='phase4an-conditioning-input-v1-unregistered', seed=seed, seed_bits=63, seed_hex=f'{seed:016x}', generator_initial_seed=generator.initial_seed(), seed_conversion='exact integer; no truncation/modulo/offset', private_generator=True, sampler='Torch CUDA rand default float32; strict U < float32 probability', generator_type=type(generator).__module__ + '.' + type(generator).__qualname__, numpy_version=str(np.__version__), torch_version=str(torch.__version__), torch_cuda_version=torch.version.cuda, execution_device=str(uniforms.device), device_name=str(torch.cuda.get_device_name(actual_device)), device_capability=list(torch.cuda.get_device_capability(actual_device)), device_identity=identity, device_identity_verified_against_external_pin=False, matrix_draw_calls=1, uniform_matrix_shape=[steps, len(targets)], uniform_scalar_draws=uniform_draws, max_uniform_draws=max_uniform_draws, uniform_matrix_bytes=uniform_draws * np.dtype(np.float32).itemsize, budget_scope='Uniform scalar count only, not total process or GPU memory', sampled_before_window_mask=True, dt_ms=dt_ms, dt_float32_identity=array_identity(np.asarray([dt_ms], np.float32)), rate_units='Hz', probability_identity=array_identity(probability), rates_identity=array_identity(rates_hz), input_mapping_authority_verified_by_this_module=False, reference_L_source_sha256=L_SOURCE_SHA, reference_AK_encoder_source_sha256=AK_ENCODER_SHA, full_old_L_input_identity_claimed=False, CUDA_sequence_equivalence_tested_here=False, CUDA_high_seed_bit_sequence_influence_tested_here=False, timing_scope='Full epoch matrix drawn once, then explicit CS window; old L supplied PN requests over its full duration', RNG_prefix_invariance_across_shapes_claimed=False, continuous_time_Poisson_claimed=False, brain_reset_called=False, brain_executed=False, training_executed=False, protocol_registered=False) return result def validate_requests(requests, steps): _clock(steps, 0, steps) require(type(requests) is dict and set(requests) == {'tick', 'neuron_idx'}, 'Exact sparse request schema required') for key, value in requests.items(): _vector(value, np.int32, key) tick, neuron = requests['tick'], requests['neuron_idx'] require(tick.shape == neuron.shape and np.all((tick >= 0) & (tick < steps)) and np.all((neuron >= 0) & (neuron < N_NEURONS)), 'Requests outside local clock/global neuron range') keys = tick.astype(np.int64) * N_NEURONS + neuron require(len(keys) < 2 or np.all(keys[1:] > keys[:-1]), 'Requests must already be sorted unique; no silent sorting/deduplication') return keys class RequestConflictError(ValueError): def __init__(self, audit): super().__init__('CS/US request overlap rejected; explicit boolean_union is required to combine it') self.audit = audit def merge_requests(cs, us, *, steps, conflict_policy='error'): """Compose two valid streams, recording every cross-source collision. boolean_union explicitly reproduces a single Boolean request per neuron / tick, not two deliveries. Separate source attempts are retained in audit and uint8 source_mask (CS=1, US=2, overlap=3). This is not refractory acceptance, actual spiking, an artificial D value, or an update rule. """ require(conflict_policy in ('error', 'boolean_union'), 'Unknown explicit conflict policy') cs_keys, us_keys = validate_requests(cs, steps), validate_requests(us, steps) overlap = np.intersect1d(cs_keys, us_keys, assume_unique=True) conflicts = [dict(tick=int(k // N_NEURONS), neuron_idx=int(k % N_NEURONS), CS_attempts=1, US_attempts=1, combined_boolean_requests=1 if conflict_policy == 'boolean_union' else None) for k in overlap] audit = dict(schema='phase4an-request-composition-v1-unregistered', policy=conflict_policy, CS_attempts=len(cs_keys), US_attempts=len(us_keys), cross_source_conflict_count=len(overlap), conflicts=conflicts, duplicate_within_source_allowed=False, refractory_acceptance_evaluated=False, actual_spikes_evaluated=False, input_identity={name: {k: array_identity(v) for k, v in values.items()} for name, values in (('CS', cs), ('US', us))}) if len(overlap) and conflict_policy == 'error': raise RequestConflictError(audit) keys = np.union1d(cs_keys, us_keys) sources = np.zeros(len(keys), np.uint8) sources[np.searchsorted(keys, cs_keys)] |= np.uint8(1) sources[np.searchsorted(keys, us_keys)] |= np.uint8(2) requests = {'tick': (keys // N_NEURONS).astype(np.int32), 'neuron_idx': (keys % N_NEURONS).astype(np.int32)} validate_requests(requests, steps) for value in (*requests.values(), sources): value.setflags(write=False) audit.update(output_requests=len(keys), explicit_boolean_overlap_collapses=len(overlap), requests_identity={k: array_identity(v) for k, v in requests.items()}, source_mask_identity=array_identity(sources), returned_arrays_read_only=True, consumer_must_recheck_array_hashes=True, learning_or_D_command_defined=False, brain_executed=False, protocol_registered=False) return dict(requests=requests, source_mask=sources, audit=audit)